mirror of
https://github.com/Kong/insomnia.git
synced 2025-12-23 22:28:58 -05:00
add lint warning about node modules (#9439)
* add lint warning about node modules * eliminate hash function from test case * use bridge for read file * use main bridges for fs * add header * fix types * remove node:url * path bridge * multipart bridge * clean up eslint file * add naive warning count in order to see impact
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { builtinModules } from 'node:module';
|
||||
|
||||
import eslint from '@eslint/js';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier/flat';
|
||||
@@ -8,10 +10,13 @@ import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
|
||||
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default defineConfig([
|
||||
// https://typescript-eslint.io/getting-started#additional-configs
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.strict,
|
||||
tseslint.configs.stylistic,
|
||||
// Unicorn section
|
||||
eslintPluginUnicorn.configs.unopinionated,
|
||||
{
|
||||
rules: {
|
||||
@@ -48,6 +53,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-switch': 'off', // TODO: delete me
|
||||
},
|
||||
},
|
||||
// Playwright section
|
||||
{
|
||||
...playwright.configs['flat/recommended'],
|
||||
files: ['packages/insomnia-smoke-test/tests/**/*.ts'],
|
||||
@@ -62,7 +68,22 @@ export default defineConfig([
|
||||
'playwright/no-wait-for-timeout': 'error',
|
||||
},
|
||||
},
|
||||
|
||||
// nodeIntegration: false section
|
||||
{
|
||||
files: [
|
||||
'packages/insomnia/src/ui/**/*.{ts,tsx}',
|
||||
// TODO: 'packages/insomnia/src/common/**/*.{ts,tsx}',
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: builtinModules.map(m => `node:${m}`),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// React hooks section
|
||||
{
|
||||
files: ['packages/insomnia/src/**/*.{ts,tsx}'],
|
||||
plugins: { 'react-hooks': reactHooksPlugin },
|
||||
@@ -75,6 +96,7 @@ export default defineConfig([
|
||||
'react-hooks/incompatible-library': 'off', //TODO(use react-aria virtualizer): delete me
|
||||
},
|
||||
},
|
||||
// React section
|
||||
{
|
||||
files: ['packages/insomnia/src/**/*.{ts,tsx}'],
|
||||
...reactPlugin.configs.flat.recommended,
|
||||
@@ -111,6 +133,7 @@ export default defineConfig([
|
||||
'react/no-array-index-key': 'error',
|
||||
},
|
||||
},
|
||||
// simple-import-sort section
|
||||
{
|
||||
plugins: {
|
||||
'simple-import-sort': simpleImportSortPlugin,
|
||||
@@ -119,6 +142,7 @@ export default defineConfig([
|
||||
'simple-import-sort/imports': 'error',
|
||||
},
|
||||
},
|
||||
// General ESLint rules
|
||||
{
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
@@ -148,6 +172,7 @@ export default defineConfig([
|
||||
'no-useless-escape': 'off', // TODO: delete me
|
||||
},
|
||||
},
|
||||
// TypeScript ESLint rules
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/array-type': ['error', { default: 'array', readonly: 'array' }],
|
||||
|
||||
@@ -7,6 +7,7 @@ import contextMenu from 'electron-context-menu';
|
||||
import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
|
||||
import { configureFetch } from 'insomnia-api';
|
||||
|
||||
import { registerPathHandlers } from '~/main/ipc/path';
|
||||
import { registerLLMConfigServiceAPI } from '~/main/llm-config-service';
|
||||
import { insomniaFetch } from '~/ui/insomnia-fetch';
|
||||
|
||||
@@ -75,6 +76,7 @@ app.on('ready', async () => {
|
||||
registerElectronHandlers();
|
||||
// @TODO - Maybe move the register stuff in the registerMainHandlers function
|
||||
registerMainHandlers();
|
||||
registerPathHandlers();
|
||||
registergRPCHandlers();
|
||||
registerGitServiceAPI();
|
||||
registerLLMConfigServiceAPI();
|
||||
|
||||
@@ -181,6 +181,7 @@ const main: Window['main'] = {
|
||||
onDefaultBrowserOAuthRedirect: options => ipcRenderer.invoke('onDefaultBrowserOAuthRedirect', options),
|
||||
cancelAuthorizationInDefaultBrowser: options => ipcRenderer.invoke('cancelAuthorizationInDefaultBrowser', options),
|
||||
setMenuBarVisibility: options => ipcRenderer.send('setMenuBarVisibility', options),
|
||||
multipartBufferToArray: options => ipcRenderer.invoke('multipartBufferToArray', options),
|
||||
installPlugin: (lookupName: string, allowScopedPackageNames = false) =>
|
||||
ipcRenderer.invoke('installPlugin', lookupName, allowScopedPackageNames),
|
||||
curlRequest: options => ipcRenderer.invoke('curlRequest', options),
|
||||
@@ -263,7 +264,12 @@ ipcRenderer.on('hidden-browser-window-response-listener', event => {
|
||||
ports.set('hiddenWindowPort', port);
|
||||
ipcRenderer.invoke('main-window-script-port-ready');
|
||||
});
|
||||
|
||||
const path: Window['path'] = {
|
||||
dirname: (p: string) => ipcRenderer.sendSync('path.dirname', p),
|
||||
basename: (p: string) => ipcRenderer.sendSync('path.basename', p),
|
||||
join: (...paths: string[]) => ipcRenderer.sendSync('path.join', ...paths),
|
||||
resolve: (...paths: string[]) => ipcRenderer.sendSync('path.resolve', ...paths),
|
||||
};
|
||||
const dialog: Window['dialog'] = {
|
||||
showOpenDialog: options => ipcRenderer.invoke('showOpenDialog', options),
|
||||
showSaveDialog: options => ipcRenderer.invoke('showSaveDialog', options),
|
||||
@@ -291,6 +297,7 @@ if (process.contextIsolated) {
|
||||
contextBridge.exposeInMainWorld('shell', shell);
|
||||
contextBridge.exposeInMainWorld('clipboard', clipboard);
|
||||
contextBridge.exposeInMainWorld('webUtils', webUtils);
|
||||
contextBridge.exposeInMainWorld('path', path);
|
||||
} else {
|
||||
window.main = main;
|
||||
window.dialog = dialog;
|
||||
@@ -298,4 +305,5 @@ if (process.contextIsolated) {
|
||||
window.shell = shell;
|
||||
window.clipboard = clipboard;
|
||||
window.webUtils = webUtils;
|
||||
window.path = path;
|
||||
}
|
||||
|
||||
@@ -24,30 +24,30 @@ export type HandleChannels =
|
||||
| 'authorizeUserInWindow'
|
||||
| 'backup'
|
||||
| 'cancelAuthorizationInDefaultBrowser'
|
||||
| 'generateMockRouteDataFromSpec'
|
||||
| 'generateCommitsFromDiff'
|
||||
| 'curl.event.findMany'
|
||||
| 'curl.open'
|
||||
| 'curl.readyState'
|
||||
| 'curlRequest'
|
||||
| 'database.caCertificate.create'
|
||||
| 'extractJsonFileFromPostmanDataDumpArchive'
|
||||
| 'generateCommitsFromDiff'
|
||||
| 'generateMockRouteDataFromSpec'
|
||||
| 'getExecution'
|
||||
| 'getLocalStorageDataFromFileOrigin'
|
||||
| 'git.abortMerge'
|
||||
| 'git.canPushLoader'
|
||||
| 'git.checkoutGitBranch'
|
||||
| 'git.cloneGitRepo'
|
||||
| 'git.commitAndPushToGitRepo'
|
||||
| 'git.commitToGitRepo'
|
||||
| 'git.multipleCommitToGitRepo'
|
||||
| 'git.completeSignInToGitHub'
|
||||
| 'git.completeSignInToGitLab'
|
||||
| 'git.continueMerge'
|
||||
| 'git.createNewGitBranch'
|
||||
| 'git.deleteGitBranch'
|
||||
| 'git.diff'
|
||||
| 'git.diffFileLoader'
|
||||
| 'git.discardChanges'
|
||||
| 'git.abortMerge'
|
||||
| 'git.fetchGitRemoteBranches'
|
||||
| 'git.getGitBranches'
|
||||
| 'git.getGitHubRepositories'
|
||||
@@ -57,13 +57,13 @@ export type HandleChannels =
|
||||
| 'git.gitFetchAction'
|
||||
| 'git.gitLogLoader'
|
||||
| 'git.gitStatus'
|
||||
| 'git.diff'
|
||||
| 'git.initGitRepoClone'
|
||||
| 'git.initSignInToGitHub'
|
||||
| 'git.initSignInToGitLab'
|
||||
| 'git.loadGitRepository'
|
||||
| 'git.mergeGitBranch'
|
||||
| 'git.migrateLegacyInsomniaFolderToFile'
|
||||
| 'git.multipleCommitToGitRepo'
|
||||
| 'git.pullFromGitRemote'
|
||||
| 'git.pushToGitRemote'
|
||||
| 'git.resetGitRepo'
|
||||
@@ -74,33 +74,53 @@ export type HandleChannels =
|
||||
| 'git.updateGitRepo'
|
||||
| 'grpc.loadMethods'
|
||||
| 'grpc.loadMethodsFromReflection'
|
||||
| 'installPlugin'
|
||||
| 'lintSpec'
|
||||
| 'llm.getActiveBackend'
|
||||
| 'llm.setActiveBackend'
|
||||
| 'llm.clearActiveBackend'
|
||||
| 'llm.getBackendConfig'
|
||||
| 'llm.updateBackendConfig'
|
||||
| 'llm.getAllConfigurations'
|
||||
| 'llm.getCurrentConfig'
|
||||
| 'llm.getAIFeatureEnabled'
|
||||
| 'llm.setAIFeatureEnabled'
|
||||
| 'onDefaultBrowserOAuthRedirect'
|
||||
| 'open-channel-to-hidden-browser-window'
|
||||
| 'parseImport'
|
||||
| 'openPath'
|
||||
| 'readCurlResponse'
|
||||
| 'readOrCreateDataDir'
|
||||
| 'readDir'
|
||||
| 'insecureReadFile'
|
||||
| 'insecureReadFileWithEncoding'
|
||||
| 'secureReadFile'
|
||||
| 'installPlugin'
|
||||
| 'lintSpec'
|
||||
| 'llm.clearActiveBackend'
|
||||
| 'llm.getActiveBackend'
|
||||
| 'llm.getAIFeatureEnabled'
|
||||
| 'llm.getAllConfigurations'
|
||||
| 'llm.getBackendConfig'
|
||||
| 'llm.getCurrentConfig'
|
||||
| 'llm.setActiveBackend'
|
||||
| 'llm.setAIFeatureEnabled'
|
||||
| 'llm.updateBackendConfig'
|
||||
| 'mcp.client.cancelRequest'
|
||||
| 'mcp.client.hasRequestResponded'
|
||||
| 'mcp.close'
|
||||
| 'mcp.connect'
|
||||
| 'mcp.event.findMany'
|
||||
| 'mcp.event.findNotifications'
|
||||
| 'mcp.event.findPendingEvents'
|
||||
| 'mcp.notification.rootListChange'
|
||||
| 'mcp.notification.rootListChange'
|
||||
| 'mcp.primitive.callTool'
|
||||
| 'mcp.primitive.getPrompt'
|
||||
| 'mcp.primitive.listPrompts'
|
||||
| 'mcp.primitive.listResources'
|
||||
| 'mcp.primitive.listResourceTemplates'
|
||||
| 'mcp.primitive.listTools'
|
||||
| 'mcp.primitive.readResource'
|
||||
| 'mcp.primitive.subscribeResource'
|
||||
| 'mcp.primitive.unsubscribeResource'
|
||||
| 'mcp.readyState'
|
||||
| 'multipartBufferToArray'
|
||||
| 'onDefaultBrowserOAuthRedirect'
|
||||
| 'open-channel-to-hidden-browser-window'
|
||||
| 'openPath'
|
||||
| 'parseImport'
|
||||
| 'readCurlResponse'
|
||||
| 'readDir'
|
||||
| 'readOrCreateDataDir'
|
||||
| 'restoreBackup'
|
||||
| 'secretStorage.decryptString'
|
||||
| 'secretStorage.deleteSecret'
|
||||
| 'secretStorage.encryptString'
|
||||
| 'secretStorage.getSecret'
|
||||
| 'secretStorage.setSecret'
|
||||
| 'secureReadFile'
|
||||
| 'showOpenDialog'
|
||||
| 'showSaveDialog'
|
||||
| 'socketIO.event.findMany'
|
||||
@@ -111,26 +131,7 @@ export type HandleChannels =
|
||||
| 'webSocket.event.send'
|
||||
| 'webSocket.open'
|
||||
| 'webSocket.readyState'
|
||||
| 'writeFile'
|
||||
| 'mcp.connect'
|
||||
| 'mcp.primitive.listTools'
|
||||
| 'mcp.primitive.callTool'
|
||||
| 'mcp.primitive.listPrompts'
|
||||
| 'mcp.primitive.getPrompt'
|
||||
| 'mcp.primitive.listResources'
|
||||
| 'mcp.primitive.listResourceTemplates'
|
||||
| 'mcp.primitive.readResource'
|
||||
| 'mcp.primitive.subscribeResource'
|
||||
| 'mcp.primitive.unsubscribeResource'
|
||||
| 'mcp.notification.rootListChange'
|
||||
| 'mcp.readyState'
|
||||
| 'mcp.event.findMany'
|
||||
| 'mcp.event.findNotifications'
|
||||
| 'mcp.event.findPendingEvents'
|
||||
| 'mcp.notification.rootListChange'
|
||||
| 'mcp.client.hasRequestResponded'
|
||||
| 'mcp.client.cancelRequest'
|
||||
| 'mcp.close';
|
||||
| 'writeFile';
|
||||
|
||||
export const ipcMainHandle = (
|
||||
channel: HandleChannels,
|
||||
@@ -154,6 +155,10 @@ export type MainOnChannels =
|
||||
| 'manualUpdateCheck'
|
||||
| 'openDeepLink'
|
||||
| 'openInBrowser'
|
||||
| 'path.basename'
|
||||
| 'path.dirname'
|
||||
| 'path.join'
|
||||
| 'path.resolve'
|
||||
| 'readText'
|
||||
| 'restart'
|
||||
| 'set-hidden-window-busy-status'
|
||||
@@ -220,6 +225,7 @@ const getTemplateValue = (arg: NunjucksParsedTagArg) => {
|
||||
}
|
||||
return arg.defaultValue;
|
||||
};
|
||||
|
||||
export function registerElectronHandlers() {
|
||||
ipcMainOn(
|
||||
'show-nunjucks-context-menu',
|
||||
|
||||
@@ -19,6 +19,7 @@ import iconv from 'iconv-lite';
|
||||
import { AI_PLUGIN_NAME } from '~/common/constants';
|
||||
import { convert } from '~/main/importers/convert';
|
||||
import { getCurrentConfig, type LLMConfigServiceAPI } from '~/main/llm-config-service';
|
||||
import { multipartBufferToArray, type Part } from '~/main/multipart-buffer-to-array';
|
||||
import { insecureReadFile, insecureReadFileWithEncoding, secureReadFile } from '~/main/secure-read-file';
|
||||
import type { GenerateCommitsFromDiffFunction, MockRouteData, ModelConfig } from '~/plugins/types';
|
||||
|
||||
@@ -97,6 +98,7 @@ export interface RendererToMainBridgeAPI {
|
||||
setMenuBarVisibility: (visible: boolean) => void;
|
||||
installPlugin: typeof installPlugin;
|
||||
parseImport: typeof convert;
|
||||
multipartBufferToArray: (options: { bodyBuffer: Buffer; contentType: string }) => Promise<Part[]>;
|
||||
writeFile: (options: { path: string; content: string }) => Promise<string>;
|
||||
secureReadFile: (options: { path: string }) => Promise<string>;
|
||||
insecureReadFile: (options: { path: string }) => Promise<string>;
|
||||
@@ -183,6 +185,9 @@ export function registerMainHandlers() {
|
||||
ipcMainHandle('database.caCertificate.create', async (_, options: { parentId: string; path: string }) => {
|
||||
return models.caCertificate.create(options);
|
||||
});
|
||||
ipcMainHandle('multipartBufferToArray', async (_, options) => {
|
||||
return multipartBufferToArray(options);
|
||||
});
|
||||
ipcMainOn('loginStateChange', async () => {
|
||||
BrowserWindow.getAllWindows().forEach(w => {
|
||||
w.webContents.send('loggedIn');
|
||||
|
||||
18
packages/insomnia/src/main/ipc/path.ts
Normal file
18
packages/insomnia/src/main/ipc/path.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { ipcMainOn } from '~/main/ipc/electron';
|
||||
|
||||
export function registerPathHandlers() {
|
||||
ipcMainOn('path.basename', (event, p: string) => {
|
||||
event.returnValue = path.basename(p);
|
||||
});
|
||||
ipcMainOn('path.dirname', (event, p: string) => {
|
||||
event.returnValue = path.dirname(p);
|
||||
});
|
||||
ipcMainOn('path.join', (event, ...paths: string[]) => {
|
||||
event.returnValue = path.join(...paths);
|
||||
});
|
||||
ipcMainOn('path.resolve', (event, ...paths: string[]) => {
|
||||
event.returnValue = path.resolve(...paths);
|
||||
});
|
||||
}
|
||||
71
packages/insomnia/src/main/multipart-buffer-to-array.ts
Normal file
71
packages/insomnia/src/main/multipart-buffer-to-array.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { PassThrough } from 'node:stream';
|
||||
|
||||
import multiparty from 'multiparty';
|
||||
|
||||
export interface Part {
|
||||
id: number;
|
||||
title: string;
|
||||
name: string;
|
||||
bytes: number;
|
||||
value: Buffer;
|
||||
filename: string | null;
|
||||
headers: { name: string; value: string }[];
|
||||
}
|
||||
export function multipartBufferToArray({
|
||||
bodyBuffer,
|
||||
contentType,
|
||||
}: {
|
||||
bodyBuffer: Buffer | null;
|
||||
contentType: string;
|
||||
}): Promise<Part[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const parts: Part[] = [];
|
||||
|
||||
if (!bodyBuffer) {
|
||||
return resolve(parts);
|
||||
}
|
||||
|
||||
const fakeReq = new PassThrough();
|
||||
// @ts-expect-error -- TSCONVERSION investigate `stream` types
|
||||
fakeReq.headers = {
|
||||
'content-type': contentType,
|
||||
};
|
||||
const form = new multiparty.Form();
|
||||
let id = 0;
|
||||
form.on('part', part => {
|
||||
const dataBuffers: any[] = [];
|
||||
part.on('data', data => {
|
||||
dataBuffers.push(data);
|
||||
});
|
||||
part.on('error', err => {
|
||||
reject(new Error(`Failed to parse part: ${err.message}`));
|
||||
});
|
||||
part.on('end', () => {
|
||||
const title = part.filename ? `${part.name} (${part.filename})` : part.name;
|
||||
parts.push({
|
||||
id,
|
||||
title,
|
||||
value: dataBuffers ? Buffer.concat(dataBuffers) : Buffer.from(''),
|
||||
name: part.name,
|
||||
filename: part.filename || null,
|
||||
bytes: part.byteCount,
|
||||
headers: Object.keys(part.headers).map(name => ({
|
||||
name,
|
||||
value: part.headers[name],
|
||||
})),
|
||||
});
|
||||
id += 1;
|
||||
});
|
||||
});
|
||||
form.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
form.on('close', () => {
|
||||
resolve(parts);
|
||||
});
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
form.parse(fakeReq);
|
||||
fakeReq.write(bodyBuffer);
|
||||
fakeReq.end();
|
||||
});
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import './ui';
|
||||
@@ -1,5 +1,3 @@
|
||||
import nodePath from 'node:path';
|
||||
|
||||
import React, { type HTMLAttributes, useCallback } from 'react';
|
||||
|
||||
import { selectFileOrFolder } from '../../../common/select-file-or-folder';
|
||||
@@ -18,7 +16,7 @@ interface Props extends Omit<HTMLAttributes<HTMLButtonElement>, 'onChange'> {
|
||||
export const FileInputButton = (props: Props) => {
|
||||
const { showFileName, showFileIcon, path, name, onChange, itemtypes, extensions, disabled, ...extraProps } = props;
|
||||
// NOTE: Basename fails if path is not a string, so let's make sure it is
|
||||
const fileName = typeof path === 'string' ? nodePath.basename(path) : null;
|
||||
const fileName = typeof path === 'string' ? window.path.basename(path) : null;
|
||||
const _handleChooseFile = useCallback(async () => {
|
||||
const { canceled, filePath } = await selectFileOrFolder({
|
||||
itemTypes: itemtypes,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
import type { IconName } from '@fortawesome/fontawesome-svg-core';
|
||||
import React, { type FC } from 'react';
|
||||
import { Button, Heading, Menu, MenuItem, MenuTrigger, Popover } from 'react-aria-components';
|
||||
@@ -60,7 +58,7 @@ export const DesignEmptyState: FC<Props> = ({ onImport }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const contents = String(await readFile(filePath));
|
||||
const contents = String(await window.main.insecureReadFile({ path: filePath }));
|
||||
onImport(contents);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import React, { type FC, useCallback } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
|
||||
@@ -46,11 +44,11 @@ export const PreviewModeDropdown: FC<Props> = ({ download, copyToClipboard }) =>
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
const to = fs.createWriteStream(filePath);
|
||||
to.on('error', err => {
|
||||
console.warn('Failed to export har', err);
|
||||
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: har,
|
||||
});
|
||||
to.end(har);
|
||||
}, [activeRequest, activeResponse]);
|
||||
|
||||
const exportDebugFile = useCallback(async () => {
|
||||
@@ -74,14 +72,11 @@ export const PreviewModeDropdown: FC<Props> = ({ download, copyToClipboard }) =>
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
const readStream = models.response.getBodyStream(activeResponse);
|
||||
|
||||
if (readStream && filePath && typeof readStream !== 'string') {
|
||||
const to = fs.createWriteStream(filePath);
|
||||
to.write(headers);
|
||||
readStream.pipe(to);
|
||||
to.on('error', err => {
|
||||
console.warn('Failed to save full response', err);
|
||||
if (filePath && activeResponse.bodyBuffer) {
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: headers + '\n' + activeResponse.bodyBuffer.toString('utf8') || '',
|
||||
});
|
||||
}
|
||||
}, [activeRequest, activeResponse]);
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import React, { type FC, useCallback } from 'react';
|
||||
|
||||
import * as misc from '../../../../common/misc';
|
||||
import { FileInputButton } from '../../base/file-input-button';
|
||||
import { PromptButton } from '../../base/prompt-button';
|
||||
|
||||
@@ -26,14 +23,6 @@ export const FileEditor: FC<Props> = ({ onChange, path }) => {
|
||||
// Replace home path with ~/ to make the path shorter
|
||||
const homeDirectory = window.app.getPath('home');
|
||||
const pathDescription = path.replace(homeDirectory, '~');
|
||||
let sizeDescription = '';
|
||||
|
||||
try {
|
||||
const bytes = fs.statSync(path).size;
|
||||
sizeDescription = misc.describeByteSize(bytes);
|
||||
} catch {
|
||||
sizeDescription = '';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-center">
|
||||
@@ -43,8 +32,7 @@ export const FileEditor: FC<Props> = ({ onChange, path }) => {
|
||||
<code className="txt-sm block">
|
||||
<span className="force-wrap selectable" title={path}>
|
||||
{pathDescription}
|
||||
</span>{' '}
|
||||
<span className="no-wrap">({sizeDescription})</span>
|
||||
</span>
|
||||
</code>
|
||||
) : (
|
||||
<code className="super-faint txt-sm block">No file selected</code>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
import type { LintOptions, ShowHintOptions, TextMarker } from 'codemirror';
|
||||
import type { GraphQLHintOptions } from 'codemirror-graphql/hint';
|
||||
import type { GraphQLInfoOptions } from 'codemirror-graphql/info';
|
||||
@@ -432,7 +430,7 @@ export const GraphQLEditor: FC<Props> = ({
|
||||
}
|
||||
try {
|
||||
const filePath = filePaths[0]; // showOpenDialog is single select
|
||||
const file = readFileSync(filePath);
|
||||
const file = await window.main.insecureReadFile({ path: filePath });
|
||||
const content = JSON.parse(file.toString());
|
||||
if (!content.data) {
|
||||
throw new Error('JSON file should have a data field with the introspection results');
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import { CallToolResultSchema, ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
import { type RJSFSchema, type UiSchema } from '@rjsf/utils';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
@@ -23,7 +21,6 @@ import {
|
||||
useRequestLoaderData,
|
||||
} from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
|
||||
import { CodeEditor, type CodeEditorHandle } from '../../components/.client/codemirror/code-editor';
|
||||
import { showError } from '../../components/modals';
|
||||
import { useRequestMetaPatcher } from '../../hooks/use-request';
|
||||
import { Dropdown, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
|
||||
|
||||
@@ -63,20 +60,10 @@ export const MessageEventView = ({ event }: Props) => {
|
||||
if (canceled || !outputPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const to = fs.createWriteStream(outputPath);
|
||||
|
||||
to.on('error', err => {
|
||||
showError({
|
||||
title: 'Save Failed',
|
||||
message: 'Failed to save response body',
|
||||
error: err,
|
||||
});
|
||||
await window.main.writeFile({
|
||||
path: outputPath,
|
||||
content: raw,
|
||||
});
|
||||
|
||||
to.write(raw);
|
||||
|
||||
to.end();
|
||||
}, [raw]);
|
||||
|
||||
const handleCopyResponseToClipboard = useCallback(() => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import type * as Har from 'har-format';
|
||||
import React, { Fragment, useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Tab, TabList, TabPanel, Tabs, Toolbar } from 'react-aria-components';
|
||||
@@ -334,17 +332,19 @@ const PreviewModeDropdown = ({
|
||||
icon="save"
|
||||
label="Export raw response"
|
||||
onClick={async () => {
|
||||
const bodyBuffer = await models.response.getBodyBuffer(activeResponse);
|
||||
const { canceled, filePath } = await window.dialog.showSaveDialog({
|
||||
title: 'Save Full Response',
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: `response-${Date.now()}.txt`,
|
||||
});
|
||||
|
||||
if (canceled || !filePath || !bodyBuffer) {
|
||||
if (canceled || !filePath || !activeResponse.bodyBuffer) {
|
||||
return;
|
||||
}
|
||||
fs.promises.writeFile(filePath, bodyBuffer.toString('utf8'));
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: activeResponse.bodyBuffer?.toString('utf8') || '',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</DropdownItem>
|
||||
@@ -364,7 +364,10 @@ const PreviewModeDropdown = ({
|
||||
if (canceled || !filePath || !bodyBuffer) {
|
||||
return;
|
||||
}
|
||||
fs.promises.writeFile(filePath, jsonPrettify(bodyBuffer.toString('utf8')));
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: jsonPrettify(activeResponse.bodyBuffer?.toString('utf8')) || '',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -389,7 +392,10 @@ const PreviewModeDropdown = ({
|
||||
.map(v => v.value)
|
||||
.join('');
|
||||
|
||||
fs.promises.writeFile(filePath, headers);
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: headers,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</DropdownItem>
|
||||
@@ -411,7 +417,10 @@ const PreviewModeDropdown = ({
|
||||
const data = await exportHarCurrentRequest(activeRequest, activeResponse);
|
||||
const har = JSON.stringify(data, null, '\t');
|
||||
|
||||
fs.promises.writeFile(filePath, har);
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: har,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</DropdownItem>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import fs from 'node:fs/promises';
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
@@ -76,8 +74,7 @@ export const MockRouteModal = ({
|
||||
let body = '';
|
||||
if (responseData?.bodyPath) {
|
||||
try {
|
||||
const bodyBuffer = await fs.readFile(responseData.bodyPath);
|
||||
body = bodyBuffer.toString();
|
||||
body = await window.main.secureReadFile({ path: responseData.bodyPath });
|
||||
} catch (error) {
|
||||
console.error('Failed to read response body:', error);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import * as protoLoader from '@grpc/proto-loader';
|
||||
import React, { type FC, useEffect, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
@@ -232,7 +230,7 @@ export const ProtoFilesModal: FC<Props> = ({ defaultId, onHide, onSave }) => {
|
||||
const protoText = await window.main.insecureReadFile({ path: filePath });
|
||||
|
||||
const updatedFile = await models.protoFile.update(protoFile, {
|
||||
name: path.basename(filePath),
|
||||
name: window.path.basename(filePath),
|
||||
protoText,
|
||||
});
|
||||
const impacted = await models.grpcRequest.findByProtoFileId(updatedFile._id);
|
||||
@@ -287,7 +285,7 @@ export const ProtoFilesModal: FC<Props> = ({ defaultId, onHide, onSave }) => {
|
||||
const protoText = await window.main.insecureReadFile({ path: filePath });
|
||||
|
||||
const newFile = await models.protoFile.create({
|
||||
name: path.basename(filePath),
|
||||
name: window.path.basename(filePath),
|
||||
parentId: workspaceId,
|
||||
protoText,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
import React, { type FC, useState } from 'react';
|
||||
import { Toolbar } from 'react-aria-components';
|
||||
|
||||
@@ -59,9 +57,7 @@ export const RequestTestResultRows: FC<RequestTestResultRowsProps> = ({
|
||||
|
||||
return Boolean(fuzzyMatch(resultFilter, result.testCase, { splitSpace: false, loose: true })?.indexes);
|
||||
})
|
||||
.map((result, i: number) => {
|
||||
const key = crypto.createHash('sha1').update(`${result.testCase}"-${i}`).digest('hex');
|
||||
|
||||
.map(result => {
|
||||
const statusText = {
|
||||
passed: 'PASS',
|
||||
failed: 'FAIL',
|
||||
@@ -99,7 +95,7 @@ export const RequestTestResultRows: FC<RequestTestResultRowsProps> = ({
|
||||
: 'Unknown';
|
||||
|
||||
return (
|
||||
<div key={key} data-testid="test-result-row">
|
||||
<div key={result.testCase} data-testid="test-result-row">
|
||||
<div className="my-3 flex w-full text-base">
|
||||
<div className="m-auto mx-1 leading-4">
|
||||
<span className="mr-2 ml-2">{statusTag}</span>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import { extension as mimeExtension } from 'mime-types';
|
||||
import React, { type FC, useCallback, useMemo } from 'react';
|
||||
import { Tab, TabList, TabPanel, Tabs, Toolbar } from 'react-aria-components';
|
||||
|
||||
import { useRootLoaderData } from '~/root';
|
||||
import { jsonPrettify } from '~/utils/prettify/json';
|
||||
|
||||
import { PREVIEW_MODE_SOURCE } from '../../../common/constants';
|
||||
import { getSetCookieHeaders } from '../../../common/misc';
|
||||
@@ -14,14 +13,12 @@ import {
|
||||
type RequestLoaderData,
|
||||
useRequestLoaderData,
|
||||
} from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
|
||||
import { jsonPrettify } from '../../../utils/prettify/json';
|
||||
import { useExecutionState } from '../../hooks/use-execution-state';
|
||||
import { useRequestMetaPatcher } from '../../hooks/use-request';
|
||||
import { PreviewModeDropdown } from '../dropdowns/preview-mode-dropdown';
|
||||
import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown';
|
||||
import { MockResponseExtractor } from '../editors/mock-response-extractor';
|
||||
import { ErrorBoundary } from '../error-boundary';
|
||||
import { showError } from '../modals';
|
||||
import { ResponseTimer } from '../response-timer';
|
||||
import { SizeTag } from '../tags/size-tag';
|
||||
import { StatusTag } from '../tags/status-tag';
|
||||
@@ -85,34 +82,14 @@ export const ResponsePane: FC<Props> = ({ activeRequestId }) => {
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const readStream = models.response.getBodyStream(activeResponse);
|
||||
const dataBuffers: any[] = [];
|
||||
|
||||
if (readStream && outputPath && typeof readStream !== 'string') {
|
||||
readStream.on('data', data => {
|
||||
dataBuffers.push(data);
|
||||
});
|
||||
readStream.on('end', () => {
|
||||
const to = fs.createWriteStream(outputPath);
|
||||
const finalBuffer = Buffer.concat(dataBuffers);
|
||||
to.on('error', err => {
|
||||
showError({
|
||||
title: 'Save Failed',
|
||||
message: 'Failed to save response body',
|
||||
error: err,
|
||||
});
|
||||
});
|
||||
|
||||
if (prettify && contentType.includes('json')) {
|
||||
to.write(jsonPrettify(finalBuffer.toString('utf8')));
|
||||
} else {
|
||||
to.write(finalBuffer);
|
||||
}
|
||||
|
||||
to.end();
|
||||
if (prettify && contentType.includes('json')) {
|
||||
await window.main.writeFile({
|
||||
path: outputPath,
|
||||
content: jsonPrettify(activeResponse.bodyBuffer?.toString('utf8')) || '',
|
||||
});
|
||||
return;
|
||||
}
|
||||
await window.main.writeFile({ path: outputPath, content: activeResponse.bodyBuffer?.toString('utf8') || '' });
|
||||
},
|
||||
[activeRequest, activeResponse],
|
||||
);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import { format } from 'date-fns';
|
||||
import { getProductName } from 'insomnia/src/common/constants';
|
||||
import { database } from 'insomnia/src/common/database';
|
||||
@@ -108,7 +105,7 @@ const showSaveExportedFileDialog = async ({
|
||||
const options = {
|
||||
title: 'Export Insomnia Data',
|
||||
buttonLabel: 'Export',
|
||||
defaultPath: `${path.join(dir, `${name}_${date}`)}.${selectedFormat}`,
|
||||
defaultPath: `${window.path.join(dir, `${name}_${date}`)}.${selectedFormat}`,
|
||||
};
|
||||
const { filePath } = await window.dialog.showSaveDialog(options);
|
||||
return filePath || null;
|
||||
@@ -131,8 +128,11 @@ const showSaveExportedFolderDialog = async () => {
|
||||
|
||||
async function writeExportedFileToFileSystem(filename: string, data: string) {
|
||||
// Remember last exported path
|
||||
window.localStorage.setItem('insomnia.lastExportPath', path.dirname(filename));
|
||||
await writeFile(filename, data);
|
||||
window.localStorage.setItem('insomnia.lastExportPath', window.path.dirname(filename));
|
||||
await window.main.writeFile({
|
||||
path: filename,
|
||||
content: data,
|
||||
});
|
||||
}
|
||||
|
||||
export const exportProjectToFile = (activeProjectName: string, workspacesForActiveProject: Workspace[]) => {
|
||||
@@ -196,12 +196,14 @@ export const exportProjectToFile = (activeProjectName: string, workspacesForActi
|
||||
}
|
||||
|
||||
const projectName = activeProjectName.replace(/ /g, '-');
|
||||
const insomniaProjectExportFolder = path.join(dirPath, `insomnia-export.${projectName}.${Date.now()}`);
|
||||
await mkdir(insomniaProjectExportFolder);
|
||||
const insomniaProjectExportFolder = window.path.join(
|
||||
dirPath,
|
||||
`insomnia-export.${projectName}.${Date.now()}`,
|
||||
);
|
||||
|
||||
for (const workspace of workspacesForActiveProject) {
|
||||
const workspaceName = workspace.name.replace(/ /g, '-');
|
||||
const fileName = path.join(insomniaProjectExportFolder, `${workspaceName}-${workspace._id}.yaml`);
|
||||
const fileName = window.path.join(insomniaProjectExportFolder, `${workspaceName}-${workspace._id}.yaml`);
|
||||
const stringifiedExport = await getInsomniaV5DataExport({
|
||||
workspaceId: workspace._id,
|
||||
includePrivateEnvironments: shouldExportPrivateEnvironments,
|
||||
@@ -379,7 +381,7 @@ export async function exportWorkspaceData({
|
||||
|
||||
try {
|
||||
const workspaceName = workspace.name.replace(/ /g, '-');
|
||||
const filePath = path.join(dirPath, `${workspaceName}-${workspace._id}.yaml`);
|
||||
const filePath = window.path.join(dirPath, `${workspaceName}-${workspace._id}.yaml`);
|
||||
await writeExportedFileToFileSystem(filePath, insomniaExport);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -404,8 +406,7 @@ export async function exportAllData({ dirPath }: { dirPath: string }): Promise<v
|
||||
includePrivateEnvironments = await showExportPrivateEnvironmentsModal();
|
||||
}
|
||||
|
||||
const insomniaExportFolder = path.join(dirPath, `insomnia-export.${Date.now()}`);
|
||||
await mkdir(insomniaExportFolder);
|
||||
const insomniaExportFolder = window.path.join(dirPath, `insomnia-export.${Date.now()}`);
|
||||
|
||||
for (const workspace of workspacesWithoutMcp) {
|
||||
await exportWorkspaceData({
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import nodePath from 'node:path';
|
||||
|
||||
import { useCallback, useEffect, useId, useMemo, useState } from 'react';
|
||||
import { Button, Input, Text } from 'react-aria-components';
|
||||
import z from 'zod/v4';
|
||||
@@ -41,8 +39,8 @@ export const GGUF = ({
|
||||
});
|
||||
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||
|
||||
const userDataPath = nodePath.resolve(window.app.getPath('userData'));
|
||||
const llmsFolder = nodePath.resolve(userDataPath, LLMS_FOLDER_NAME);
|
||||
const userDataPath = window.path.resolve(window.app.getPath('userData'));
|
||||
const llmsFolder = window.path.resolve(userDataPath, LLMS_FOLDER_NAME);
|
||||
const [availableLLMs, setAvailableLLMs] = useState<string[]>([]);
|
||||
const [selectedModel, setSelectedModel] = useState<string>('');
|
||||
const refreshModelsDirectory = useCallback(() => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import nodePath from 'node:path';
|
||||
|
||||
import React, { type FC, useEffect, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
@@ -403,10 +401,9 @@ export const Plugins: FC = () => {
|
||||
)}
|
||||
>
|
||||
{plugin => {
|
||||
const link = nodePath.resolve(
|
||||
plugin.name.startsWith('insomnia-plugin-') ? PLUGIN_HUB_BASE : NPM_PACKAGE_BASE,
|
||||
plugin.name,
|
||||
);
|
||||
const link = plugin.name.startsWith('insomnia-plugin-')
|
||||
? PLUGIN_HUB_BASE
|
||||
: NPM_PACKAGE_BASE + '/' + plugin.name;
|
||||
|
||||
return (
|
||||
<GridListItem
|
||||
@@ -490,7 +487,7 @@ export const Plugins: FC = () => {
|
||||
className="text-(--color-surprise) underline"
|
||||
onPress={() =>
|
||||
window.shell.showItemInFolder(
|
||||
nodePath.resolve(process.env['INSOMNIA_DATA_PATH'] || window.app.getPath('userData'), 'plugins'),
|
||||
window.path.resolve(process.env['INSOMNIA_DATA_PATH'] || window.app.getPath('userData'), 'plugins'),
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { URL } from 'node:url';
|
||||
|
||||
import React, { type FC, Fragment, useMemo } from 'react';
|
||||
|
||||
import type { ResponseHeader } from '../../../models/response';
|
||||
|
||||
@@ -1,32 +1,18 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { PassThrough } from 'node:stream';
|
||||
|
||||
import { format } from 'date-fns';
|
||||
import type { SaveDialogOptions } from 'electron';
|
||||
import { extension as mimeExtension } from 'mime-types';
|
||||
import multiparty from 'multiparty';
|
||||
import React, { type FC, useCallback, useEffect, useState } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
|
||||
import type { Part } from '~/main/multipart-buffer-to-array';
|
||||
|
||||
import { getContentTypeFromHeaders, PREVIEW_MODE_FRIENDLY } from '../../../common/constants';
|
||||
import type { ResponseHeader } from '../../../models/response';
|
||||
import { Dropdown, DropdownItem, ItemContent } from '../base/dropdown';
|
||||
import { showModal } from '../modals/index';
|
||||
import { WrapperModal } from '../modals/wrapper-modal';
|
||||
import { ResponseHeadersViewer } from './response-headers-viewer';
|
||||
import { ResponseViewer } from './response-viewer';
|
||||
|
||||
interface Part {
|
||||
id: number;
|
||||
title: string;
|
||||
name: string;
|
||||
bytes: number;
|
||||
value: Buffer;
|
||||
filename: string | null;
|
||||
headers: ResponseHeader[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
download: (...args: any[]) => any;
|
||||
responseId: string;
|
||||
@@ -58,8 +44,11 @@ export const ResponseMultipartViewer: FC<Props> = ({
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
if (!bodyBuffer || !contentType) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parts = await multipartBufferToArray({ bodyBuffer, contentType });
|
||||
const parts = await window.main.multipartBufferToArray({ bodyBuffer, contentType });
|
||||
setParts(parts);
|
||||
setSelectedPart(parts[0]);
|
||||
} catch (err) {
|
||||
@@ -96,7 +85,7 @@ export const ResponseMultipartViewer: FC<Props> = ({
|
||||
const options: SaveDialogOptions = {
|
||||
title: 'Save as File',
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: path.join(dir, filename),
|
||||
defaultPath: window.path.join(dir, filename),
|
||||
filters: [
|
||||
// @ts-expect-error https://github.com/electron/electron/pull/29322
|
||||
{
|
||||
@@ -111,11 +100,13 @@ export const ResponseMultipartViewer: FC<Props> = ({
|
||||
}
|
||||
|
||||
// Remember last exported path
|
||||
window.localStorage.setItem('insomnia.lastExportPath', path.dirname(filename));
|
||||
window.localStorage.setItem('insomnia.lastExportPath', window.path.dirname(filename));
|
||||
|
||||
// Save the file
|
||||
try {
|
||||
await fs.promises.writeFile(filePath, selectedPart.value);
|
||||
await window.main.writeFile({
|
||||
path: filePath,
|
||||
content: selectedPart.value.toString('utf8'),
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('Failed to save multipart to file', err);
|
||||
}
|
||||
@@ -217,62 +208,3 @@ export const ResponseMultipartViewer: FC<Props> = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function multipartBufferToArray({
|
||||
bodyBuffer,
|
||||
contentType,
|
||||
}: {
|
||||
bodyBuffer: Buffer | null;
|
||||
contentType: string;
|
||||
}): Promise<Part[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const parts: Part[] = [];
|
||||
|
||||
if (!bodyBuffer) {
|
||||
return resolve(parts);
|
||||
}
|
||||
|
||||
const fakeReq = new PassThrough();
|
||||
// @ts-expect-error -- TSCONVERSION investigate `stream` types
|
||||
fakeReq.headers = {
|
||||
'content-type': contentType,
|
||||
};
|
||||
const form = new multiparty.Form();
|
||||
let id = 0;
|
||||
form.on('part', part => {
|
||||
const dataBuffers: any[] = [];
|
||||
part.on('data', data => {
|
||||
dataBuffers.push(data);
|
||||
});
|
||||
part.on('error', err => {
|
||||
reject(new Error(`Failed to parse part: ${err.message}`));
|
||||
});
|
||||
part.on('end', () => {
|
||||
const title = part.filename ? `${part.name} (${part.filename})` : part.name;
|
||||
parts.push({
|
||||
id,
|
||||
title,
|
||||
value: dataBuffers ? Buffer.concat(dataBuffers) : Buffer.from(''),
|
||||
name: part.name,
|
||||
filename: part.filename || null,
|
||||
bytes: part.byteCount,
|
||||
headers: Object.keys(part.headers).map(name => ({
|
||||
name,
|
||||
value: part.headers[name],
|
||||
})),
|
||||
});
|
||||
id += 1;
|
||||
});
|
||||
});
|
||||
form.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
form.on('close', () => {
|
||||
resolve(parts);
|
||||
});
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
form.parse(fakeReq);
|
||||
fakeReq.write(bodyBuffer);
|
||||
fakeReq.end();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import React, { type FC, useCallback, useRef } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
@@ -11,7 +9,6 @@ import type { SocketIOEvent } from '../../../main/network/socket-io';
|
||||
import type { WebSocketEvent, WebSocketMessageEvent } from '../../../main/network/websocket';
|
||||
import { useRequestLoaderData } from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
|
||||
import { useRequestMetaPatcher } from '../../hooks/use-request';
|
||||
import { showError } from '../modals';
|
||||
import { WebSocketPreviewModeDropdown } from './websocket-preview-dropdown';
|
||||
|
||||
interface Props<T> {
|
||||
@@ -42,20 +39,10 @@ export const MessageEventView: FC<Props<CurlMessageEvent | WebSocketMessageEvent
|
||||
if (canceled || !outputPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const to = fs.createWriteStream(outputPath);
|
||||
|
||||
to.on('error', err => {
|
||||
showError({
|
||||
title: 'Save Failed',
|
||||
message: 'Failed to save response body',
|
||||
error: err,
|
||||
});
|
||||
await window.main.writeFile({
|
||||
path: outputPath,
|
||||
content: raw,
|
||||
});
|
||||
|
||||
to.write(raw);
|
||||
|
||||
to.end();
|
||||
}, [raw]);
|
||||
|
||||
const handleCopyResponseToClipboard = useCallback(() => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import classnames from 'classnames';
|
||||
import React, { type FC, useEffect, useMemo, useState } from 'react';
|
||||
import { Button, Input, SearchField, Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
|
||||
@@ -206,18 +204,10 @@ const RealtimeActiveResponsePane: FC<RealtimeActiveResponsePaneProps & { readySt
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
const fn = async () => {
|
||||
try {
|
||||
await fs.promises.stat(response.timelinePath);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return setTimeline([]);
|
||||
}
|
||||
}
|
||||
|
||||
// allow to read the file as it is chosen by user
|
||||
const content = await window.main.secureReadFile({
|
||||
path: response.timelinePath,
|
||||
});
|
||||
|
||||
const timelineParsed = deserializeNDJSON(content);
|
||||
if (isMounted) {
|
||||
setTimeline(timelineParsed);
|
||||
|
||||
6
packages/insomnia/types/global.d.ts
vendored
6
packages/insomnia/types/global.d.ts
vendored
@@ -12,6 +12,12 @@ declare global {
|
||||
shell: Pick<Electron.Shell, 'showItemInFolder' | 'openPath'>;
|
||||
clipboard: Pick<Electron.Clipboard, 'readText' | 'writeText' | 'clear'>;
|
||||
webUtils: Pick<Electron.WebUtils, 'getPathForFile'>;
|
||||
path: {
|
||||
resolve: (...paths: string[]) => string;
|
||||
dirname: (p: string) => string;
|
||||
basename: (p: string) => string;
|
||||
join: (...paths: string[]) => string;
|
||||
};
|
||||
showAlert: (options?: Record<string, any>) => void;
|
||||
showWrapper: (options?: Record<string, any>) => void;
|
||||
showPrompt: (options?: Record<string, any>) => void;
|
||||
|
||||
@@ -70,7 +70,7 @@ export default defineConfig(({ mode }) => {
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
let totalWarnings = 0;
|
||||
function DetectNodeBuiltinImports() {
|
||||
const builtins = new Set(builtinModules);
|
||||
|
||||
@@ -85,7 +85,8 @@ function DetectNodeBuiltinImports() {
|
||||
// If the import target is a Node builtin module
|
||||
if (builtins.has(source) || builtins.has(source.replace('virtual:external:node:', ''))) {
|
||||
const file = path.relative(process.cwd(), importer);
|
||||
console.warn(`⚠️ File "${file}" imports Node builtin module "${source}"`);
|
||||
totalWarnings += 1;
|
||||
console.warn(`⚠️ ${totalWarnings} File "${file}" imports Node builtin module "${source}"`);
|
||||
}
|
||||
|
||||
return null; // Let Vite handle the actual resolution
|
||||
|
||||
Reference in New Issue
Block a user