mirror of
https://github.com/Kong/insomnia.git
synced 2026-08-04 03:42:20 -04:00
refactor(runtime): introduce explicit network runtime capabilities (#10037)
* refactor(runtime): introduce explicit network runtime capabilities Add a RuntimeCapabilities boundary for network operations and initialize renderer/node implementations from app, plugin, hidden-window, CLI, and test entrypoints. Migrate network request execution and HAR export paths to consume getRuntime().network instead of importing network adapters directly. * refactor: remove unused network adapter alias from Vitest and Vite config
This commit is contained in:
@@ -10,6 +10,8 @@ import { Confirm } from 'enquirer';
|
||||
import { pick } from 'es-toolkit';
|
||||
import { isDevelopment, JSON_ORDER_PREFIX, JSON_ORDER_SEPARATOR } from 'insomnia/src/common/constants';
|
||||
import { insomniaFetch } from 'insomnia/src/common/insomnia-fetch';
|
||||
import { initRuntime } from 'insomnia/src/common/runtime';
|
||||
import { nodeRuntime } from 'insomnia/src/common/runtime/runtime.node';
|
||||
import { getSendRequestCallbackMemDb } from 'insomnia/src/common/send-request';
|
||||
import { configureFetch } from 'insomnia-api';
|
||||
import type {
|
||||
@@ -50,6 +52,7 @@ import { generateDocumentation } from './scripts/docs';
|
||||
import { getAppDataDir, getDefaultProductName } from './util';
|
||||
|
||||
initServices(servicesNodeImpl);
|
||||
initRuntime(nodeRuntime);
|
||||
|
||||
export interface GlobalOptions {
|
||||
ci: boolean;
|
||||
|
||||
@@ -4,7 +4,6 @@ export default defineConfig({
|
||||
test: {
|
||||
hideSkippedTests: true,
|
||||
alias: {
|
||||
'~/network/network-adapter': new URL('../insomnia/src/network/network-adapter.node.ts', import.meta.url).pathname,
|
||||
'~/templating/render-adapter': new URL('../insomnia/src/templating/render-adapter.node.ts', import.meta.url)
|
||||
.pathname,
|
||||
'~/': new URL('../insomnia/src/', import.meta.url).pathname,
|
||||
|
||||
@@ -6,10 +6,13 @@ import { vi } from 'vitest';
|
||||
import { v4Mock } from '../insomnia-data/__mocks__/uuid';
|
||||
import { nodeLibcurlMock } from './src/__mocks__/@getinsomnia/node-libcurl';
|
||||
import { electronMock } from './src/__mocks__/electron';
|
||||
import { initRuntime } from './src/common/runtime';
|
||||
import { nodeRuntime } from './src/common/runtime/runtime.node';
|
||||
import { mainDatabase } from './src/main/database.main';
|
||||
|
||||
await initDatabase(mainDatabase, { inMemoryOnly: true }, true);
|
||||
await initServices(servicesNodeImpl);
|
||||
initRuntime(nodeRuntime);
|
||||
|
||||
vi.mock('electron', () => ({ default: electronMock }));
|
||||
|
||||
|
||||
@@ -2,15 +2,19 @@ import path from 'node:path';
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('~/network/network-adapter', () => ({
|
||||
getTimelinePath: () => Promise.resolve(''),
|
||||
appendToTimelineOnError: () => Promise.resolve(),
|
||||
appendTimelineLines: () => Promise.resolve(),
|
||||
getAuthHeader: () => Promise.resolve(),
|
||||
executeCurlRequest: () => Promise.resolve({}),
|
||||
runScript: () => Promise.resolve({}),
|
||||
applyRequestHooks: (request: any) => Promise.resolve(request),
|
||||
applyResponseHooks: (response: any) => Promise.resolve(response),
|
||||
vi.mock('~/common/runtime', () => ({
|
||||
getRuntime: () => ({
|
||||
network: {
|
||||
getTimelinePath: () => Promise.resolve(''),
|
||||
appendToTimelineOnError: () => Promise.resolve(),
|
||||
appendTimelineLines: () => Promise.resolve(),
|
||||
getAuthHeader: () => Promise.resolve(),
|
||||
executeCurlRequest: () => Promise.resolve({}),
|
||||
runScript: () => Promise.resolve({}),
|
||||
applyRequestHooks: (request: any) => Promise.resolve(request),
|
||||
applyResponseHooks: (response: any) => Promise.resolve(response),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
vi.mock('~/utils/crypt-adapter', () => ({
|
||||
decryptSecretValue: (value: any) => value,
|
||||
|
||||
19
packages/insomnia/src/common/runtime/index.ts
Normal file
19
packages/insomnia/src/common/runtime/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { RuntimeCapabilities } from './types';
|
||||
|
||||
let runtime: RuntimeCapabilities | null = null;
|
||||
|
||||
export function initRuntime(impl: RuntimeCapabilities) {
|
||||
if (runtime) {
|
||||
throw new Error('Runtime has already been initialized.');
|
||||
}
|
||||
runtime = impl;
|
||||
}
|
||||
|
||||
export function getRuntime(): RuntimeCapabilities {
|
||||
if (!runtime) {
|
||||
throw new Error('Runtime not initialized. Call initRuntime() first.');
|
||||
}
|
||||
return runtime;
|
||||
}
|
||||
|
||||
export type { NetworkRuntime, RuntimeCapabilities } from './types';
|
||||
6
packages/insomnia/src/common/runtime/runtime.node.ts
Normal file
6
packages/insomnia/src/common/runtime/runtime.node.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import * as networkAdapter from '../../network/network-adapter.node';
|
||||
import type { RuntimeCapabilities } from './types';
|
||||
|
||||
export const nodeRuntime = {
|
||||
network: networkAdapter,
|
||||
} satisfies RuntimeCapabilities;
|
||||
6
packages/insomnia/src/common/runtime/runtime.renderer.ts
Normal file
6
packages/insomnia/src/common/runtime/runtime.renderer.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import * as networkAdapter from '../../network/network-adapter.renderer';
|
||||
import type { RuntimeCapabilities } from './types';
|
||||
|
||||
export const rendererRuntime = {
|
||||
network: networkAdapter,
|
||||
} satisfies RuntimeCapabilities;
|
||||
38
packages/insomnia/src/common/runtime/types.ts
Normal file
38
packages/insomnia/src/common/runtime/types.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Cookie, RequestHeader } from 'insomnia-data';
|
||||
|
||||
import type { RequestContext } from '../../../../insomnia-scripting-environment/src/objects';
|
||||
import type { CurlRequestOptions, CurlRequestOutput, ResponsePatch } from '../../main/network/libcurl-promise';
|
||||
import type { RenderedRequest } from '../../templating/types';
|
||||
|
||||
interface CurlRequestErrorOutput {
|
||||
statusMessage: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface NetworkRuntime {
|
||||
getTimelinePath: (responseId: string) => Promise<string>;
|
||||
appendToTimelineOnError: (timelinePath: string, data: string) => Promise<void>;
|
||||
appendTimelineLines: (timelinePath: string, logs: string[]) => Promise<void>;
|
||||
getAuthHeader: (request: RenderedRequest, url: string) => Promise<RequestHeader | undefined>;
|
||||
executeCurlRequest: (options: CurlRequestOptions) => Promise<CurlRequestOutput | CurlRequestErrorOutput>;
|
||||
extractCookies: (options: {
|
||||
setCookieStrings: string[];
|
||||
currentUrl: string;
|
||||
cookieJar: { cookies: Cookie[] };
|
||||
settingStoreCookies: boolean;
|
||||
}) => Promise<{ cookies: Cookie[]; rejectedCookies: string[]; totalSetCookies: number }>;
|
||||
runScript: (options: { script: string; context: RequestContext }) => Promise<RequestContext | { error: string }>;
|
||||
applyRequestHooks: (
|
||||
renderedRequest: RenderedRequest,
|
||||
renderedContext: Record<string, any>,
|
||||
) => Promise<RenderedRequest>;
|
||||
applyResponseHooks: (
|
||||
response: ResponsePatch,
|
||||
renderedRequest: RenderedRequest,
|
||||
renderedContext: Record<string, any>,
|
||||
) => Promise<ResponsePatch>;
|
||||
}
|
||||
|
||||
export interface RuntimeCapabilities {
|
||||
network: NetworkRuntime;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { hydrateRoot } from 'react-dom/client';
|
||||
import { HydratedRouter } from 'react-router/dom';
|
||||
|
||||
import { insomniaFetch } from '~/common/insomnia-fetch';
|
||||
import { initRuntime } from '~/common/runtime';
|
||||
import { rendererRuntime } from '~/common/runtime/runtime.renderer';
|
||||
import { database as clientDatabase } from '~/ui/database.client';
|
||||
import { clearOAuthWindowSessionId } from '~/ui/spawn-oauth-window';
|
||||
|
||||
@@ -36,6 +38,7 @@ if (!window._dataServices) {
|
||||
initServices(window._dataServices);
|
||||
// Remove the global services reference after initialization to improve security by preventing unintended access from the global scope.
|
||||
delete window._dataServices;
|
||||
initRuntime(rendererRuntime);
|
||||
|
||||
configureFetch(options => insomniaFetch({ ...options, onDeepLink: (uri: string) => window.main.openDeepLink(uri) }));
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import { SENTRY_OPTIONS } from 'insomnia/src/common/sentry';
|
||||
import { initServices } from 'insomnia-data';
|
||||
|
||||
import type { RequestContext } from '../../insomnia-scripting-environment/src/objects';
|
||||
import { initRuntime } from './common/runtime';
|
||||
import { rendererRuntime } from './common/runtime/runtime.renderer';
|
||||
import { runScript } from './scripting/run-script';
|
||||
import { type ScriptSecurityPolicy } from './scripting/sandbox';
|
||||
|
||||
@@ -27,6 +29,7 @@ if (!window._dataServices) {
|
||||
initServices(window._dataServices);
|
||||
// Remove the global services reference after initialization to improve security by preventing unintended access from the global scope.
|
||||
delete window._dataServices;
|
||||
initRuntime(rendererRuntime);
|
||||
|
||||
window.bridge.onmessage(
|
||||
async (data: { script: string; context: RequestContext }, callback: ({ error }: { error: string }) => void) => {
|
||||
|
||||
@@ -13,6 +13,8 @@ import { isMac } from 'insomnia-data/common';
|
||||
import { servicesNodeImpl } from 'insomnia-data/node';
|
||||
|
||||
import { insomniaFetch } from '~/common/insomnia-fetch';
|
||||
import { initRuntime } from '~/common/runtime';
|
||||
import { nodeRuntime } from '~/common/runtime/runtime.node';
|
||||
import { mainDatabase } from '~/main/database.main';
|
||||
import { initElectronStorage } from '~/main/electron-storage';
|
||||
import { runGitCredentialsMigration } from '~/main/git/migrations';
|
||||
@@ -127,6 +129,7 @@ app.on('ready', async () => {
|
||||
await initDatabase(mainDatabase);
|
||||
// Initialize services for main process
|
||||
initServices(servicesNodeImpl);
|
||||
initRuntime(nodeRuntime);
|
||||
await _createModelInstances();
|
||||
// backup needs the channel from settings which needs the database
|
||||
await backupIfNewerVersionAvailable();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { initDatabase, initServices } from 'insomnia-data';
|
||||
|
||||
import { initRuntime } from './common/runtime';
|
||||
import { rendererRuntime } from './common/runtime/runtime.renderer';
|
||||
import { pluginWindowDatabase } from './main/database.plugin-window';
|
||||
import { invokePluginMethod } from './plugins/invoke-method';
|
||||
import { servicesProxy } from './ui/renderer-services-proxy';
|
||||
@@ -28,6 +30,7 @@ ipcRenderer.on('plugins.invoke', async (_event, { id, method, args }: PluginInvo
|
||||
try {
|
||||
await initDatabase(pluginWindowDatabase);
|
||||
initServices(servicesProxy);
|
||||
initRuntime(rendererRuntime);
|
||||
ipcRenderer.send('plugins.windowReady');
|
||||
} catch (err) {
|
||||
console.error('[plugin-window] Initialization failed:', err);
|
||||
|
||||
@@ -3,9 +3,9 @@ import type { BaseModel, Cookie, Environment, Request, RequestGroup, Response, W
|
||||
import { models, services } from 'insomnia-data';
|
||||
import { Cookie as ToughCookie } from 'tough-cookie';
|
||||
|
||||
import { getRuntime } from '~/common/runtime';
|
||||
import { getAuthHeader } from '~/main/network/get-auth-header';
|
||||
import { secureReadFile } from '~/main/secure-read-file';
|
||||
import { applyRequestHooks } from '~/network/network-adapter';
|
||||
|
||||
import { getAppVersion } from '../common/constants';
|
||||
import { database } from '../common/database';
|
||||
@@ -245,7 +245,7 @@ export async function exportHarRequest(requestId: string, environmentOrWorkspace
|
||||
export async function exportHarWithRequest(request: Request, environmentId?: string, addContentLength = false) {
|
||||
try {
|
||||
const renderResult = await getRenderedRequestAndContext({ request, environment: environmentId });
|
||||
const renderedRequest = await applyRequestHooks(renderResult.request, renderResult.context);
|
||||
const renderedRequest = await getRuntime().network.applyRequestHooks(renderResult.request, renderResult.context);
|
||||
parseGraphQLReqeustBody(renderedRequest);
|
||||
return exportHarWithRenderedRequest(renderedRequest, addContentLength);
|
||||
} catch (err) {
|
||||
|
||||
@@ -42,20 +42,6 @@ describe('getAuthQueryParams', () => {
|
||||
});
|
||||
});
|
||||
describe('sendCurlAndWriteTimeline()', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('window', {
|
||||
main: {
|
||||
timeline: {
|
||||
getPath: (responseId: string) => Promise.resolve(`/tmp/${responseId}.timeline`),
|
||||
appendToFile: vi.fn().mockResolvedValue(null),
|
||||
},
|
||||
getAuthHeader,
|
||||
curlRequest,
|
||||
cancelCurlRequest: vi.fn(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sends a generic request', async () => {
|
||||
const workspace = await services.workspace.create();
|
||||
const settings = await services.settings.getOrCreate();
|
||||
|
||||
@@ -77,7 +77,7 @@ export async function applyRequestHooks(
|
||||
renderedContext: Record<string, any>,
|
||||
): Promise<RenderedRequest> {
|
||||
const newRenderedRequest = applyDefaultHeaders(renderedRequest, renderedContext['DEFAULT_HEADERS']);
|
||||
const pluginIndex = require('../plugins/index');
|
||||
const pluginIndex = await import('../plugins/index');
|
||||
for (const { plugin, hook } of await pluginIndex.getRequestHooks()) {
|
||||
const context = {
|
||||
...(pluginApp.init() as Record<string, any>),
|
||||
@@ -104,7 +104,7 @@ export async function applyResponseHooks(
|
||||
): Promise<ResponsePatch> {
|
||||
const newResponse = clone(response);
|
||||
const newRequest = clone(renderedRequest);
|
||||
const pluginIndex = require('../plugins/index');
|
||||
const pluginIndex = await import('../plugins/index');
|
||||
for (const { plugin, hook } of await pluginIndex.getResponseHooks()) {
|
||||
const context = {
|
||||
...(pluginApp.init() as Record<string, any>),
|
||||
|
||||
@@ -24,17 +24,7 @@ import { EnvironmentType, models, services } from 'insomnia-data';
|
||||
import { invariant, serializeNDJSON } from 'insomnia-data/common';
|
||||
import orderedJSON from 'json-order';
|
||||
|
||||
import {
|
||||
appendTimelineLines,
|
||||
appendToTimelineOnError,
|
||||
applyRequestHooks,
|
||||
applyResponseHooks,
|
||||
executeCurlRequest,
|
||||
extractCookies,
|
||||
getAuthHeader,
|
||||
getTimelinePath,
|
||||
runScript,
|
||||
} from '~/network/network-adapter';
|
||||
import { getRuntime } from '~/common/runtime';
|
||||
import { getKVPairFromData } from '~/utils/environment-utils';
|
||||
|
||||
import type { ExecutionOption, RequestContext } from '../../../insomnia-scripting-environment/src/objects';
|
||||
@@ -150,7 +140,7 @@ export const fetchRequestGroupData = async (requestGroupId: string) => {
|
||||
const clientCertificates = await services.clientCertificate.findByParentId(workspaceId);
|
||||
const caCert = await services.caCertificate.getByParentId(workspaceId);
|
||||
const responseId = generateId('res');
|
||||
const timelinePath = await getTimelinePath(responseId);
|
||||
const timelinePath = await getRuntime().network.getTimelinePath(responseId);
|
||||
return { environment, settings, clientCertificates, caCert, activeEnvironmentId, timelinePath, responseId };
|
||||
};
|
||||
|
||||
@@ -213,7 +203,7 @@ export const fetchRequestData = async (
|
||||
const caCert = await services.caCertificate.getByParentId(workspaceId);
|
||||
|
||||
const responseId = generateId('res');
|
||||
const timelinePath = await getTimelinePath(responseId);
|
||||
const timelinePath = await getRuntime().network.getTimelinePath(responseId);
|
||||
|
||||
return {
|
||||
request,
|
||||
@@ -252,7 +242,7 @@ export const fetchMcpRequestData = async (mcpRequestId: string) => {
|
||||
invariant(settings, 'failed to create settings');
|
||||
|
||||
const responseId = generateId('res');
|
||||
const timelinePath = await getTimelinePath(responseId);
|
||||
const timelinePath = await getRuntime().network.getTimelinePath(responseId);
|
||||
|
||||
return {
|
||||
environment,
|
||||
@@ -514,7 +504,7 @@ const tryToExecuteScript = async (context: RequestAndContextAndOptionalResponse)
|
||||
}
|
||||
|
||||
try {
|
||||
const output = await runScript({
|
||||
const output = await getRuntime().network.runScript({
|
||||
script,
|
||||
context: {
|
||||
request,
|
||||
@@ -647,7 +637,7 @@ const tryToExecuteScript = async (context: RequestAndContextAndOptionalResponse)
|
||||
parentFolders: output.parentFolders,
|
||||
};
|
||||
} catch (err) {
|
||||
await appendToTimelineOnError(
|
||||
await getRuntime().network.appendToTimelineOnError(
|
||||
timelinePath,
|
||||
serializeNDJSON([{ value: err.message, name: 'Text', timestamp: Date.now() }]),
|
||||
);
|
||||
@@ -798,7 +788,7 @@ export const tryToTransformRequestWithPlugins = async (renderResult: {
|
||||
}) => {
|
||||
const { request, context } = renderResult;
|
||||
try {
|
||||
return await applyRequestHooks(request, context);
|
||||
return await getRuntime().network.applyRequestHooks(request, context);
|
||||
} catch {
|
||||
throw new Error(`Failed to transform request with plugins: ${request._id}`);
|
||||
}
|
||||
@@ -858,7 +848,7 @@ export async function sendCurlAndWriteTimeline(
|
||||
if (!renderedRequest.settingSendCookies) {
|
||||
timeline.push({ value: 'Disable cookie sending due to user setting', name: 'Text', timestamp: Date.now() });
|
||||
}
|
||||
const authHeader = await getAuthHeader(renderedRequest, finalUrl);
|
||||
const authHeader = await getRuntime().network.getAuthHeader(renderedRequest, finalUrl);
|
||||
const requestOptions = {
|
||||
requestId,
|
||||
req: renderedRequest,
|
||||
@@ -870,7 +860,7 @@ export async function sendCurlAndWriteTimeline(
|
||||
authHeader,
|
||||
};
|
||||
|
||||
const output = await executeCurlRequest(requestOptions);
|
||||
const output = await getRuntime().network.executeCurlRequest(requestOptions);
|
||||
|
||||
if ('error' in output) {
|
||||
if (runtime) {
|
||||
@@ -893,7 +883,7 @@ export async function sendCurlAndWriteTimeline(
|
||||
// todo: move to main process
|
||||
debugTimeline.forEach(entry => timeline.push(entry));
|
||||
// transform output
|
||||
const { cookies, rejectedCookies, totalSetCookies } = await extractCookies({
|
||||
const { cookies, rejectedCookies, totalSetCookies } = await getRuntime().network.extractCookies({
|
||||
setCookieStrings: headerResults.flatMap(({ headers }: any) => getSetCookiesFromResponseHeaders(headers)),
|
||||
currentUrl: getCurrentUrl({ headerResults, finalUrl }),
|
||||
cookieJar: renderedRequest.cookieJar,
|
||||
@@ -951,7 +941,7 @@ export const responseTransform = async (
|
||||
}
|
||||
console.log(`[network] Response succeeded req=${patch.parentId} status=${response.statusCode || '?'}`);
|
||||
try {
|
||||
return await applyResponseHooks(response, renderedRequest, context);
|
||||
return await getRuntime().network.applyResponseHooks(response, renderedRequest, context);
|
||||
} catch (err) {
|
||||
console.log('[plugin] Response hook failed', err, response);
|
||||
return {
|
||||
@@ -1025,5 +1015,5 @@ export const getCurrentUrl = ({ headerResults, finalUrl }: { headerResults: any;
|
||||
};
|
||||
|
||||
export const defaultSendActionRuntime: SendActionRuntime = {
|
||||
appendTimeline: appendTimelineLines,
|
||||
appendTimeline: (timelinePath, logs) => getRuntime().network.appendTimelineLines(timelinePath, logs),
|
||||
};
|
||||
|
||||
@@ -2,15 +2,19 @@ import { exportRequestsHAR, exportWorkspacesHAR } from 'insomnia/src/main/har';
|
||||
import { database as db, services } from 'insomnia-data';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('~/network/network-adapter', () => ({
|
||||
getTimelinePath: () => Promise.resolve(''),
|
||||
appendToTimelineOnError: () => Promise.resolve(),
|
||||
appendTimelineLines: () => Promise.resolve(),
|
||||
getAuthHeader: () => Promise.resolve(),
|
||||
executeCurlRequest: () => Promise.resolve({}),
|
||||
runScript: () => Promise.resolve({}),
|
||||
applyRequestHooks: (request: any) => Promise.resolve(request),
|
||||
applyResponseHooks: (response: any) => Promise.resolve(response),
|
||||
vi.mock('~/common/runtime', () => ({
|
||||
getRuntime: () => ({
|
||||
network: {
|
||||
getTimelinePath: () => Promise.resolve(''),
|
||||
appendToTimelineOnError: () => Promise.resolve(),
|
||||
appendTimelineLines: () => Promise.resolve(),
|
||||
getAuthHeader: () => Promise.resolve(),
|
||||
executeCurlRequest: () => Promise.resolve({}),
|
||||
runScript: () => Promise.resolve({}),
|
||||
applyRequestHooks: (request: any) => Promise.resolve(request),
|
||||
applyResponseHooks: (response: any) => Promise.resolve(response),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
// @vitest-environment jsdom
|
||||
|
||||
@@ -55,7 +55,6 @@ export default defineConfig(({ mode }) => {
|
||||
alias: {
|
||||
// Short-circuit the adapter wrappers to the renderer implementation directly.
|
||||
// These must appear before the '~' catch-all so the specific path wins.
|
||||
'~/network/network-adapter': path.resolve(__dirname, './src/network/network-adapter.renderer'),
|
||||
'~/templating/render-adapter': path.resolve(__dirname, './src/templating/render-adapter.renderer'),
|
||||
'~/utils/crypt-adapter': path.resolve(__dirname, './src/utils/crypt-adapter.renderer'),
|
||||
'~': path.resolve(__dirname, './src'),
|
||||
|
||||
@@ -14,7 +14,6 @@ export default defineConfig({
|
||||
},
|
||||
exclude: ['src/routes/**.*.tsx', '.react-router', 'node_modules'],
|
||||
alias: {
|
||||
'~/network/network-adapter': path.resolve(__dirname, './src/network/network-adapter.renderer'),
|
||||
'~/templating/render-adapter': path.resolve(__dirname, './src/templating/render-adapter.node'),
|
||||
'~': path.resolve(__dirname, './src'),
|
||||
'electron/main': 'electron',
|
||||
|
||||
Reference in New Issue
Block a user