diff --git a/.gitignore b/.gitignore index ed60e1959a..35083ffed2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,3 @@ packages/insomnia-smoke-test/screenshots *.tsbuildinfo dist .history -packages/insomnia/src/main.min.js.map diff --git a/packages/insomnia/.gitignore b/packages/insomnia/.gitignore index 86a72d5b1f..bc1c552dc8 100644 --- a/packages/insomnia/.gitignore +++ b/packages/insomnia/.gitignore @@ -3,3 +3,6 @@ build # Generated src/main.min.js +src/main.min.js.map +src/preload.js +src/preload.js.map diff --git a/packages/insomnia/esbuild.main.ts b/packages/insomnia/esbuild.main.ts index 4fce96a198..e145de1a36 100644 --- a/packages/insomnia/esbuild.main.ts +++ b/packages/insomnia/esbuild.main.ts @@ -31,8 +31,16 @@ export default async function build(options: Options) { 'process.env.INSOMNIA_ENV': JSON.stringify('production'), 'process.env.BUILD_DATE': JSON.stringify(new Date()), }; - - return esbuild.build({ + const preload = esbuild.build({ + entryPoints: ['./src/preload.ts'], + outfile: path.join(outdir, 'preload.js'), + target: 'esnext', + bundle: true, + platform: 'node', + format: 'cjs', + external: ['electron'], + }); + const main = esbuild.build({ entryPoints: ['./src/main.development.ts'], outfile: path.join(outdir, 'main.min.js'), bundle: true, @@ -47,6 +55,7 @@ export default async function build(options: Options) { ...Object.keys(builtinModules), ], }); + return Promise.all([main, preload]); } // Build if ran as a cli script diff --git a/packages/insomnia/esbuild.preload.ts b/packages/insomnia/esbuild.preload.ts deleted file mode 100644 index 26c72958d9..0000000000 --- a/packages/insomnia/esbuild.preload.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { build } from 'esbuild'; -import path from 'path'; - -const outdir = path.join(__dirname, 'build'); - -build({ - entryPoints: ['./src/preload.ts'], - outfile: path.join(outdir, 'preload.js'), - bundle: true, - platform: 'node', - sourcemap: true, - format: 'esm', - external: ['@getinsomnia/node-libcurl', 'electron'], -}); diff --git a/packages/insomnia/scripts/build.ts b/packages/insomnia/scripts/build.ts index bf56f860e2..846399e846 100644 --- a/packages/insomnia/scripts/build.ts +++ b/packages/insomnia/scripts/build.ts @@ -1,5 +1,4 @@ import childProcess from 'child_process'; -import { build } from 'esbuild'; import { readFileSync, writeFileSync } from 'fs'; import licenseChecker from 'license-checker'; import mkdirp from 'mkdirp'; @@ -8,7 +7,7 @@ import path from 'path'; import rimraf from 'rimraf'; import * as vite from 'vite'; -import buildMain from '../esbuild.main'; +import buildMainAndPreload from '../esbuild.main'; // Start build if ran from CLI if (require.main === module) { @@ -138,27 +137,11 @@ export const start = async () => { path.join(buildFolder, 'opensource-licenses.txt') ); - console.log('[build] Building main.min.js'); - await buildMain({ + console.log('[build] Building main.min.js and preload'); + await buildMainAndPreload({ mode: 'production', }); - console.log('[build] Building preload'); - await build({ - entryPoints: [path.join(__dirname, '../src/preload.js')], - outfile: path.join(__dirname, '../build/preload.js'), - platform: 'node', - bundle: true, - target: 'esnext', - sourcemap: false, - format: 'esm', - define: { - 'process.env.NODE_ENV': JSON.stringify('production'), - }, - minify: true, - external: ['electron'], - }); - console.log('[build] Building renderer'); await vite.build({ diff --git a/packages/insomnia/src/__tests__/install.test.ts b/packages/insomnia/src/__tests__/install.test.ts index 3691510b88..392f491371 100644 --- a/packages/insomnia/src/__tests__/install.test.ts +++ b/packages/insomnia/src/__tests__/install.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, jest } from '@jest/globals'; -import { containsOnlyDeprecationWarnings, isDeprecatedDependencies } from '../plugins/install'; +import { containsOnlyDeprecationWarnings, isDeprecatedDependencies } from '../main/install-plugin'; describe('install.js', () => { describe('containsOnlyDeprecationWarning', () => { diff --git a/packages/insomnia/src/common/misc.ts b/packages/insomnia/src/common/misc.ts index b9dd841789..6f962fe3d0 100644 --- a/packages/insomnia/src/common/misc.ts +++ b/packages/insomnia/src/common/misc.ts @@ -450,14 +450,3 @@ export const toTitleCase = (value: string) => ( .map(capitalize) .join(' ') ); - -// Because node-libcurl changed some names that we used in the timeline -export const LIBCURL_DEBUG_MIGRATION_MAP = { - HeaderIn: 'HEADER_IN', - DataIn: 'DATA_IN', - SslDataIn: 'SSL_DATA_IN', - HeaderOut: 'HEADER_OUT', - DataOut: 'DATA_OUT', - SslDataOut: 'SSL_DATA_OUT', - Text: 'TEXT', -} as const; diff --git a/packages/insomnia/src/global.d.ts b/packages/insomnia/src/global.d.ts index 4a28b8439a..da6e30b361 100644 --- a/packages/insomnia/src/global.d.ts +++ b/packages/insomnia/src/global.d.ts @@ -1,11 +1,13 @@ -declare module '*.svg' { - const content: any; - export default content; -} +import type { MainBridgeAPI } from './main/ipc/main'; -declare module '*.png' { - const content: any; - export default content; +declare global { + interface Window { + __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: Function; + main: MainBridgeAPI; + dialog: Pick; + app: Pick; + shell: Pick; + } } declare const __DEV__: boolean; @@ -19,38 +21,3 @@ declare namespace NodeJS { jsyaml: any; } } - -interface Window { - __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: Function; - main: { - restart: () => void; - authorizeUserInWindow: (options: { url: string; urlSuccessRegex?: RegExp; urlFailureRegex?: RegExp; sessionId: string }) => Promise; - setMenuBarVisibility: (visible: boolean) => void; - installPlugin: (url: string) => void; - writeFile: (options: {path: string; content: string}) => Promise; - cancelCurlRequest: (requestId: string) => void; - curlRequest: (options: { - curlOptions: CurlOpt[]; - responseBodyPath: string; - maxTimelineDataSizeKB: number; - requestId: string; - requestBodyPath?: string; - isMultipart: boolean; - }) => Promise<{ - patch: ResponsePatch; - debugTimeline: ResponseTimelineEntry[]; - headerResults: HeaderResult[]; - }>; - }; - dialog: { - showOpenDialog: (options: Electron.OpenDialogOptions) => Promise; - showSaveDialog: (options: Electron.SaveDialogOptions) => Promise; - }; - app: { - getPath: (name: string) => string; - getAppPath: () => string; - }; - shell: { - showItemInFolder: (fullPath: string) => void; - }; -} diff --git a/packages/insomnia/src/main.development.ts b/packages/insomnia/src/main.development.ts index 659992758a..2dade09bbb 100644 --- a/packages/insomnia/src/main.development.ts +++ b/packages/insomnia/src/main.development.ts @@ -1,7 +1,6 @@ -import * as electron from 'electron'; +import electron, { app, ipcMain, session } from 'electron'; import contextMenu from 'electron-context-menu'; import installExtension, { REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS } from 'electron-devtools-installer'; -import { writeFile } from 'fs/promises'; import path from 'path'; import appConfig from '../config/config.json'; @@ -11,16 +10,15 @@ import { database } from './common/database'; import { disableSpellcheckerDownload } from './common/electron-helpers'; import log, { initializeLogging } from './common/log'; import { validateInsomniaConfig } from './common/validate-insomnia-config'; -import * as grpcIpcMain from './main/grpc-ipc-main'; +import { registerElectronHandlers } from './main/ipc/electron'; +import { registergRPCHandlers } from './main/ipc/grpc'; +import { registerMainHandlers } from './main/ipc/main'; import { initializeSentry, sentryWatchAnalyticsEnabled } from './main/sentry'; import { checkIfRestartNeeded } from './main/squirrel-startup'; import * as updates from './main/updates'; import * as windowUtils from './main/window-utils'; import * as models from './models/index'; import type { Stats } from './models/stats'; -import { cancelCurlRequest, curlRequest } from './network/libcurl-promise'; -import { authorizeUserInWindow } from './network/o-auth-2/misc'; -import installPlugin from './plugins/install'; import type { ToastNotification } from './ui/components/toast'; initializeSentry(); @@ -31,7 +29,6 @@ if (checkIfRestartNeeded()) { } initializeLogging(); -const { app, ipcMain, session } = electron; const commandLineArgs = process.argv.slice(1); log.info(`Running version ${getAppVersion()}`); @@ -61,6 +58,7 @@ app.on('web-contents-created', (_, contents) => { // When the app is first launched app.on('ready', async () => { + const { error } = validateInsomniaConfig(); if (error) { @@ -69,6 +67,9 @@ app.on('ready', async () => { app.exit(1); return; } + registerElectronHandlers(); + registerMainHandlers(); + registergRPCHandlers(); disableSpellcheckerDownload(); @@ -92,7 +93,6 @@ app.on('ready', async () => { // Init the rest await updates.init(); - grpcIpcMain.init(); }); // Set as default protocol @@ -218,70 +218,6 @@ async function _trackStats() { trackSegmentEvent(SegmentEvent.appStarted, {}, { queueable: true }); - ipcMain.handle('showOpenDialog', async (_, options: Electron.OpenDialogOptions) => { - const { filePaths, canceled } = await electron.dialog.showOpenDialog(options); - return { filePaths, canceled }; - }); - - ipcMain.handle('showSaveDialog', async (_, options: Electron.SaveDialogOptions) => { - const { filePath, canceled } = await electron.dialog.showSaveDialog(options); - return { filePath, canceled }; - }); - - ipcMain.handle('installPlugin', (_, options) => { - return installPlugin(options); - }); - - ipcMain.on('showItemInFolder', (_, name) => { - electron.shell.showItemInFolder(name); - }); - - ipcMain.on('restart', () => { - app.relaunch(); - app.exit(); - }); - - ipcMain.on('setMenuBarVisibility', (_, visible) => { - electron.BrowserWindow.getAllWindows() - .forEach(window => { - // the `setMenuBarVisibility` signature uses `visible` semantics - window.setMenuBarVisibility(visible); - // the `setAutoHideMenu` signature uses `hide` semantics - const hide = !visible; - window.setAutoHideMenuBar(hide); - }); - }); - - ipcMain.on('getPath', (event, name) => { - event.returnValue = electron.app.getPath(name); - }); - - ipcMain.on('getAppPath', event => { - event.returnValue = electron.app.getAppPath(); - }); - - ipcMain.handle('authorizeUserInWindow', (_, options) => { - const { url, urlSuccessRegex, urlFailureRegex, sessionId } = options; - return authorizeUserInWindow({ url, urlSuccessRegex, urlFailureRegex, sessionId }); - }); - - ipcMain.handle('writeFile', async (_, options) => { - try { - await writeFile(options.path, options.content); - return options.path; - } catch (err) { - throw new Error(err); - } - }); - - ipcMain.handle('curlRequest', (_, options) => { - return curlRequest(options); - }); - - ipcMain.on('cancelCurlRequest', (_, requestId: string): void => { - cancelCurlRequest(requestId); - }); - ipcMain.once('window-ready', () => { const { currentVersion, launches, lastVersion } = stats; diff --git a/packages/insomnia/src/main/__tests__/grpc-ipc-main.test.ts b/packages/insomnia/src/main/__tests__/grpc-ipc-main.test.ts index bd91417985..91848f804b 100644 --- a/packages/insomnia/src/main/__tests__/grpc-ipc-main.test.ts +++ b/packages/insomnia/src/main/__tests__/grpc-ipc-main.test.ts @@ -4,7 +4,7 @@ import { ipcMain } from 'electron'; import { GrpcRequestEventEnum } from '../../common/grpc-events'; import * as grpc from '../../network/grpc'; import { ResponseCallbacks } from '../../network/grpc/response-callbacks'; -import * as grpcIpcMain from '../grpc-ipc-main'; +import * as grpcIpcMain from '../ipc/grpc'; jest.mock('../../network/grpc'); @@ -14,7 +14,7 @@ describe('grpcIpcMain', () => { }; const id = 'abc'; beforeEach(() => { - grpcIpcMain.init(); // ipcMain is mocked + grpcIpcMain.registergRPCHandlers(); // ipcMain is mocked }); it.each(Object.values(GrpcRequestEventEnum))('should add listener for channel: %s', channel => { diff --git a/packages/insomnia/src/plugins/install.ts b/packages/insomnia/src/main/install-plugin.ts similarity index 99% rename from packages/insomnia/src/plugins/install.ts rename to packages/insomnia/src/main/install-plugin.ts index 73a6cbfb64..49c74d3da3 100644 --- a/packages/insomnia/src/plugins/install.ts +++ b/packages/insomnia/src/main/install-plugin.ts @@ -1,5 +1,6 @@ import childProcess from 'child_process'; import * as electron from 'electron'; +import { app } from 'electron'; import fs from 'fs'; import fsx from 'fs-extra'; import mkdirp from 'mkdirp'; @@ -248,8 +249,6 @@ export function isDeprecatedDependencies(str: string) { } function _getYarnPath() { - const { app } = process.type === 'renderer' ? window : electron; - // TODO: This is brittle. Make finding this more robust. if (isDevelopment()) { return path.resolve(app.getAppPath(), './bin/yarn-standalone.js'); diff --git a/packages/insomnia/src/main/ipc/electron.ts b/packages/insomnia/src/main/ipc/electron.ts new file mode 100644 index 0000000000..ec0cc2ca2a --- /dev/null +++ b/packages/insomnia/src/main/ipc/electron.ts @@ -0,0 +1,36 @@ +import type { OpenDialogOptions, SaveDialogOptions } from 'electron'; +import { app, BrowserWindow, dialog, ipcMain, shell } from 'electron'; + +export function registerElectronHandlers() { + ipcMain.on('setMenuBarVisibility', (_, visible: boolean) => { + BrowserWindow.getAllWindows() + .forEach(window => { + // the `setMenuBarVisibility` signature uses `visible` semantics + window.setMenuBarVisibility(visible); + // the `setAutoHideMenu` signature uses `hide` semantics + const hide = !visible; + window.setAutoHideMenuBar(hide); + }); + }); + ipcMain.handle('showOpenDialog', async (_, options: OpenDialogOptions) => { + const { filePaths, canceled } = await dialog.showOpenDialog(options); + return { filePaths, canceled }; + }); + + ipcMain.handle('showSaveDialog', async (_, options: SaveDialogOptions) => { + const { filePath, canceled } = await dialog.showSaveDialog(options); + return { filePath, canceled }; + }); + + ipcMain.on('showItemInFolder', (_, name: string) => { + shell.showItemInFolder(name); + }); + + ipcMain.on('getPath', (event, name: Parameters[0]) => { + event.returnValue = app.getPath(name); + }); + + ipcMain.on('getAppPath', event => { + event.returnValue = app.getAppPath(); + }); +} diff --git a/packages/insomnia/src/main/grpc-ipc-main.ts b/packages/insomnia/src/main/ipc/grpc.ts similarity index 69% rename from packages/insomnia/src/main/grpc-ipc-main.ts rename to packages/insomnia/src/main/ipc/grpc.ts index ce8fdc4ae2..6994eb06fd 100644 --- a/packages/insomnia/src/main/grpc-ipc-main.ts +++ b/packages/insomnia/src/main/ipc/grpc.ts @@ -1,11 +1,11 @@ import { ipcMain } from 'electron'; -import { GrpcRequestEventEnum } from '../common/grpc-events'; -import * as grpc from '../network/grpc'; -import { GrpcIpcRequestParams } from '../network/grpc/prepare'; -import { ResponseCallbacks } from '../network/grpc/response-callbacks'; +import { GrpcRequestEventEnum } from '../../common/grpc-events'; +import * as grpc from '../../network/grpc'; +import { GrpcIpcRequestParams } from '../../network/grpc/prepare'; +import { ResponseCallbacks } from '../../network/grpc/response-callbacks'; -export function init() { +export function registergRPCHandlers() { ipcMain.on(GrpcRequestEventEnum.start, (event, params: GrpcIpcRequestParams) => grpc.start(params, new ResponseCallbacks(event)), ); diff --git a/packages/insomnia/src/main/ipc/main.ts b/packages/insomnia/src/main/ipc/main.ts new file mode 100644 index 0000000000..a938bf67e4 --- /dev/null +++ b/packages/insomnia/src/main/ipc/main.ts @@ -0,0 +1,47 @@ +import { app, ipcMain } from 'electron'; +import { writeFile } from 'fs/promises'; + +import { authorizeUserInWindow } from '../../network/o-auth-2/misc'; +import installPlugin from '../install-plugin'; +import { cancelCurlRequest, curlRequest } from '../network/libcurl-promise'; + +export interface MainBridgeAPI { + restart: () => void; + authorizeUserInWindow: typeof authorizeUserInWindow; + setMenuBarVisibility: (visible: boolean) => void; + installPlugin: typeof installPlugin; + writeFile: (options: { path: string; content: string }) => Promise; + cancelCurlRequest: typeof cancelCurlRequest; + curlRequest: typeof curlRequest; +} +export function registerMainHandlers() { + ipcMain.handle('authorizeUserInWindow', (_, options: Parameters[0]) => { + const { url, urlSuccessRegex, urlFailureRegex, sessionId } = options; + return authorizeUserInWindow({ url, urlSuccessRegex, urlFailureRegex, sessionId }); + }); + + ipcMain.handle('writeFile', async (_, options: { path: string; content: string }) => { + try { + await writeFile(options.path, options.content); + return options.path; + } catch (err) { + throw new Error(err); + } + }); + + ipcMain.handle('curlRequest', (_, options: Parameters[0]) => { + return curlRequest(options); + }); + + ipcMain.on('cancelCurlRequest', (_, requestId: string): void => { + cancelCurlRequest(requestId); + }); + + ipcMain.handle('installPlugin', (_, lookupName: string) => { + return installPlugin(lookupName); + }); + ipcMain.on('restart', () => { + app.relaunch(); + app.exit(); + }); +} diff --git a/packages/insomnia/src/network/libcurl-promise.ts b/packages/insomnia/src/main/network/libcurl-promise.ts similarity index 90% rename from packages/insomnia/src/network/libcurl-promise.ts rename to packages/insomnia/src/main/network/libcurl-promise.ts index 808ed59812..91d1b2f024 100644 --- a/packages/insomnia/src/network/libcurl-promise.ts +++ b/packages/insomnia/src/main/network/libcurl-promise.ts @@ -11,17 +11,15 @@ import mkdirp from 'mkdirp'; import path from 'path'; import { Readable, Writable } from 'stream'; import tls from 'tls'; -import { ValueOf } from 'type-fest'; import { parse as urlParse } from 'url'; import { v4 as uuidv4 } from 'uuid'; -import { version } from '../../package.json'; -import { AUTH_AWS_IAM, AUTH_DIGEST, AUTH_NETRC, AUTH_NTLM, CONTENT_TYPE_FORM_DATA, CONTENT_TYPE_FORM_URLENCODED } from '../common/constants'; -import { describeByteSize, hasAuthHeader, hasUserAgentHeader } from '../common/misc'; -import { ClientCertificate } from '../models/client-certificate'; -import { ResponseHeader } from '../models/response'; +import { version } from '../../../package.json'; +import { AUTH_AWS_IAM, AUTH_DIGEST, AUTH_NETRC, AUTH_NTLM, CONTENT_TYPE_FORM_DATA, CONTENT_TYPE_FORM_URLENCODED } from '../../common/constants'; +import { describeByteSize, hasAuthHeader, hasUserAgentHeader } from '../../common/misc'; +import { ClientCertificate } from '../../models/client-certificate'; +import { ResponseHeader } from '../../models/response'; import { buildMultipart } from './multipart'; -import { ResponsePatch } from './network'; import { parseHeaderStrings } from './parse-header-strings'; interface CurlRequestOptions { @@ -57,8 +55,9 @@ interface SettingsUsedHere { httpsProxy: string; noProxy: string; } -interface ResponseTimelineEntry { - name: ValueOf; + +export interface ResponseTimelineEntry { + name: keyof typeof CurlInfoDebug; timestamp: number; value: string; } @@ -70,6 +69,26 @@ interface CurlRequestOutput { responseBodyPath?: string; } +export interface ResponsePatch { + bodyCompression?: 'zip' | null; + bodyPath?: string; + bytesContent?: number; + bytesRead?: number; + contentType?: string; + elapsedTime: number; + environmentId?: string | null; + error?: string; + headers?: ResponseHeader[]; + httpVersion?: string; + message?: string; + parentId?: string; + settingSendCookies?: boolean; + settingStoreCookies?: boolean; + statusCode?: number; + statusMessage?: string; + timelinePath?: string; + url?: string; +} const getDataDirectory = () => process.env.INSOMNIA_DATA_PATH || electron.app.getPath('userData'); // NOTE: this is a dictionary of functions to close open listeners @@ -99,23 +118,23 @@ export const curlRequest = (options: CurlRequestOptions) => new Promise new Promise new Promise new Promise new Promise new Promise responseBodyWriteStream.end()); - curl.on('end', async (_1, _2, rawHeaders: Buffer) => { + curl.on('end', async (_1: any, _2: any, rawHeaders: Buffer) => { const patch = { bytesContent: responseBodyBytes, bytesRead: curl.getInfo(Curl.info.SIZE_DOWNLOAD) as number, @@ -357,18 +377,6 @@ const closeReadFunction = (fd: number, isMultipart: boolean, path?: string) => { } }; -// Because node-libcurl changed some names that we used in the timeline -const LIBCURL_DEBUG_MIGRATION_MAP: Record = { - HeaderIn: 'HEADER_IN', - DataIn: 'DATA_IN', - SslDataIn: 'SSL_DATA_IN', - HeaderOut: 'HEADER_OUT', - DataOut: 'DATA_OUT', - SslDataOut: 'SSL_DATA_OUT', - Text: 'TEXT', - '': '', -}; - interface HeaderResult { headers: ResponseHeader[]; version: string; diff --git a/packages/insomnia/src/network/multipart.ts b/packages/insomnia/src/main/network/multipart.ts similarity index 98% rename from packages/insomnia/src/network/multipart.ts rename to packages/insomnia/src/main/network/multipart.ts index bc212b36da..f83df97744 100644 --- a/packages/insomnia/src/network/multipart.ts +++ b/packages/insomnia/src/main/network/multipart.ts @@ -7,7 +7,7 @@ import { lookup } from 'mime-types'; import os from 'os'; import path from 'path'; -import type { RequestBodyParameter } from '../models/request'; +import type { RequestBodyParameter } from '../../models/request'; export const DEFAULT_BOUNDARY = 'X-INSOMNIA-BOUNDARY'; diff --git a/packages/insomnia/src/network/parse-header-strings.ts b/packages/insomnia/src/main/network/parse-header-strings.ts similarity index 98% rename from packages/insomnia/src/network/parse-header-strings.ts rename to packages/insomnia/src/main/network/parse-header-strings.ts index 0c294a67b9..06beda6de6 100644 --- a/packages/insomnia/src/network/parse-header-strings.ts +++ b/packages/insomnia/src/main/network/parse-header-strings.ts @@ -7,7 +7,7 @@ import { AUTH_DIGEST, AUTH_NTLM, CONTENT_TYPE_FORM_DATA, -} from '../common/constants'; +} from '../../common/constants'; import { getContentTypeHeader, getHostHeader, @@ -15,7 +15,7 @@ import { hasAcceptHeader, hasAuthHeader, hasContentTypeHeader, -} from '../common/misc'; +} from '../../common/misc'; import { DEFAULT_BOUNDARY } from './multipart'; // Special header value that will prevent the header being sent diff --git a/packages/insomnia/src/models/response.ts b/packages/insomnia/src/models/response.ts index 61b9cf44ee..f6c3dfbcc7 100644 --- a/packages/insomnia/src/models/response.ts +++ b/packages/insomnia/src/models/response.ts @@ -1,10 +1,9 @@ import fs from 'fs'; import { Readable } from 'stream'; -import { ValueOf } from 'type-fest'; import zlib from 'zlib'; import { database as db, Query } from '../common/database'; -import { LIBCURL_DEBUG_MIGRATION_MAP } from '../common/misc'; +import type { ResponseTimelineEntry } from '../main/network/libcurl-promise'; import type { BaseModel } from './index'; import * as models from './index'; @@ -23,12 +22,6 @@ export interface ResponseHeader { value: string; } -export interface ResponseTimelineEntry { - name: ValueOf; - timestamp: number; - value: string; -} - type Compression = 'zip' | null | '__NEEDS_MIGRATION__' | undefined; export interface BaseResponse { @@ -234,7 +227,7 @@ export function getTimeline(response: Response, showBody?: boolean) { const timeline = JSON.parse(rawBuffer.toString()) as ResponseTimelineEntry[]; const body: ResponseTimelineEntry[] = showBody ? [ { - name: LIBCURL_DEBUG_MIGRATION_MAP.DataOut, + name: 'DataOut', timestamp: Date.now(), value: fs.readFileSync(bodyPath).toString(), }, diff --git a/packages/insomnia/src/network/__tests__/multipart.test.ts b/packages/insomnia/src/network/__tests__/multipart.test.ts index 51e7188281..dd7781144c 100644 --- a/packages/insomnia/src/network/__tests__/multipart.test.ts +++ b/packages/insomnia/src/network/__tests__/multipart.test.ts @@ -4,7 +4,7 @@ import fs from 'fs'; import path from 'path'; import { globalBeforeEach } from '../../__jest__/before-each'; -import { buildMultipart, DEFAULT_BOUNDARY } from '../multipart'; +import { buildMultipart, DEFAULT_BOUNDARY } from '../../main/network/multipart'; window.app = electron.app; diff --git a/packages/insomnia/src/network/__tests__/network.test.ts b/packages/insomnia/src/network/__tests__/network.test.ts index d9b80754c3..c32b08c7a0 100644 --- a/packages/insomnia/src/network/__tests__/network.test.ts +++ b/packages/insomnia/src/network/__tests__/network.test.ts @@ -17,12 +17,12 @@ import { } from '../../common/constants'; import { filterHeaders } from '../../common/misc'; import { getRenderedRequestAndContext } from '../../common/render'; +import { _parseHeaders, getHttpVersion } from '../../main/network/libcurl-promise'; +import { DEFAULT_BOUNDARY } from '../../main/network/multipart'; +import { _getAwsAuthHeaders } from '../../main/network/parse-header-strings'; import * as models from '../../models'; -import { _parseHeaders, getHttpVersion } from '../libcurl-promise'; -import { DEFAULT_BOUNDARY } from '../multipart'; import * as networkUtils from '../network'; import { getSetCookiesFromResponseHeaders } from '../network'; -import { _getAwsAuthHeaders } from '../parse-header-strings'; window.app = electron.app; diff --git a/packages/insomnia/src/network/__tests__/parse-header-strings.test.ts b/packages/insomnia/src/network/__tests__/parse-header-strings.test.ts index a5c22f616c..9a1e2e2a77 100644 --- a/packages/insomnia/src/network/__tests__/parse-header-strings.test.ts +++ b/packages/insomnia/src/network/__tests__/parse-header-strings.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from '@jest/globals'; import { AUTH_AWS_IAM, CONTENT_TYPE_FORM_DATA } from '../../common/constants'; -import { parseHeaderStrings } from '../parse-header-strings'; +import { parseHeaderStrings } from '../../main/network/parse-header-strings'; describe('parseHeaderStrings', () => { it('should default with empty inputs', () => { diff --git a/packages/insomnia/src/network/authentication.ts b/packages/insomnia/src/network/authentication.ts index f6d8ae421e..ecbc97670c 100644 --- a/packages/insomnia/src/network/authentication.ts +++ b/packages/insomnia/src/network/authentication.ts @@ -25,7 +25,7 @@ export async function getAuthHeader(renderedRequest: RenderedRequest, url: strin const requestId = renderedRequest._id; if (authentication.disabled) { - return null; + return; } if (authentication.type === AUTH_BASIC) { @@ -51,7 +51,7 @@ export async function getAuthHeader(renderedRequest: RenderedRequest, url: strin const token = oAuth2Token.accessToken; return _buildBearerHeader(token, authentication.tokenPrefix); } else { - return null; + return; } } @@ -64,7 +64,7 @@ export async function getAuthHeader(renderedRequest: RenderedRequest, url: strin value: oAuth1Token.Authorization, }; } else { - return null; + return; } } @@ -138,12 +138,12 @@ export async function getAuthHeader(renderedRequest: RenderedRequest, url: strin }); } - return null; + return; } export const _buildBearerHeader = (accessToken: string, prefix: string) => { if (!accessToken) { - return null; + return; } const header = { diff --git a/packages/insomnia/src/network/network.ts b/packages/insomnia/src/network/network.ts index b6137a0505..f968903fbc 100644 --- a/packages/insomnia/src/network/network.ts +++ b/packages/insomnia/src/network/network.ts @@ -28,11 +28,11 @@ import { RENDER_PURPOSE_NO_RENDER, RENDER_PURPOSE_SEND, } from '../common/render'; +import type { ResponsePatch, ResponseTimelineEntry } from '../main/network/libcurl-promise'; import * as models from '../models'; import { ClientCertificate } from '../models/client-certificate'; import type { Environment } from '../models/environment'; import type { Request } from '../models/request'; -import type { ResponseHeader, ResponseTimelineEntry } from '../models/response'; import type { Settings } from '../models/settings'; import { isWorkspace } from '../models/workspace'; import * as pluginContexts from '../plugins/context/index'; @@ -40,27 +40,6 @@ import * as plugins from '../plugins/index'; import { getAuthHeader } from './authentication'; import { urlMatchesCertHost } from './url-matches-cert-host'; -export interface ResponsePatch { - bodyCompression?: 'zip' | null; - bodyPath?: string; - bytesContent?: number; - bytesRead?: number; - contentType?: string; - elapsedTime: number; - environmentId?: string | null; - error?: string; - headers?: ResponseHeader[]; - httpVersion?: string; - message?: string; - parentId?: string; - settingSendCookies?: boolean; - settingStoreCookies?: boolean; - statusCode?: number; - statusMessage?: string; - timelinePath?: string; - url?: string; -} - // Time since user's last keypress to wait before making the request const MAX_DELAY_TIME = 1000; @@ -95,8 +74,8 @@ export async function _actuallySend( // NOTE: conditionally use ipc bridge, renderer cannot import native modules directly const nodejsCancelCurlRequest = process.type === 'renderer' ? window.main.cancelCurlRequest - // eslint-disable-next-line @typescript-eslint/no-var-requires - : require('./libcurl-promise').cancelCurlRequest; + : (await import('../main/network/libcurl-promise')).cancelCurlRequest; + nodejsCancelCurlRequest(renderedRequest._id); return resolve({ elapsedTime: 0, @@ -122,12 +101,12 @@ export async function _actuallySend( const socketUrl = (match && match[3]) || ''; finalUrl = `${protocol}//${socketUrl}`; } - timeline.push({ value: `Preparing request to ${finalUrl}`, name: 'TEXT', timestamp: Date.now() }); - timeline.push({ value: `Current time is ${new Date().toISOString()}`, name: 'TEXT', timestamp: Date.now() }); - timeline.push({ value: `${renderedRequest.settingEncodeUrl ? 'Enable' : 'Disable'} automatic URL encoding`, name: 'TEXT', timestamp: Date.now() }); + timeline.push({ value: `Preparing request to ${finalUrl}`, name: 'Text', timestamp: Date.now() }); + timeline.push({ value: `Current time is ${new Date().toISOString()}`, name: 'Text', timestamp: Date.now() }); + timeline.push({ value: `${renderedRequest.settingEncodeUrl ? 'Enable' : 'Disable'} automatic URL encoding`, name: 'Text', timestamp: Date.now() }); if (!renderedRequest.settingSendCookies) { - timeline.push({ value: 'Disable cookie sending due to user setting', name: 'TEXT', timestamp: Date.now() }); + timeline.push({ value: 'Disable cookie sending due to user setting', name: 'Text', timestamp: Date.now() }); } const certificates = clientCertificates.filter(c => !c.disabled && urlMatchesCertHost(setDefaultProtocol(c.host, 'https:'), renderedRequest.url)); @@ -137,8 +116,8 @@ export async function _actuallySend( // NOTE: conditionally use ipc bridge, renderer cannot import native modules directly const nodejsCurlRequest = process.type === 'renderer' ? window.main.curlRequest - // eslint-disable-next-line @typescript-eslint/no-var-requires - : require('./libcurl-promise').curlRequest; + : (await import('../main/network/libcurl-promise')).curlRequest; + const requestOptions = { requestId: renderedRequest._id, req: renderedRequest, @@ -160,11 +139,11 @@ export async function _actuallySend( if (totalSetCookies) { const currentUrl = getCurrentUrl({ headerResults, finalUrl }); const { cookies, rejectedCookies } = await addSetCookiesToToughCookieJar({ setCookieStrings, currentUrl, cookieJar }); - rejectedCookies.forEach(errorMessage => timeline.push({ value: `Rejected cookie: ${errorMessage}`, name: 'TEXT', timestamp: Date.now() })); + rejectedCookies.forEach(errorMessage => timeline.push({ value: `Rejected cookie: ${errorMessage}`, name: 'Text', timestamp: Date.now() })); const hasCookiesToPersist = totalSetCookies > rejectedCookies.length; if (hasCookiesToPersist) { await models.cookieJar.update(cookieJar, { cookies }); - timeline.push({ value: `Saved ${totalSetCookies} cookies`, name: 'TEXT', timestamp: Date.now() }); + timeline.push({ value: `Saved ${totalSetCookies} cookies`, name: 'Text', timestamp: Date.now() }); } } } diff --git a/packages/insomnia/src/preload.js b/packages/insomnia/src/preload.js deleted file mode 100644 index 30491dc214..0000000000 --- a/packages/insomnia/src/preload.js +++ /dev/null @@ -1,34 +0,0 @@ -const { contextBridge, ipcRenderer } = require('electron'); - -const main = { - restart: () => ipcRenderer.send('restart'), - authorizeUserInWindow: (/** @type {any} */ options) => ipcRenderer.invoke('authorizeUserInWindow', options), - setMenuBarVisibility: (/** @type {any} */ options) => ipcRenderer.send('setMenuBarVisibility', options), - installPlugin: (/** @type {any} */ options) => ipcRenderer.invoke('installPlugin', options), - curlRequest: (/** @type {any} */ options) => ipcRenderer.invoke('curlRequest', options), - cancelCurlRequest: (/** @type {any} */ options) => ipcRenderer.send('cancelCurlRequest', options), - writeFile: (/** @type {any} */ options) => ipcRenderer.invoke('writeFile', options), -}; -const dialog = { - showOpenDialog: (/** @type {any} */ options) => ipcRenderer.invoke('showOpenDialog', options), - showSaveDialog: (/** @type {any} */ options) => ipcRenderer.invoke('showSaveDialog', options), -}; -const app = { - getPath: (/** @type {any} */ options) => ipcRenderer.sendSync('getPath', options), - getAppPath: (/** @type {any} */ options) => ipcRenderer.sendSync('getAppPath', options), -}; -const shell = { - showItemInFolder: (/** @type {any} */ options) => ipcRenderer.send('showItemInFolder', options), -}; - -if (process.contextIsolated) { - contextBridge.exposeInMainWorld('main', main); - contextBridge.exposeInMainWorld('dialog', dialog); - contextBridge.exposeInMainWorld('app', app); - contextBridge.exposeInMainWorld('shell', shell); -} else { - window.main = main; - window.dialog = dialog; - window.app = app; - window.shell = shell; -} diff --git a/packages/insomnia/src/preload.ts b/packages/insomnia/src/preload.ts new file mode 100644 index 0000000000..dfdf2b57d7 --- /dev/null +++ b/packages/insomnia/src/preload.ts @@ -0,0 +1,34 @@ +import { contextBridge, ipcRenderer } from 'electron'; + +const main: Window['main'] = { + restart: () => ipcRenderer.send('restart'), + authorizeUserInWindow: options => ipcRenderer.invoke('authorizeUserInWindow', options), + setMenuBarVisibility: options => ipcRenderer.send('setMenuBarVisibility', options), + installPlugin: options => ipcRenderer.invoke('installPlugin', options), + curlRequest: options => ipcRenderer.invoke('curlRequest', options), + cancelCurlRequest: options => ipcRenderer.send('cancelCurlRequest', options), + writeFile: options => ipcRenderer.invoke('writeFile', options), +}; +const dialog: Window['dialog'] = { + showOpenDialog: options => ipcRenderer.invoke('showOpenDialog', options), + showSaveDialog: options => ipcRenderer.invoke('showSaveDialog', options), +}; +const app: Window['app'] = { + getPath: options => ipcRenderer.sendSync('getPath', options), + getAppPath: () => ipcRenderer.sendSync('getAppPath'), +}; +const shell: Window['shell'] = { + showItemInFolder: options => ipcRenderer.send('showItemInFolder', options), +}; + +if (process.contextIsolated) { + contextBridge.exposeInMainWorld('main', main); + contextBridge.exposeInMainWorld('dialog', dialog); + contextBridge.exposeInMainWorld('app', app); + contextBridge.exposeInMainWorld('shell', shell); +} else { + window.main = main; + window.dialog = dialog; + window.app = app; + window.shell = shell; +} diff --git a/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx b/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx index e4ee6347c8..b5b41a1c9a 100644 --- a/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx +++ b/packages/insomnia/src/ui/components/editors/body/graph-ql-editor.tsx @@ -21,12 +21,12 @@ import { hotKeyRefs } from '../../../../common/hotkeys'; import { executeHotKey } from '../../../../common/hotkeys-listener'; import { markdownToHTML } from '../../../../common/markdown-to-html'; import { jsonParseOr } from '../../../../common/misc'; +import type { ResponsePatch } from '../../../../main/network/libcurl-promise'; import * as models from '../../../../models/index'; import type { Request } from '../../../../models/request'; import { newBodyRaw } from '../../../../models/request'; import type { Settings } from '../../../../models/settings'; import type { Workspace } from '../../../../models/workspace'; -import type { ResponsePatch } from '../../../../network/network'; import * as network from '../../../../network/network'; import { Dropdown } from '../../base/dropdown/dropdown'; import { DropdownButton } from '../../base/dropdown/dropdown-button'; diff --git a/packages/insomnia/src/ui/components/panes/response-pane.tsx b/packages/insomnia/src/ui/components/panes/response-pane.tsx index b6fc25714b..d43baea23c 100644 --- a/packages/insomnia/src/ui/components/panes/response-pane.tsx +++ b/packages/insomnia/src/ui/components/panes/response-pane.tsx @@ -136,7 +136,7 @@ export class ResponsePane extends PureComponent { const timeline = models.response.getTimeline(response); const headers = timeline - .filter(v => v.name === 'HEADER_IN') + .filter(v => v.name === 'HeaderIn') .map(v => v.value) .join(''); diff --git a/packages/insomnia/src/ui/components/viewers/response-timeline-viewer.tsx b/packages/insomnia/src/ui/components/viewers/response-timeline-viewer.tsx index d5ec20d609..551ecd91be 100644 --- a/packages/insomnia/src/ui/components/viewers/response-timeline-viewer.tsx +++ b/packages/insomnia/src/ui/components/viewers/response-timeline-viewer.tsx @@ -1,9 +1,9 @@ import React, { PureComponent } from 'react'; import { clickLink } from '../../../common/electron-helpers'; -import { LIBCURL_DEBUG_MIGRATION_MAP } from '../../../common/misc'; +import type { ResponseTimelineEntry } from '../../../main/network/libcurl-promise'; import * as models from '../../../models'; -import { Response, ResponseTimelineEntry } from '../../../models/response'; +import type { Response } from '../../../models/response'; import { CodeEditor } from '../codemirror/code-editor'; interface Props { @@ -47,40 +47,16 @@ export class ResponseTimelineViewer extends PureComponent { renderRow(row: ResponseTimelineEntry, i: number, all: ResponseTimelineEntry[]) { const { name, value } = row; const previousName = i > 0 ? all[i - 1].name : ''; - let prefix: string | null = null; - - switch (name) { - case LIBCURL_DEBUG_MIGRATION_MAP.HeaderIn: - prefix = '< '; - break; - - case LIBCURL_DEBUG_MIGRATION_MAP.DataIn: - prefix = '| '; - break; - - case LIBCURL_DEBUG_MIGRATION_MAP.SslDataIn: - prefix = '<< '; - break; - - case LIBCURL_DEBUG_MIGRATION_MAP.HeaderOut: - prefix = '> '; - break; - - case LIBCURL_DEBUG_MIGRATION_MAP.DataOut: - prefix = '| '; - break; - - case LIBCURL_DEBUG_MIGRATION_MAP.SslDataOut: - prefix = '>> '; - break; - - case LIBCURL_DEBUG_MIGRATION_MAP.Text: - prefix = '* '; - break; - - default: - return null; - } + const prefixLookup: Record = { + HeaderIn: '< ', + DataIn: '| ', + SslDataIn: '<< ', + HeaderOut: '> ', + DataOut: '| ', + SslDataOut: '>> ', + Text: '* ', + }; + const prefix: string = prefixLookup[name] || '* '; const lines = (value + '').replace(/\n$/, '').split('\n'); const newLines = lines.filter(l => !l.match(/^\s*$/)).map(l => `${prefix}${l}`); diff --git a/packages/insomnia/tsconfig.build.json b/packages/insomnia/tsconfig.build.json index 1d104113d0..e9d00ad56a 100644 --- a/packages/insomnia/tsconfig.build.json +++ b/packages/insomnia/tsconfig.build.json @@ -9,7 +9,7 @@ "jsx": "react", "experimentalDecorators": true, "skipLibCheck": true, - "types": ["node"], + "types": ["node", "vite/client"], }, "include": [ "**/*.d.ts", diff --git a/packages/insomnia/tsconfig.json b/packages/insomnia/tsconfig.json index d6a72a2aae..231e5b51bf 100644 --- a/packages/insomnia/tsconfig.json +++ b/packages/insomnia/tsconfig.json @@ -13,7 +13,7 @@ "package.json", "scripts", "send-request", - "vite.config.ts", + "vite.config.ts" ], "exclude": [ "**/@types/mocha",