diff --git a/sidecar/src/api/browserExtension.ts b/sidecar/src/api/browserExtension.ts index 0d19212..8eea466 100644 --- a/sidecar/src/api/browserExtension.ts +++ b/sidecar/src/api/browserExtension.ts @@ -1,39 +1,4 @@ -import { writeOutput } from '../io'; -import * as crypto from 'crypto'; - -const pendingRequests = new Map< - string, - { resolve: (value: unknown) => void; reject: (reason?: unknown) => void } ->(); - -export function handleBrowserExtensionResponse(requestId: string, result: unknown, error?: string) { - const promise = pendingRequests.get(requestId); - if (promise) { - if (error) { - promise.reject(new Error(error)); - } else { - promise.resolve(result); - } - pendingRequests.delete(requestId); - } -} - -function sendRequest(method: string, params: unknown): Promise { - return new Promise((resolve, reject) => { - const requestId = crypto.randomUUID(); - pendingRequests.set(requestId, { resolve: resolve as (value: unknown) => void, reject }); - writeOutput({ - type: 'browser-extension-request', - payload: { requestId, method, params } - }); - setTimeout(() => { - if (pendingRequests.has(requestId)) { - pendingRequests.delete(requestId); - reject(new Error(`Request for ${method} timed out`)); - } - }, 5000); - }); -} +import { sendRequest } from './rpc'; type Tab = { active: boolean; @@ -51,9 +16,13 @@ type RawTab = { active: boolean; }; +const sendBrowserRequest = (method: string, params: unknown) => { + return sendRequest('browser-extension-request', { method, params }); +}; + export const BrowserExtensionAPI = { async getTabs(): Promise { - const result = await sendRequest<{ value: RawTab[] }>('getTabs', {}); + const result = await sendBrowserRequest<{ value: RawTab[] }>('getTabs', {}); return result.value.map((tab) => ({ id: tab.tabId, url: tab.url, @@ -84,7 +53,7 @@ export const BrowserExtensionAPI = { params.tabId = options.tabId; } - const result = await sendRequest<{ value: string }>('getTab', params); + const result = await sendBrowserRequest<{ value: string }>('getTab', params); return result.value; } }; diff --git a/sidecar/src/api/clipboard.ts b/sidecar/src/api/clipboard.ts index 6290b05..beca833 100644 --- a/sidecar/src/api/clipboard.ts +++ b/sidecar/src/api/clipboard.ts @@ -1,5 +1,4 @@ -import { writeOutput } from '../io'; -import * as crypto from 'crypto'; +import { sendRequest } from './rpc'; import type * as api from '@raycast/api'; type ClipboardContent = { @@ -14,40 +13,6 @@ type ReadResult = { file?: string; }; -const pendingRequests = new Map< - string, - { resolve: (value: unknown) => void; reject: (reason?: unknown) => void } ->(); - -function sendRequest(type: string, payload: object): Promise { - return new Promise((resolve, reject) => { - const requestId = crypto.randomUUID(); - pendingRequests.set(requestId, { resolve: resolve as (value: unknown) => void, reject }); - writeOutput({ - type, - payload: { requestId, ...payload } - }); - setTimeout(() => { - if (pendingRequests.has(requestId)) { - pendingRequests.delete(requestId); - reject(new Error(`Request for ${type} timed out`)); - } - }, 5000); - }); -} - -export function handleClipboardResponse(requestId: string, result: unknown, error?: string) { - const promise = pendingRequests.get(requestId); - if (promise) { - if (error) { - promise.reject(new Error(error)); - } else { - promise.resolve(result); - } - pendingRequests.delete(requestId); - } -} - function normalizeContent(content: string | number | api.Clipboard.Content): ClipboardContent { if (typeof content === 'string' || typeof content === 'number') { return { text: String(content) }; diff --git a/sidecar/src/api/environment.ts b/sidecar/src/api/environment.ts index 77a015a..a408f2f 100644 --- a/sidecar/src/api/environment.ts +++ b/sidecar/src/api/environment.ts @@ -4,7 +4,7 @@ import { writeOutput } from '../io'; import type { Application } from './types'; import { config } from '../config'; import { browserExtensionState } from '../state'; -import * as crypto from 'crypto'; +import { sendRequest } from './rpc'; const supportPath = config.supportDir; try { @@ -21,40 +21,6 @@ export interface FileSystemItem { export const BrowserExtension = { name: 'BrowserExtension' }; -const pendingSystemRequests = new Map< - string, - { resolve: (value: unknown) => void; reject: (reason?: unknown) => void } ->(); - -function sendSystemRequest(type: string, payload: object = {}): Promise { - return new Promise((resolve, reject) => { - const requestId = crypto.randomUUID(); - pendingSystemRequests.set(requestId, { resolve, reject }); - writeOutput({ - type: `system-${type}`, - payload: { requestId, ...payload } - }); - setTimeout(() => { - if (pendingSystemRequests.has(requestId)) { - pendingSystemRequests.delete(requestId); - reject(new Error(`Request for ${type} timed out`)); - } - }, 5000); // 5-second timeout - }); -} - -export function handleSystemResponse(requestId: string, result: unknown, error?: string) { - const promise = pendingSystemRequests.get(requestId); - if (promise) { - if (error) { - promise.reject(new Error(error)); - } else { - promise.resolve(result); - } - pendingSystemRequests.delete(requestId); - } -} - export const environment = { appearance: 'dark' as const, assetsPath: config.assetsDir, @@ -76,11 +42,11 @@ export const environment = { }; export async function getSelectedFinderItems(): Promise { - return sendSystemRequest('get-selected-finder-items'); + return sendRequest('system-get-selected-finder-items'); } export async function getSelectedText(): Promise { - return sendSystemRequest('get-selected-text'); + return sendRequest('system-get-selected-text'); } export async function open(target: string, application?: Application | string): Promise { @@ -103,22 +69,22 @@ export async function open(target: string, application?: Application | string): export async function getApplications(path?: fs.PathLike): Promise { const pathString = path ? path.toString() : undefined; - return sendSystemRequest('get-applications', { path: pathString }); + return sendRequest('system-get-applications', { path: pathString }); } export async function getDefaultApplication(path: fs.PathLike): Promise { - return sendSystemRequest('get-default-application', { path: path.toString() }); + return sendRequest('system-get-default-application', { path: path.toString() }); } export async function getFrontmostApplication(): Promise { - return sendSystemRequest('get-frontmost-application'); + return sendRequest('system-get-frontmost-application'); } export async function showInFinder(path: fs.PathLike): Promise { - return sendSystemRequest('show-in-finder', { path: path.toString() }); + return sendRequest('system-show-in-finder', { path: path.toString() }); } export async function trash(path: fs.PathLike | fs.PathLike[]): Promise { const paths = (Array.isArray(path) ? path : [path]).map((p) => p.toString()); - return sendSystemRequest('trash', { paths }); + return sendRequest('system-trash', { paths }); } diff --git a/sidecar/src/api/rpc.ts b/sidecar/src/api/rpc.ts new file mode 100644 index 0000000..b72a910 --- /dev/null +++ b/sidecar/src/api/rpc.ts @@ -0,0 +1,38 @@ +import { writeOutput } from '../io'; +import * as crypto from 'crypto'; + +const pendingRequests = new Map< + string, + { resolve: (value: unknown) => void; reject: (reason?: unknown) => void } +>(); + +export function sendRequest(type: string, payload: object = {}): Promise { + return new Promise((resolve, reject) => { + const requestId = crypto.randomUUID(); + pendingRequests.set(requestId, { resolve: resolve as (value: unknown) => void, reject }); + + writeOutput({ + type, + payload: { requestId, ...payload } + }); + + setTimeout(() => { + if (pendingRequests.has(requestId)) { + pendingRequests.delete(requestId); + reject(new Error(`Request for ${type} timed out`)); + } + }, 5000); + }); +} + +export function handleResponse(requestId: string, result: unknown, error?: string) { + const promise = pendingRequests.get(requestId); + if (promise) { + if (error) { + promise.reject(new Error(error)); + } else { + promise.resolve(result); + } + pendingRequests.delete(requestId); + } +} diff --git a/sidecar/src/index.ts b/sidecar/src/index.ts index 9d62b07..ca86c2e 100644 --- a/sidecar/src/index.ts +++ b/sidecar/src/index.ts @@ -5,9 +5,7 @@ import { instances, navigationStack, toasts, browserExtensionState } from './sta import { batchedUpdates, updateContainer } from './reconciler'; import { preferencesStore } from './preferences'; import type { RaycastInstance } from './types'; -import { handleSystemResponse } from './api/environment'; -import { handleBrowserExtensionResponse } from './api/browserExtension'; -import { handleClipboardResponse } from './api/clipboard'; +import { handleResponse } from './api/rpc'; import { handleOAuthResponse, handleTokenResponse } from './api/oauth'; process.on('unhandledRejection', (reason: unknown) => { @@ -23,13 +21,22 @@ rl.on('line', (line) => { try { const command: { action: string; payload: unknown } = JSON.parse(line); - if (command.action.startsWith('system-') && command.action.endsWith('-response')) { - const { requestId, result, error } = command.payload as { + if (command.action.endsWith('-response')) { + const { requestId, result, error, state, code } = command.payload as { requestId: string; result?: unknown; error?: string; + state?: string; + code?: string; }; - handleSystemResponse(requestId, result, error); + + if (command.action === 'oauth-authorize-response') { + handleOAuthResponse(state!, code!, state, error); + } else if (command.action.startsWith('oauth-')) { + handleTokenResponse(requestId, result, error); + } else { + handleResponse(requestId, result, error); + } return; } @@ -126,53 +133,11 @@ rl.on('line', (line) => { toast?.hide(); break; } - case 'browser-extension-response': { - const { requestId, result, error } = command.payload as { - requestId: string; - result?: unknown; - error?: string; - }; - handleBrowserExtensionResponse(requestId, result, error); - break; - } case 'browser-extension-connection-status': { const { isConnected } = command.payload as { isConnected: boolean }; browserExtensionState.isConnected = isConnected; break; } - case 'oauth-authorize-response': { - const { code, state, error } = command.payload as { - code: string; - state: string; - error?: string; - }; - handleOAuthResponse(state, code, state, error); - break; - } - case 'oauth-get-tokens-response': - case 'oauth-set-tokens-response': - case 'oauth-remove-tokens-response': { - const { requestId, result, error } = command.payload as { - requestId: string; - result?: unknown; - error?: string; - }; - handleTokenResponse(requestId, result, error); - break; - } - case 'clipboard-read-text-response': - case 'clipboard-read-response': - case 'clipboard-copy-response': - case 'clipboard-paste-response': - case 'clipboard-clear-response': { - const { requestId, result, error } = command.payload as { - requestId: string; - result?: unknown; - error?: string; - }; - handleClipboardResponse(requestId, result, error); - break; - } default: writeLog(`Unknown command action: ${command.action}`); }