diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ff5a3d809..8577c7e1b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,6 +47,9 @@ jobs: - name: Lint run: npm run lint + - name: Check renderer Node import baseline + run: npm run check:renderer-node-imports + - name: Type checks run: npm run type-check diff --git a/package.json b/package.json index 4a6c8a5377..24861f27ee 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "scripts": { "dev": "npm start -w insomnia", "dev:autoRestart": "npm run start:autoRestart -w insomnia", + "check:renderer-node-imports": "npm run check:renderer-node-imports -w insomnia", "lint": "npm run lint --workspaces --if-present", "type-check": "npm run type-check --workspaces --if-present", "test": "npm run test --workspaces --if-present", diff --git a/packages/insomnia/package.json b/packages/insomnia/package.json index 95ac82ad07..88fe463362 100644 --- a/packages/insomnia/package.json +++ b/packages/insomnia/package.json @@ -20,6 +20,9 @@ "verify-bundle-plugins": "esr --cache ./scripts/verify-bundle-plugins.ts", "install-x64-native-dependencies": "esr --cache ./scripts/install-x64-native-dependencies.ts", "build": "react-router build && esr --cache ./scripts/build.ts --noErrorTruncation", + "analyze:renderer-node-imports": "cross-env NODE_OPTIONS=--max-old-space-size=8192 INSOMNIA_NODE_IMPORT_REPORT=1 react-router build", + "check:renderer-node-imports": "npm run analyze:renderer-node-imports && esr --cache ./scripts/check-renderer-node-imports.ts", + "update:renderer-node-import-baseline": "npm run analyze:renderer-node-imports && esr --cache ./scripts/check-renderer-node-imports.ts --write-baseline", "build:react-router": "react-router build", "generate:schema": "esr ./src/schema.ts", "build:electron-entrypoints": "cross-env NODE_ENV=development esr esbuild.entrypoints.ts", diff --git a/packages/insomnia/src/entry.main.ts b/packages/insomnia/src/entry.main.ts index 2099223068..c1c5e58d57 100644 --- a/packages/insomnia/src/entry.main.ts +++ b/packages/insomnia/src/entry.main.ts @@ -26,7 +26,6 @@ import { registerInsomniaProtocols } from './main/api.protocol'; import { backupIfNewerVersionAvailable } from './main/backup'; import { registerSyncHandlers } from './main/cloud-sync/ipc'; import { registerGitServiceAPI } from './main/git-service'; -import { registerCookieHandlers } from './main/ipc/cookies'; import { ipcMainOn, ipcMainOnce, registerElectronHandlers } from './main/ipc/electron'; import { registerElectronStorageHandlers } from './main/ipc/electron-storage'; import { registergRPCHandlers } from './main/ipc/grpc'; @@ -89,7 +88,6 @@ app.on('ready', async () => { // @TODO - Maybe move the register stuff in the registerMainHandlers function registerMainHandlers(); registerPathHandlers(); - registerCookieHandlers(); registergRPCHandlers(); registerGitServiceAPI(); registerLLMConfigServiceAPI(); diff --git a/packages/insomnia/src/entry.plugin-window-preload.ts b/packages/insomnia/src/entry.plugin-window-preload.ts index b8fffbc1fe..97384583b4 100644 --- a/packages/insomnia/src/entry.plugin-window-preload.ts +++ b/packages/insomnia/src/entry.plugin-window-preload.ts @@ -3,9 +3,6 @@ import { ipcRenderer } from 'electron'; // Provide window.app so plugin-loading code (which checks process.type === 'renderer') // can resolve the userData path without needing the main renderer's full preload. window.app = { - env: Object.fromEntries( - Object.entries(process.env).filter(([key, value]) => value !== undefined && key.startsWith('INSOMNIA_')), - ), getPath: (name: string) => ipcRenderer.sendSync('getPath', name) as string, getAppPath: () => ipcRenderer.sendSync('getAppPath') as string, process: { platform: process.platform as NodeJS.Platform }, diff --git a/packages/insomnia/src/entry.preload.ts b/packages/insomnia/src/entry.preload.ts index 6f03ccefae..2a782fd483 100644 --- a/packages/insomnia/src/entry.preload.ts +++ b/packages/insomnia/src/entry.preload.ts @@ -9,7 +9,6 @@ import { servicesProxy } from '~/ui/renderer-services-proxy'; import type { SyncBridgeAPI } from './main/cloud-sync/ipc'; import type { GitServiceAPI } from './main/git-service'; -import type { CookiesBridgeAPI } from './main/ipc/cookies'; import type { electronStorageBridgeAPI } from './main/ipc/electron-storage'; import type { gRPCBridgeAPI } from './main/ipc/grpc'; import type { secretStorageBridgeAPI } from './main/ipc/secret-storage'; @@ -124,17 +123,9 @@ const grpc: gRPCBridgeAPI = { closeAll: () => ipcRenderer.send('grpc.closeAll'), loadMethods: options => invokeWithNormalizedError('grpc.loadMethods', options), loadMethodsFromReflection: options => invokeWithNormalizedError('grpc.loadMethodsFromReflection', options), - validateProtoFile: filePath => invokeWithNormalizedError('grpc.validateProtoFile', filePath), writeProtoFile: protoFileId => invokeWithNormalizedError('grpc.writeProtoFile', protoFileId), }; -const cookies: CookiesBridgeAPI = { - fromJSON: cookie => invokeWithNormalizedError('cookies.fromJSON', cookie), - parse: cookie => invokeWithNormalizedError('cookies.parse', cookie), - toString: cookie => invokeWithNormalizedError('cookies.toString', cookie), - getCookiesForUrl: args => invokeWithNormalizedError('cookies.getCookiesForUrl', args), -}; - const secretStorage: secretStorageBridgeAPI = { setSecret: (key, secret) => invokeWithNormalizedError('secretStorage.setSecret', key, secret), getSecret: key => invokeWithNormalizedError('secretStorage.getSecret', key), @@ -261,30 +252,6 @@ const llm: LLMConfigServiceAPI = { invokeWithNormalizedError('llm.setAIFeatureEnabled', feature, enabled), }; -const rendererProcessEnv = Object.fromEntries( - Object.entries(process.env).filter( - ([key, value]) => - value !== undefined && - (key.startsWith('INSOMNIA_') || - key === 'BUILD_DATE' || - key === 'NODE_ENV' || - key === 'PLAYWRIGHT_TEST' || - key === 'PORTABLE_EXECUTABLE_DIR'), - ), -); - -const rendererProcess = { - env: rendererProcessEnv, - platform: process.platform as NodeJS.Platform, - type: 'renderer' as const, - versions: { - chrome: process.versions.chrome, - electron: process.versions.electron, - node: process.versions.node, - v8: process.versions.v8, - }, -}; - const main: Window['main'] = { startExecution: options => ipcRenderer.send('startExecution', options), addExecutionStep: options => ipcRenderer.send('addExecutionStep', options), @@ -338,7 +305,6 @@ const main: Window['main'] = { webSocket, socketIO, mcp, - cookies, git, llm, grpc, @@ -447,7 +413,6 @@ const dialog: Window['dialog'] = { showSaveDialog: options => invokeWithNormalizedError('showSaveDialog', options), }; const app: Window['app'] = { - env: rendererProcessEnv, getPath: options => ipcRenderer.sendSync('getPath', options), getAppPath: () => ipcRenderer.sendSync('getAppPath'), process: { @@ -474,8 +439,6 @@ const database: Window['database'] = { if (process.contextIsolated) { contextBridge.exposeInMainWorld('main', main); - contextBridge.exposeInMainWorld('process', rendererProcess); - contextBridge.exposeInMainWorld('global', globalThis); contextBridge.exposeInMainWorld('dialog', dialog); contextBridge.exposeInMainWorld('app', app); contextBridge.exposeInMainWorld('shell', shell); @@ -486,14 +449,6 @@ if (process.contextIsolated) { contextBridge.exposeInMainWorld('_dataServices', servicesProxy); } else { window.main = main; - Object.defineProperty(window, 'process', { - configurable: true, - value: rendererProcess, - }); - Object.defineProperty(window, 'global', { - configurable: true, - value: window, - }); window.dialog = dialog; window.app = app; window.shell = shell; diff --git a/packages/insomnia/src/main/ipc/cookies.ts b/packages/insomnia/src/main/ipc/cookies.ts deleted file mode 100644 index 11eb4f7ca9..0000000000 --- a/packages/insomnia/src/main/ipc/cookies.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Cookie as ToughCookie, CookieJar } from 'tough-cookie'; - -import type { Cookie } from '~/insomnia-data'; - -import { ipcMainHandle } from './electron'; - -type CookieInput = Cookie | string; - -const parseCookieFromJSON = (cookie: CookieInput) => { - return typeof cookie === 'string' ? ToughCookie.fromJSON(cookie) : ToughCookie.fromJSON(cookie); -}; - -const cookieToString = (cookie: CookieInput) => { - const parsedCookie = parseCookieFromJSON(cookie); - - if (parsedCookie === null) { - throw new Error(`Unable to read cookie: ${cookie}`); - } - - let value = parsedCookie.toString(); - - if (parsedCookie.domain && parsedCookie.hostOnly) { - value += `; Domain=${parsedCookie.domain}`; - } - - return value; -}; - -const getCookiesForUrl = (cookies: Cookie[], url: string): Cookie[] => { - try { - const sanitized = cookies.map(c => ({ - ...c, - expires: c.expires === null || c.expires === undefined ? 'Infinity' : c.expires, - })); - const jar = CookieJar.fromJSON(JSON.stringify({ cookies: sanitized })); - jar.rejectPublicSuffixes = false; - jar.looseMode = true; - return jar.getCookiesSync(url).map(c => c.toJSON() as Cookie); - } catch { - return []; - } -}; - -export interface CookiesBridgeAPI { - fromJSON: (cookie: CookieInput) => Promise; - parse: (cookie: string) => Promise; - toString: (cookie: CookieInput) => Promise; - getCookiesForUrl: (args: { cookies: Cookie[]; url: string }) => Promise; -} - -export function registerCookieHandlers() { - ipcMainHandle('cookies.fromJSON', (_, cookie: CookieInput) => { - return parseCookieFromJSON(cookie)?.toJSON() as Cookie | null; - }); - ipcMainHandle('cookies.parse', (_, cookie: string) => { - return ToughCookie.parse(cookie, { loose: true })?.toJSON() as Cookie | null; - }); - ipcMainHandle('cookies.toString', (_, cookie: CookieInput) => { - return cookieToString(cookie); - }); - ipcMainHandle('cookies.getCookiesForUrl', (_, { cookies, url }: { cookies: Cookie[]; url: string }) => { - return getCookiesForUrl(cookies, url); - }); -} diff --git a/packages/insomnia/src/main/ipc/electron.ts b/packages/insomnia/src/main/ipc/electron.ts index 70a3f709b3..88bd9cd8c2 100644 --- a/packages/insomnia/src/main/ipc/electron.ts +++ b/packages/insomnia/src/main/ipc/electron.ts @@ -26,10 +26,6 @@ export type HandleChannels = | 'curl.event.findMany' | 'curl.open' | 'curl.readyState' - | 'cookies.fromJSON' - | 'cookies.getCookiesForUrl' - | 'cookies.parse' - | 'cookies.toString' | 'createPlugin' | 'curlRequest' | 'database.caCertificate.create' @@ -84,7 +80,6 @@ export type HandleChannels = | 'git.getGitProviderEmails' | 'grpc.loadMethods' | 'grpc.loadMethodsFromReflection' - | 'grpc.validateProtoFile' | 'grpc.writeProtoFile' | 'initializeWorkspaceBackendProject' | 'insecureReadFile' diff --git a/packages/insomnia/src/main/ipc/grpc.ts b/packages/insomnia/src/main/ipc/grpc.ts index 15796b1961..c554a481f0 100644 --- a/packages/insomnia/src/main/ipc/grpc.ts +++ b/packages/insomnia/src/main/ipc/grpc.ts @@ -1,5 +1,3 @@ -import path from 'node:path'; - import { FileDescriptorSet as ProtobufEsFileDescriptorSet, MethodIdempotency, @@ -62,7 +60,6 @@ export interface gRPCBridgeAPI { cancel: typeof cancel; loadMethods: typeof loadMethods; loadMethodsFromReflection: typeof loadMethodsFromReflection; - validateProtoFile: (filePath: string) => Promise; closeAll: typeof closeAll; writeProtoFile: (protoFileId: string) => Promise<{ filePath: string; dirs: string[] }>; } @@ -94,17 +91,9 @@ export function registergRPCHandlers() { ipcMainOn('grpc.closeAll', closeAll); ipcMainHandle('grpc.loadMethods', (_, requestId) => loadMethods(requestId)); ipcMainHandle('grpc.loadMethodsFromReflection', (_, requestId) => loadMethodsFromReflection(requestId)); - ipcMainHandle('grpc.validateProtoFile', (_, filePath: string) => validateProtoFile(filePath)); ipcMainHandle('grpc.writeProtoFile', (_, protoFileId: string) => writeProtoFileById(protoFileId)); } -const validateProtoFile = async (filePath: string): Promise => { - await protoLoader.load(filePath, { - ...grpcOptions, - includeDirs: [path.dirname(filePath)], - }); -}; - const loadMethodsFromFilePath = async (filePath: string, includeDirs: string[]): Promise => { const definition = await protoLoader.load(filePath, { ...grpcOptions, diff --git a/packages/insomnia/src/main/ipc/main.ts b/packages/insomnia/src/main/ipc/main.ts index 2db5577114..5fd282317f 100644 --- a/packages/insomnia/src/main/ipc/main.ts +++ b/packages/insomnia/src/main/ipc/main.ts @@ -67,7 +67,6 @@ import { import type { SocketIOBridgeAPI } from '../network/socket-io'; import type { WebSocketBridgeAPI } from '../network/websocket'; import { registerPluginIpcHandlers } from '../plugin-window'; -import type { CookiesBridgeAPI } from './cookies'; import { ipcMainHandle, ipcMainOn, type RendererOnChannels } from './electron'; import type { electronStorageBridgeAPI } from './electron-storage'; import extractPostmanDataDumpHandler from './extract-postman-data-dump'; @@ -227,7 +226,6 @@ export interface RendererToMainBridgeAPI { webSocket: WebSocketBridgeAPI; socketIO: SocketIOBridgeAPI; mcp: McpBridgeAPI; - cookies: CookiesBridgeAPI; grpc: gRPCBridgeAPI; curl: CurlBridgeAPI; git: GitServiceAPI; diff --git a/packages/insomnia/src/main/window-utils.ts b/packages/insomnia/src/main/window-utils.ts index c9a0f27dc0..b1754e2548 100644 --- a/packages/insomnia/src/main/window-utils.ts +++ b/packages/insomnia/src/main/window-utils.ts @@ -201,7 +201,7 @@ export function createWindow(): ElectronBrowserWindow { webPreferences: { preload: path.join(__dirname, 'entry.preload.min.js'), zoomFactor: getZoomFactor(), - nodeIntegration: false, + nodeIntegration: true, nodeIntegrationInWorker: false, // must remain false to ensure the nunjucks web worker sandbox does not have access to Node.js APIs webviewTag: true, // TODO: enable context isolation diff --git a/packages/insomnia/src/network/network-adapter.node.ts b/packages/insomnia/src/network/network-adapter.node.ts index 739d4908f7..746e2c2216 100644 --- a/packages/insomnia/src/network/network-adapter.node.ts +++ b/packages/insomnia/src/network/network-adapter.node.ts @@ -4,7 +4,6 @@ import nodePath from 'node:path'; import clone from 'clone'; import type { RequestHeader } from '~/insomnia-data'; -import { services } from '~/insomnia-data'; import type { RenderedRequest } from '~/templating/types'; import type { RequestContext } from '../../../insomnia-scripting-environment/src/objects'; @@ -48,23 +47,10 @@ export const runScript = (options: { context: RequestContext; }): Promise => executeScript(options); -async function canRunPluginHooksInNodeAdapter() { - if (!process.versions?.electron || typeof global.require !== 'function') { - return false; - } - - const settings = await services.settings.get(); - return Boolean(settings); -} - export async function applyRequestHooks( newRenderedRequest: RenderedRequest, renderedContext: Record, ): Promise { - if (!await canRunPluginHooksInNodeAdapter()) { - return newRenderedRequest; - } - const pluginIndex = require('../plugins/index'); for (const { plugin, hook } of await pluginIndex.getRequestHooks()) { const context = { @@ -90,10 +76,6 @@ export async function applyResponseHooks( renderedRequest: RenderedRequest, renderedContext: Record, ): Promise { - if (!await canRunPluginHooksInNodeAdapter()) { - return response; - } - const newResponse = clone(response); const newRequest = clone(renderedRequest); const pluginIndex = require('../plugins/index'); diff --git a/packages/insomnia/src/network/network-adapter.renderer.ts b/packages/insomnia/src/network/network-adapter.renderer.ts index fe580e9fde..c03f93780b 100644 --- a/packages/insomnia/src/network/network-adapter.renderer.ts +++ b/packages/insomnia/src/network/network-adapter.renderer.ts @@ -31,10 +31,6 @@ export async function applyRequestHooks( newRenderedRequest: RenderedRequest, renderedContext: Record, ): Promise { - if (!globalThis.window?.main?.plugins) { - return newRenderedRequest; - } - if (!await pluginsBridge.hasRequestHooks()) { return newRenderedRequest; } @@ -50,10 +46,6 @@ export async function applyResponseHooks( renderedRequest: RenderedRequest, renderedContext: Record, ): Promise { - if (!globalThis.window?.main?.plugins) { - return response; - } - if (!await pluginsBridge.hasResponseHooks()) { return response; } diff --git a/packages/insomnia/src/network/network-adapter.ts b/packages/insomnia/src/network/network-adapter.ts index 065be33b32..9409e0d477 100644 --- a/packages/insomnia/src/network/network-adapter.ts +++ b/packages/insomnia/src/network/network-adapter.ts @@ -4,10 +4,7 @@ import type * as AdapterType from './network-adapter.renderer'; const impl = ( - (process as any).type === 'renderer' && - globalThis.window !== undefined && - globalThis.window.main !== undefined && - globalThis.window.main.plugins !== undefined + (process as any).type === 'renderer' ? require('./network-adapter.renderer') : require(/* @vite-ignore */ './network-adapter.node') ) as typeof AdapterType; diff --git a/packages/insomnia/src/plugins/misc.ts b/packages/insomnia/src/plugins/misc.ts index 4f78bdba96..f5794a366a 100644 --- a/packages/insomnia/src/plugins/misc.ts +++ b/packages/insomnia/src/plugins/misc.ts @@ -3,10 +3,8 @@ import Color from 'color'; import type { ThemeSettings } from '~/insomnia-data'; import { getAppDefaultTheme } from '~/insomnia-data/common'; -import type { SerializableTheme } from './bridge-types'; -import { plugins } from './renderer-bridge'; - -export type ColorScheme = 'default' | 'light' | 'dark'; +import type { Theme } from './index'; +import { type ColorScheme, getThemes } from './index'; export type HexColor = `#${string}`; export type RGBColor = `rgb(${string})`; @@ -333,7 +331,7 @@ export async function setTheme(themeName: string) { return; } - const themes: SerializableTheme[] = await plugins.getThemes(); + const themes: Theme[] = await getThemes(); let selectedTheme = themes.find(t => t.theme.name === themeName); if (!selectedTheme) { diff --git a/packages/insomnia/src/plugins/renderer-bridge.ts b/packages/insomnia/src/plugins/renderer-bridge.ts index 85c1768b57..7b4c4c3652 100644 --- a/packages/insomnia/src/plugins/renderer-bridge.ts +++ b/packages/insomnia/src/plugins/renderer-bridge.ts @@ -1,4 +1,5 @@ import type { PluginBridgeMetrics, PluginsBridgeAPI } from './bridge-types'; +import { invokePluginMethod } from './invoke-method'; // Phase 1a rollback switch: set INSOMNIA_ENABLE_PLUGIN_BRIDGE=false to fall // back to running plugins directly in the renderer (legacy behaviour). @@ -6,17 +7,15 @@ import type { PluginBridgeMetrics, PluginsBridgeAPI } from './bridge-types'; // plugin-system deps it pulls in don't inflate the preload. const bridgeEnabled = process.env.INSOMNIA_ENABLE_PLUGIN_BRIDGE !== 'false'; -async function call>( +function call>( method: M, args?: Parameters[0], -): Promise>> { +): ReturnType { if (bridgeEnabled) { const fn = (window.main.plugins[method] as (...a: any[]) => any); - return fn(args) as Promise>>; + return fn(args) as ReturnType; } - - const { invokePluginMethod } = await import('./invoke-method'); - return invokePluginMethod(method as any, args) as Promise>>; + return invokePluginMethod(method as any, args) as ReturnType; } const emptyBridgeMetrics: PluginBridgeMetrics = { diff --git a/packages/insomnia/src/templating/node.ts b/packages/insomnia/src/templating/node.ts deleted file mode 100644 index 24d8117244..0000000000 --- a/packages/insomnia/src/templating/node.ts +++ /dev/null @@ -1,178 +0,0 @@ -import type { Environment } from 'nunjucks'; -import nunjucks from 'nunjucks'; - -import { localTemplateTags } from '~/templating/local-template-tags'; - -import type { TemplateTag } from '../plugins'; -import BaseExtension from './base-extension'; -import { extractUndefinedVariableKey, RenderError } from './render-error'; - -// Some constants -export const NUNJUCKS_TEMPLATE_GLOBAL_PROPERTY_NAME = '_'; - -type NunjucksEnvironment = Environment & { - extensions: Record; -}; - -// Cached globals -let nunjucksAll: NunjucksEnvironment | null = null; - -/** - * Render text based on stuff - * @param {String} text - Nunjucks template in text form - * @param {Object} [config] - Config options for rendering - * @param {Object} [config.context] - Context to render with - * @param {Object} [config.path] - Path to include in the error message - */ -export function render( - text: string, - config: { - context?: Record; - path?: string; - ignoreUndefinedEnvVariable?: boolean; - } = {}, -) { - const hasNunjucksInterpolationSymbols = text.includes('{{') && text.includes('}}'); - const hasNunjucksCustomTagSymbols = text.includes('{%') && text.includes('%}'); - const hasNunjucksCommentSymbols = text.includes('{#') && text.includes('#}'); - if (!hasNunjucksInterpolationSymbols && !hasNunjucksCustomTagSymbols && !hasNunjucksCommentSymbols) { - return text; - } - const context = config.context || {}; - // context needs to exist on the root for the old templating syntax, and in _ for the new templating syntax - // old: {{ arr[0].prop }} - // new: {{ _['arr-name-with-dash'][0].prop }} - const templatingContext = { ...context, [NUNJUCKS_TEMPLATE_GLOBAL_PROPERTY_NAME]: context }; - const path = config.path || null; - return new Promise(async (resolve, reject) => { - // NOTE: this is added as a breadcrumb because renderString sometimes hangs - const id = setTimeout(() => console.log('[templating] Warning: nunjucks failed to respond within 5 seconds'), 5000); - const nj = await getNunjucks(config.ignoreUndefinedEnvVariable); - nj?.renderString(text, templatingContext, (err: Error | null, result: any) => { - clearTimeout(id); - if (!err) { - return resolve(result); - } - console.warn('[templating] Error rendering template', err); - const sanitizedMsg = err.message - .replace(/\(unknown path\)\s/, '') - .replace(/\[Line \d+, Column \d*]/, '') - .replace(/^\s*Error:\s*/, '') - .trim(); - const location = err.message.match(/\[Line (\d+), Column (\d+)*]/); - const line = location ? Number.parseInt(location[1]) : 1; - const column = location ? Number.parseInt(location[2]) : 1; - const reason = err.message.includes('attempted to output null or undefined value') ? 'undefined' : 'error'; - const newError = new RenderError(sanitizedMsg); - newError.path = path || ''; - newError.message = sanitizedMsg; - newError.location = { - line, - column, - }; - newError.type = 'render'; - newError.reason = reason; - // regard as environment variable missing - if (hasNunjucksInterpolationSymbols && reason === 'undefined') { - newError.extraInfo = { - subType: 'environmentVariable', - undefinedEnvironmentVariables: extractUndefinedVariableKey(text, templatingContext), - }; - } - reject(newError); - }); - }); -} - -/** - * Reload Nunjucks environments. Useful for if plugins change. - */ -export function reload() { - nunjucksAll = null; -} - -/** - * Get definitions of template tags - */ -export async function getTagDefinitions() { - const env = await getNunjucks(); - - return Object.keys(env.extensions) - .map(k => env.extensions[k]) - .filter(ext => !ext.isDeprecated()) - .sort((a, b) => (a.getPriority() > b.getPriority() ? 1 : -1)) - .map(ext => ({ - name: ext.getTag() || '', - displayName: ext.getName() || '', - liveDisplayName: ext.getLiveDisplayName(), - description: ext.getDescription(), - disablePreview: ext.getDisablePreview(), - args: ext.getArgs(), - actions: ext.getActions(), - })); -} - -async function getNunjucks(ignoreUndefinedEnvVariable?: boolean): Promise { - let throwOnUndefined = true; - if (ignoreUndefinedEnvVariable) { - throwOnUndefined = false; - } else if (nunjucksAll) { - return nunjucksAll; - } - - // ~~~~~~~~~~~~ // - // Setup Config // - // ~~~~~~~~~~~~ // - const config = { - autoescape: false, - // Don't escape HTML - throwOnUndefined, - // Strict mode - tags: { - blockStart: '{%', - blockEnd: '%}', - variableStart: '{{', - variableEnd: '}}', - commentStart: '{#', - commentEnd: '#}', - }, - }; - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // - // Create Env with Extensions // - // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // - const nunjucksEnvironment = nunjucks.configure(config) as NunjucksEnvironment; - nunjucksEnvironment.addGlobal('range', () => {}); - nunjucksEnvironment.addGlobal('cycler', () => {}); - nunjucksEnvironment.addGlobal('joiner', () => {}); - const pluginTemplateTags: TemplateTag[] = []; - - const allExtensions = [ - ...localTemplateTags, - - // Spread after local tags to allow plugins to override them. - // TODO: Determine if this is in fact the behavior we've explicitly decided to support. - ...pluginTemplateTags, - ]; - - for (const extension of allExtensions) { - const { templateTag, plugin } = extension; - templateTag.priority = templateTag.priority || allExtensions.indexOf(extension); - const instance = new BaseExtension(templateTag, plugin); - nunjucksEnvironment.addExtension(instance.getTag() || '', instance); - // Hidden helper filter to debug complicated things - // eg. `{{ foo | urlencode | debug | upper }}` - nunjucksEnvironment.addFilter('debug', (o: any) => o); - } - - // ~~~~~~~~~~~~~~~~~~~~ // - // Cache Env and Return (when ignoreUndefinedEnvVariable is false) // - // ~~~~~~~~~~~~~~~~~~~~ // - if (ignoreUndefinedEnvVariable) { - return nunjucksEnvironment; - } - - nunjucksAll = nunjucksEnvironment; - - return nunjucksEnvironment; -} diff --git a/packages/insomnia/src/templating/utils.ts b/packages/insomnia/src/templating/utils.ts index fd00e5081c..a163d45ff3 100644 --- a/packages/insomnia/src/templating/utils.ts +++ b/packages/insomnia/src/templating/utils.ts @@ -1,6 +1,7 @@ import type { EditorFromTextArea, MarkerRange } from 'codemirror'; import { models, services } from '~/insomnia-data'; +import { decryptSecretValue } from '~/utils/vault'; import type { NunjucksParsedTag, NunjucksParsedTagArg, RenderPurpose } from '../templating/types'; import { decryptVaultKeyFromSession } from '../utils/vault'; @@ -156,7 +157,6 @@ export async function maskOrDecryptVaultDataIfNecessary(vaultEnvironmentData: an const { vaultKey, vaultSalt } = await services.userSession.get(); const isVaultEnabled = !!vaultSalt; if (isVaultEnabled && vaultKey) { - const { decryptSecretValue } = await import('~/utils/vault-crypto'); const symmetricKey = (await decryptVaultKeyFromSession(vaultKey, true)) as JsonWebKey; // decrypt all secret values under vaultEnvironmentPath property in context Object.keys(vaultEnvironmentData).forEach(vaultContextKey => { diff --git a/packages/insomnia/types/global.d.ts b/packages/insomnia/types/global.d.ts index f9e904569c..0f819a0877 100644 --- a/packages/insomnia/types/global.d.ts +++ b/packages/insomnia/types/global.d.ts @@ -10,14 +10,10 @@ declare global { main: RendererToMainBridgeAPI; bridge: HiddenBrowserWindowToMainBridgeAPI; database: DatabaseBridgeAPI; - global: typeof globalThis; // This is a temporary measure to provide access to services on the global window object. It will be removed in the future once all usages are updated to import services directly from the insomnia-data package. _dataServices?: Services; dialog: Pick; - app: Pick & { - env: Record; - process: { platform: NodeJS.Platform }; - }; + app: Pick & { process: { platform: NodeJS.Platform } }; shell: Pick; clipboard: Pick; webUtils: Pick;