mirror of
https://github.com/Kong/insomnia.git
synced 2026-07-30 17:26:28 -04:00
update
This commit is contained in:
@@ -2,7 +2,7 @@ import path from 'node:path';
|
||||
|
||||
import type { UtilityProcess } from 'electron';
|
||||
import { BrowserWindow, MessageChannelMain, utilityProcess } from 'electron';
|
||||
import type { ChangeListener } from 'insomnia-data';
|
||||
import type { ChangeBufferEvent, ChangeListener } from 'insomnia-data';
|
||||
|
||||
import { isDevelopment } from '../common/constants';
|
||||
import { PortRpc } from './port-rpc';
|
||||
@@ -10,11 +10,16 @@ import { PortRpc } from './port-rpc';
|
||||
let child: UtilityProcess | null = null;
|
||||
|
||||
const mainProcessChangeListeners: ChangeListener[] = [];
|
||||
let deepLinkHandler: ((uri: string) => void) | null = null;
|
||||
|
||||
export function registerMainProcessChangeListener(listener: ChangeListener): void {
|
||||
mainProcessChangeListeners.push(listener);
|
||||
}
|
||||
|
||||
export function registerDeepLinkHandler(handler: (uri: string) => void): void {
|
||||
deepLinkHandler = handler;
|
||||
}
|
||||
|
||||
function getDataProcessPath(): string {
|
||||
return path.join(__dirname, 'entry.data.min.js');
|
||||
}
|
||||
@@ -76,7 +81,7 @@ export async function spawnDataProcess(dbPath: string): Promise<void> {
|
||||
mainRpc.attach(
|
||||
m => port2.postMessage(m),
|
||||
h => {
|
||||
port2.on('message', (e: Electron.MessageEvent) => h(e.data as any));
|
||||
port2.on('message', (e: Electron.MessageEvent) => h(e.data));
|
||||
port2.start();
|
||||
},
|
||||
);
|
||||
@@ -88,7 +93,9 @@ export async function spawnDataProcess(dbPath: string): Promise<void> {
|
||||
BrowserWindow.getAllWindows().forEach(w => {
|
||||
w.webContents.send('db.changes', msg.changes);
|
||||
});
|
||||
mainProcessChangeListeners.forEach(listener => listener(msg.changes as any));
|
||||
mainProcessChangeListeners.forEach(listener => listener(msg.changes as ChangeBufferEvent[]));
|
||||
} else if (msg.type === 'deep-link' && deepLinkHandler) {
|
||||
deepLinkHandler(msg.uri as string);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
88
packages/insomnia/src/data-process/port-rpc.test.ts
Normal file
88
packages/insomnia/src/data-process/port-rpc.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { PortRpc } from './port-rpc';
|
||||
|
||||
function createMockTransport() {
|
||||
let handler: ((data: any) => void) | null = null;
|
||||
const sent: unknown[] = [];
|
||||
return {
|
||||
send: (msg: unknown) => sent.push(msg),
|
||||
onMessage: (h: (data: any) => void) => { handler = h; },
|
||||
simulateResponse: (data: any) => handler?.(data),
|
||||
sent,
|
||||
};
|
||||
}
|
||||
|
||||
describe('PortRpc', () => {
|
||||
it('invoke rejects when not attached', async () => {
|
||||
const rpc = new PortRpc();
|
||||
await expect(rpc.invoke('database', 'find')).rejects.toThrow('data port not available');
|
||||
});
|
||||
|
||||
it('invoke sends a message and resolves on ok response', async () => {
|
||||
const rpc = new PortRpc();
|
||||
const transport = createMockTransport();
|
||||
rpc.attach(transport.send, transport.onMessage);
|
||||
|
||||
const promise = rpc.invoke('database', 'find', { type: 'Request' });
|
||||
|
||||
expect(transport.sent).toHaveLength(1);
|
||||
const msg = transport.sent[0] as any;
|
||||
expect(msg.type).toBe('invoke');
|
||||
expect(msg.namespace).toBe('database');
|
||||
expect(msg.method).toBe('find');
|
||||
expect(msg.args).toEqual([{ type: 'Request' }]);
|
||||
|
||||
transport.simulateResponse({ id: msg.id, ok: true, result: [{ _id: '1' }] });
|
||||
await expect(promise).resolves.toEqual([{ _id: '1' }]);
|
||||
});
|
||||
|
||||
it('invoke rejects on error response with deserialized error', async () => {
|
||||
const rpc = new PortRpc();
|
||||
const transport = createMockTransport();
|
||||
rpc.attach(transport.send, transport.onMessage);
|
||||
|
||||
const promise = rpc.invoke('services', 'request.getById', 'req_1');
|
||||
|
||||
const msg = transport.sent[0] as any;
|
||||
transport.simulateResponse({
|
||||
id: msg.id,
|
||||
ok: false,
|
||||
error: { name: 'TypeError', message: 'not found', stack: '' },
|
||||
});
|
||||
|
||||
await expect(promise).rejects.toThrow('not found');
|
||||
});
|
||||
|
||||
it('invalidate rejects all pending requests', async () => {
|
||||
const rpc = new PortRpc();
|
||||
const transport = createMockTransport();
|
||||
rpc.attach(transport.send, transport.onMessage);
|
||||
|
||||
const p1 = rpc.invoke('database', 'find');
|
||||
const p2 = rpc.invoke('database', 'count');
|
||||
|
||||
rpc.invalidate('restart');
|
||||
|
||||
await expect(p1).rejects.toThrow('restart');
|
||||
await expect(p2).rejects.toThrow('restart');
|
||||
});
|
||||
|
||||
it('invoke rejects after invalidate', async () => {
|
||||
const rpc = new PortRpc();
|
||||
const transport = createMockTransport();
|
||||
rpc.attach(transport.send, transport.onMessage);
|
||||
rpc.invalidate('gone');
|
||||
|
||||
await expect(rpc.invoke('database', 'find')).rejects.toThrow('data port not available');
|
||||
});
|
||||
|
||||
it('ignores responses for unknown request ids', () => {
|
||||
const rpc = new PortRpc();
|
||||
const transport = createMockTransport();
|
||||
rpc.attach(transport.send, transport.onMessage);
|
||||
|
||||
// Should not throw
|
||||
transport.simulateResponse({ id: 'unknown-id', ok: true, result: null });
|
||||
});
|
||||
});
|
||||
36
packages/insomnia/src/data-process/serialization.test.ts
Normal file
36
packages/insomnia/src/data-process/serialization.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { deserializeError, serializeError } from './serialization';
|
||||
|
||||
describe('serializeError', () => {
|
||||
it('serializes an Error instance', () => {
|
||||
const err = new TypeError('bad input');
|
||||
const s = serializeError(err);
|
||||
expect(s.name).toBe('TypeError');
|
||||
expect(s.message).toBe('bad input');
|
||||
expect(s.stack).toContain('bad input');
|
||||
});
|
||||
|
||||
it('serializes a non-Error value', () => {
|
||||
const s = serializeError('string failure');
|
||||
expect(s).toEqual({ name: 'Error', message: 'string failure', stack: '' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('deserializeError', () => {
|
||||
it('reconstructs an Error with name, message, and stack', () => {
|
||||
const err = deserializeError({ name: 'RangeError', message: 'out of bounds', stack: 'fake stack' });
|
||||
expect(err).toBeInstanceOf(Error);
|
||||
expect(err.name).toBe('RangeError');
|
||||
expect(err.message).toBe('out of bounds');
|
||||
expect(err.stack).toBe('fake stack');
|
||||
});
|
||||
|
||||
it('roundtrips through serialize → deserialize', () => {
|
||||
const original = new Error('roundtrip');
|
||||
const restored = deserializeError(serializeError(original));
|
||||
expect(restored.name).toBe(original.name);
|
||||
expect(restored.message).toBe(original.message);
|
||||
expect(restored.stack).toBe(original.stack);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
import { net } from 'electron/utility';
|
||||
import { configureFetch } from 'insomnia-api';
|
||||
import { initDatabase, initServices } from 'insomnia-data';
|
||||
import { createNedbDatabase, flushChangesImpl, servicesNodeImpl } from 'insomnia-data/node';
|
||||
|
||||
import { configureV3ClientDefaults } from './common/configure-v3-client';
|
||||
import { setFetchImplementation } from './common/insomnia-fetch';
|
||||
import { insomniaFetch, setFetchImplementation } from './common/insomnia-fetch';
|
||||
import { startDataProcessServer } from './data-process/server';
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
@@ -35,9 +36,12 @@ process.parentPort.once('message', async (event: Electron.MessageEvent) => {
|
||||
}));
|
||||
|
||||
await initDatabase(dataProcessDatabase);
|
||||
configureFetch(options => insomniaFetch({
|
||||
...options,
|
||||
onDeepLink: (uri: string) => process.parentPort.postMessage({ type: 'deep-link', uri }),
|
||||
}));
|
||||
configureV3ClientDefaults();
|
||||
// net.fetch picks up the proxy + OS certs like the renderer; node fetch does neither.
|
||||
// only works post-ready, which is fine — nothing calls this earlier. 'omit' = no cookies, same as before.
|
||||
setFetchImplementation((input, init) =>
|
||||
net.fetch(input, { ...init, credentials: 'omit', bypassCustomProtocolHandlers: true }),
|
||||
);
|
||||
|
||||
@@ -60,18 +60,14 @@ const bridge: HiddenBrowserWindowToMainBridgeAPI = {
|
||||
resetAsyncTasks,
|
||||
};
|
||||
|
||||
if (process.contextIsolated) {
|
||||
contextBridge.exposeInMainWorld('bridge', bridge);
|
||||
contextBridge.exposeInMainWorld('Promise', ProxiedPromise);
|
||||
} else {
|
||||
window.bridge = bridge;
|
||||
window.Promise = ProxiedPromise;
|
||||
}
|
||||
|
||||
const rpc = attachDataPortRpc('hidden-window-preload');
|
||||
|
||||
if (process.contextIsolated) {
|
||||
contextBridge.exposeInMainWorld('bridge', bridge);
|
||||
contextBridge.exposeInMainWorld('Promise', ProxiedPromise);
|
||||
contextBridge.exposeInMainWorld('invokeDataPort', rpc);
|
||||
} else {
|
||||
window.bridge = bridge;
|
||||
window.Promise = ProxiedPromise;
|
||||
window.invokeDataPort = rpc;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { models, services } from 'insomnia-data';
|
||||
import { isMac } from 'insomnia-data/common';
|
||||
|
||||
import { insomniaFetch, setFetchImplementation } from '~/common/insomnia-fetch';
|
||||
import { issuePort, mainRpc, registerMainProcessChangeListener, spawnDataProcess } from '~/data-process/data-process-manager';
|
||||
import { issuePort, mainRpc, registerDeepLinkHandler, registerMainProcessChangeListener, spawnDataProcess } from '~/data-process/data-process-manager';
|
||||
import { initDataBridge } from '~/data-process/init-data-bridge';
|
||||
import { initElectronStorage } from '~/main/electron-storage';
|
||||
import { runGitCredentialsMigration } from '~/main/git/migrations';
|
||||
@@ -142,6 +142,7 @@ app.on('ready', async () => {
|
||||
if (window) issuePort(window);
|
||||
});
|
||||
await spawnDataProcess(dataPath);
|
||||
registerDeepLinkHandler(uri => openDeepLinkUrl(uri));
|
||||
await initDataBridge(mainRpc.invoke, {
|
||||
database: { init: async () => {}, onChange: listener => registerMainProcessChangeListener(listener) },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user