diff --git a/packages/insomnia/src/entry.plugin-window-preload.ts b/packages/insomnia/src/entry.plugin-window-preload.ts index a84e54c1482..97384583b42 100644 --- a/packages/insomnia/src/entry.plugin-window-preload.ts +++ b/packages/insomnia/src/entry.plugin-window-preload.ts @@ -11,16 +11,16 @@ window.app = { // Bridge plugin UI calls to the main renderer window via IPC. // The plugin window has no visible DOM; these methods forward to the main renderer. window.showAlert = (options?: Record) => { - ipcRenderer.send('plugin-ui-alert', options ?? {}); + ipcRenderer.send('plugins.uiAlert', options ?? {}); }; window.showWrapper = (options?: Record) => { - ipcRenderer.send('plugin-ui-dialog', options ?? {}); + ipcRenderer.send('plugins.uiDialog', options ?? {}); }; window.showPrompt = (options?: Record) => { const { onComplete, onHide, ...serializableOptions } = options ?? {}; - ipcRenderer.invoke('plugin-ui-prompt', serializableOptions).then((value: string | null) => { + ipcRenderer.invoke('plugins.uiPrompt', serializableOptions).then((value: string | null) => { if (value !== null && value !== undefined) { onComplete?.(value); } diff --git a/packages/insomnia/src/entry.plugin-window.ts b/packages/insomnia/src/entry.plugin-window.ts index e576038d487..beff5f34653 100644 --- a/packages/insomnia/src/entry.plugin-window.ts +++ b/packages/insomnia/src/entry.plugin-window.ts @@ -1,10 +1,10 @@ import { ipcRenderer } from 'electron'; import { initDatabase, initServices } from '~/insomnia-data'; -import { servicesNodeImpl } from '~/insomnia-data/node'; import { pluginWindowDatabase } from './main/database.plugin-window'; import { invokePluginMethod } from './plugins/invoke-method'; +import { servicesProxy } from './ui/renderer-services-proxy'; interface PluginInvokeMessage { id: string; @@ -12,14 +12,14 @@ interface PluginInvokeMessage { args: unknown; } -ipcRenderer.on('plugin-invoke', async (_event, { id, method, args }: PluginInvokeMessage) => { +ipcRenderer.on('plugins.invoke', async (_event, { id, method, args }: PluginInvokeMessage) => { try { const result = await invokePluginMethod(method, args); - ipcRenderer.send('plugin-invoke-result', { id, result }); + ipcRenderer.send('plugins.invokeResult', { id, result }); } catch (error) { const errMsg = error instanceof Error ? error.message : String(error); console.error(`[plugin-window] Error in ${(error as any)?.method ?? method}: ${errMsg}`); - ipcRenderer.send('plugin-invoke-result', { id, error: errMsg }); + ipcRenderer.send('plugins.invokeResult', { id, error: errMsg }); } }); @@ -28,8 +28,8 @@ ipcRenderer.on('plugin-invoke', async (_event, { id, method, args }: PluginInvok (async () => { try { await initDatabase(pluginWindowDatabase); - initServices(servicesNodeImpl); - ipcRenderer.send('plugin-window-ready'); + initServices(servicesProxy); + ipcRenderer.send('plugins.windowReady'); } catch (err) { console.error('[plugin-window] Initialization failed:', err); } diff --git a/packages/insomnia/src/entry.preload.ts b/packages/insomnia/src/entry.preload.ts index ef8ba26dee2..ee6fe4e3732 100644 --- a/packages/insomnia/src/entry.preload.ts +++ b/packages/insomnia/src/entry.preload.ts @@ -34,7 +34,10 @@ type PluginMethodResult = T extends keyof PluginsB ? Awaited> : never; -const invokePluginBridgeMethod = (method: T, args?: unknown): Promise> => { +const invokePluginBridgeMethod = ( + method: T, + args?: unknown, +): Promise> => { return invokeWithNormalizedError(`plugins.${method}`, args) as Promise>; }; @@ -374,7 +377,8 @@ const main: Window['main'] = { getTemplateTags: () => invokePluginBridgeMethod('getTemplateTags'), runTemplateTagAction: (args: RunTemplateTagActionArgs) => invokePluginBridgeMethod('runTemplateTagAction', args), getBundlePlugins: () => invokePluginBridgeMethod('getBundlePlugins'), - executePluginMainAction: (args: ExecutePluginMainActionArgs) => invokePluginBridgeMethod('executePluginMainAction', args), + executePluginMainAction: (args: ExecutePluginMainActionArgs) => + invokePluginBridgeMethod('executePluginMainAction', args), hasRequestHooks: () => invokePluginBridgeMethod('hasRequestHooks'), hasResponseHooks: () => invokePluginBridgeMethod('hasResponseHooks'), applyRequestHooks: (args: ApplyRequestHooksArgs) => invokePluginBridgeMethod('applyRequestHooks', args), @@ -382,7 +386,7 @@ const main: Window['main'] = { getBridgeMetrics: () => invokeWithNormalizedError('plugins.getBridgeMetrics'), }, notifyPluginPromptResult: (id: string, value: string | null) => - ipcRenderer.send('plugin-ui-prompt-result', { id, value }), + ipcRenderer.send('plugins.uiPromptResult', { id, value }), }; ipcRenderer.on('hidden-browser-window-response-listener', event => { diff --git a/packages/insomnia/src/main/ipc/electron.ts b/packages/insomnia/src/main/ipc/electron.ts index 7e8d5840333..57b70ca6435 100644 --- a/packages/insomnia/src/main/ipc/electron.ts +++ b/packages/insomnia/src/main/ipc/electron.ts @@ -11,10 +11,7 @@ import { app, BrowserWindow, clipboard, dialog, ipcMain, Menu, shell } from 'ele import { localTemplateTags } from 'insomnia/src/templating/local-template-tags'; import { fnOrString } from '../../common/misc'; -import { - type NunjucksParsedTagArg, - type NunjucksTagContextMenuAction, -} from '../../templating/types'; +import { type NunjucksParsedTagArg, type NunjucksTagContextMenuAction } from '../../templating/types'; import type { extractNunjucksTagFromCoords } from '../../templating/utils'; import { invariant } from '../../utils/invariant'; @@ -119,20 +116,25 @@ export type HandleChannels = | 'multipartBufferToArray' | 'onDefaultBrowserOAuthRedirect' | 'open-channel-to-hidden-browser-window' + | 'plugins.applyRequestHooks' + | 'plugins.applyResponseHooks' | 'plugins.executeAction' - | 'plugins.getBundlePlugins' | 'plugins.executePluginMainAction' - | 'plugins.getTemplateTags' - | 'plugins.runTemplateTagAction' | 'plugins.getActivePlugins' + | 'plugins.getBridgeMetrics' + | 'plugins.getBundlePlugins' | 'plugins.getDocumentActions' | 'plugins.getPlugins' | 'plugins.getRequestActions' | 'plugins.getRequestGroupActions' + | 'plugins.getTemplateTags' | 'plugins.getThemes' | 'plugins.getWorkspaceActions' + | 'plugins.hasRequestHooks' + | 'plugins.hasResponseHooks' | 'plugins.reloadPlugins' - | 'plugin-ui-prompt' + | 'plugins.runTemplateTagAction' + | 'plugins.uiPrompt' | 'openPath' | 'parseImport' | 'readCurlResponse' @@ -192,8 +194,8 @@ export type MainOnChannels = | 'path.resolve' | 'readText' | 'restart' - | 'plugin-invoke-result' - | 'plugin-window-ready' + | 'plugins.invokeResult' + | 'plugins.windowReady' | 'set-hidden-window-busy-status' | 'setMenuBarVisibility' | 'show-nunjucks-context-menu' @@ -217,15 +219,15 @@ export type MainOnChannels = | 'sync.cancelConflict' | 'sync.resolveConflict' | 'mcp.sendMCPRequest' - | 'plugin-ui-prompt-result' + | 'plugins.uiPromptResult' | 'writeText'; export type RendererOnChannels = | 'contextMenuCommand' | 'db.changes' - | 'plugin-ui-alert' - | 'plugin-ui-dialog' - | 'plugin-ui-prompt' + | 'plugins.uiAlert' + | 'plugins.uiDialog' + | 'plugins.uiPrompt' | 'grpc.data' | 'grpc.end' | 'grpc.error' @@ -268,7 +270,6 @@ interface ContextMenuTag { }; } - const getTemplateValue = (arg: NunjucksParsedTagArg) => { if (arg.defaultValue === undefined) { return "''"; @@ -332,7 +333,9 @@ export function registerElectronHandlers() { }, { type: 'separator' }, ]; - const localTemplate: MenuItemConstructorOptions[] = ([...localTemplateTags, ...pluginTemplateTags] as ContextMenuTag[]) + const localTemplate: MenuItemConstructorOptions[] = ( + [...localTemplateTags, ...pluginTemplateTags] as ContextMenuTag[] + ) // sort alphabetically .sort((a, b) => fnOrString(a.templateTag.displayName).localeCompare(fnOrString(b.templateTag.displayName))) .map(l => { diff --git a/packages/insomnia/src/main/plugin-window.ts b/packages/insomnia/src/main/plugin-window.ts index 32893a80985..8d043da2ac7 100644 --- a/packages/insomnia/src/main/plugin-window.ts +++ b/packages/insomnia/src/main/plugin-window.ts @@ -5,7 +5,10 @@ import { app, BrowserWindow, ipcMain } from 'electron'; let pluginWindow: BrowserWindow | null = null; let windowReady = false; -const pendingRequests = new Map void; reject: (e: Error) => void; method: string; startedAt: number }>(); +const pendingRequests = new Map< + string, + { resolve: (v: unknown) => void; reject: (e: Error) => void; method: string; startedAt: number } +>(); let cachedHasRequestHooks: boolean | null = null; let cachedHasResponseHooks: boolean | null = null; @@ -77,7 +80,7 @@ function ensureIpcListeners() { } ipcListenersRegistered = true; - ipcMain.on('plugin-window-ready', event => { + ipcMain.on('plugins.windowReady', event => { if (event.sender !== pluginWindow?.webContents) { return; } @@ -88,21 +91,21 @@ function ensureIpcListeners() { console.log(`[plugin-bridge] window_ready startup_ms=${startupMs}`); }); - ipcMain.on('plugin-ui-alert', (event, options: Record) => { + ipcMain.on('plugins.uiAlert', (event, options: Record) => { if (event.sender !== pluginWindow?.webContents) { return; } - getMainWindow()?.webContents.send('plugin-ui-alert', options); + getMainWindow()?.webContents.send('plugins.uiAlert', options); }); - ipcMain.on('plugin-ui-dialog', (event, options: Record) => { + ipcMain.on('plugins.uiDialog', (event, options: Record) => { if (event.sender !== pluginWindow?.webContents) { return; } - getMainWindow()?.webContents.send('plugin-ui-dialog', options); + getMainWindow()?.webContents.send('plugins.uiDialog', options); }); - ipcMain.handle('plugin-ui-prompt', async (event, options: Record) => { + ipcMain.handle('plugins.uiPrompt', async (event, options: Record) => { if (event.sender !== pluginWindow?.webContents) { return null; } @@ -120,11 +123,15 @@ function ensureIpcListeners() { clearTimeout(timeout); resolve(value); }); - mainWindow.webContents.send('plugin-ui-prompt', id, options); + mainWindow.webContents.send('plugins.uiPrompt', id, options); }); }); - ipcMain.on('plugin-ui-prompt-result', (_event, { id, value }: { id: string; value: string | null }) => { + ipcMain.on('plugins.uiPromptResult', (event, { id, value }: { id: string; value: string | null }) => { + const mainWindow = getMainWindow(); + if (!mainWindow || event.sender !== mainWindow.webContents) { + return; + } const resolve = promptPendingRequests.get(id); if (!resolve) { return; @@ -133,24 +140,27 @@ function ensureIpcListeners() { resolve(value); }); - ipcMain.on('plugin-invoke-result', (event, { id, result, error }: { id: string; result?: unknown; error?: string }) => { - if (event.sender !== pluginWindow?.webContents) { - return; - } - const pending = pendingRequests.get(id); - if (!pending) { - return; - } - pendingRequests.delete(id); - const duration = Date.now() - pending.startedAt; - if (error) { - recordInvocation(pending.method, 'error', duration); - pending.reject(new Error(error)); - } else { - recordInvocation(pending.method, 'ok', duration); - pending.resolve(result); - } - }); + ipcMain.on( + 'plugins.invokeResult', + (event, { id, result, error }: { id: string; result?: unknown; error?: string }) => { + if (event.sender !== pluginWindow?.webContents) { + return; + } + const pending = pendingRequests.get(id); + if (!pending) { + return; + } + pendingRequests.delete(id); + const duration = Date.now() - pending.startedAt; + if (error) { + recordInvocation(pending.method, 'error', duration); + pending.reject(new Error(error)); + } else { + recordInvocation(pending.method, 'ok', duration); + pending.resolve(result); + } + }, + ); } export function getPluginWindow() { @@ -259,7 +269,7 @@ export async function invokeInPluginWindow(method: string, args?: unknown): Prom }, }); - pluginWindow!.webContents.send('plugin-invoke', { id, method, args }); + pluginWindow!.webContents.send('plugins.invoke', { id, method, args }); }); } @@ -293,16 +303,18 @@ export function registerPluginIpcHandlers() { ipcMain.handle('plugins.getTemplateTags', () => invokeInPluginWindow('getTemplateTags')); ipcMain.handle('plugins.runTemplateTagAction', (_event, args) => invokeInPluginWindow('runTemplateTagAction', args)); ipcMain.handle('plugins.getBundlePlugins', () => invokeInPluginWindow('getBundlePlugins')); - ipcMain.handle('plugins.executePluginMainAction', (_event, args) => invokeInPluginWindow('executePluginMainAction', args)); + ipcMain.handle('plugins.executePluginMainAction', (_event, args) => + invokeInPluginWindow('executePluginMainAction', args), + ); ipcMain.handle('plugins.hasRequestHooks', async () => { if (cachedHasRequestHooks === null) { - cachedHasRequestHooks = await invokeInPluginWindow('hasRequestHooks') as boolean; + cachedHasRequestHooks = (await invokeInPluginWindow('hasRequestHooks')) as boolean; } return cachedHasRequestHooks; }); ipcMain.handle('plugins.hasResponseHooks', async () => { if (cachedHasResponseHooks === null) { - cachedHasResponseHooks = await invokeInPluginWindow('hasResponseHooks') as boolean; + cachedHasResponseHooks = (await invokeInPluginWindow('hasResponseHooks')) as boolean; } return cachedHasResponseHooks; }); diff --git a/packages/insomnia/src/root.tsx b/packages/insomnia/src/root.tsx index 922fee22fbe..3365aad01d8 100644 --- a/packages/insomnia/src/root.tsx +++ b/packages/insomnia/src/root.tsx @@ -24,7 +24,6 @@ import { useLatest } from 'react-use'; import { EXTERNAL_VAULT_PLUGIN_NAME, isDevelopment } from '~/common/constants'; import type { Settings, UserSession } from '~/insomnia-data'; import { models, services } from '~/insomnia-data'; -import { executePluginMainAction } from '~/plugins'; import { createPlugin } from '~/plugins/create'; import { setTheme } from '~/plugins/misc'; import { plugins } from '~/plugins/renderer-bridge'; @@ -544,11 +543,11 @@ const Root = () => { if (urlWithoutParams === 'insomnia://oauth/azure/authenticate') { const { code, ...restParams } = params; if (code && typeof code === 'string') { - const authResult = await executePluginMainAction({ + const authResult = await plugins.executePluginMainAction({ pluginName: EXTERNAL_VAULT_PLUGIN_NAME, actionName: 'exchangeCode', params: { provider: 'azure', code }, - }); + }) as any; const { success, result, error } = authResult; if (success) { const { account, uniqueId } = result!; diff --git a/packages/insomnia/src/ui/renderer-listeners.ts b/packages/insomnia/src/ui/renderer-listeners.ts index 730ec8518cf..a176be071ae 100644 --- a/packages/insomnia/src/ui/renderer-listeners.ts +++ b/packages/insomnia/src/ui/renderer-listeners.ts @@ -27,15 +27,15 @@ window.main.on('show-toast', (_, options: { content: RAToastContent; options?: { showToast(options.content, options.options); }); -window.main.on('plugin-ui-alert', (_, options: Record) => { +window.main.on('plugins.uiAlert', (_, options: Record) => { window.showAlert?.(options); }); -window.main.on('plugin-ui-dialog', (_, options: Record) => { +window.main.on('plugins.uiDialog', (_, options: Record) => { window.showWrapper?.(options); }); -window.main.on('plugin-ui-prompt', (_, id: string, options: Record) => { +window.main.on('plugins.uiPrompt', (_, id: string, options: Record) => { window.showPrompt?.({ ...options, onComplete: (value: string) => {