@dimina-kit/devtools 0.4.0-dev.20260729062524 → 0.4.0-dev.20260801163345
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/app/app.js +7 -0
- package/dist/main/app/bootstrap.d.ts +8 -5
- package/dist/main/app/bootstrap.js +15 -15
- package/dist/main/index.bundle.js +2207 -5152
- package/dist/main/ipc/app-lifecycle.d.ts +1 -31
- package/dist/main/ipc/app-lifecycle.js +1 -53
- package/dist/main/ipc/bridge-router.d.ts +19 -152
- package/dist/main/ipc/bridge-router.js +28 -2186
- package/dist/main/ipc/page-scroll.d.ts +1 -13
- package/dist/main/ipc/page-scroll.js +1 -16
- package/dist/main/ipc/session-listener-bag.d.ts +1 -40
- package/dist/main/ipc/session-listener-bag.js +1 -39
- package/dist/main/services/console-forward/index.d.ts +1 -60
- package/dist/main/services/console-forward/index.js +1 -225
- package/dist/main/services/console-forward/internal-log.d.ts +1 -10
- package/dist/main/services/console-forward/internal-log.js +1 -22
- package/dist/main/services/diagnostics/index.d.ts +1 -58
- package/dist/main/services/diagnostics/index.js +1 -60
- package/dist/main/services/dimina-resource-server.d.ts +1 -6
- package/dist/main/services/dimina-resource-server.js +1 -98
- package/dist/main/services/mcp/target-manager.js +5 -1
- package/dist/main/services/network-forward/index.d.ts +15 -3
- package/dist/main/services/network-forward/index.js +30 -4
- package/dist/main/services/network-forward/websocket.d.ts +36 -0
- package/dist/main/services/network-forward/websocket.js +120 -0
- package/dist/main/services/service-host-pool/pool.d.ts +1 -196
- package/dist/main/services/service-host-pool/pool.js +1 -363
- package/dist/main/services/simulator/custom-apis.d.ts +1 -42
- package/dist/main/services/simulator/custom-apis.js +1 -46
- package/dist/main/services/simulator/referer.d.ts +1 -9
- package/dist/main/services/simulator/referer.js +1 -19
- package/dist/main/services/simulator-storage/service-storage-ops.d.ts +1 -39
- package/dist/main/services/simulator-storage/service-storage-ops.js +1 -66
- package/dist/main/services/simulator-temp-files/disk.d.ts +1 -54
- package/dist/main/services/simulator-temp-files/disk.js +1 -237
- package/dist/main/services/simulator-temp-files/fs-channels.d.ts +1 -68
- package/dist/main/services/simulator-temp-files/fs-channels.js +1 -107
- package/dist/main/services/simulator-temp-files/index.d.ts +1 -32
- package/dist/main/services/simulator-temp-files/index.js +1 -181
- package/dist/main/services/simulator-temp-files/request-handler.d.ts +1 -29
- package/dist/main/services/simulator-temp-files/request-handler.js +1 -177
- package/dist/main/services/simulator-temp-files/resolver.d.ts +1 -21
- package/dist/main/services/simulator-temp-files/resolver.js +1 -21
- package/dist/main/services/simulator-temp-files/store.d.ts +1 -13
- package/dist/main/services/simulator-temp-files/store.js +1 -23
- package/dist/main/services/views/miniapp-partition.d.ts +1 -89
- package/dist/main/services/views/miniapp-partition.js +1 -158
- package/dist/main/services/views/simulator-session-policy.d.ts +1 -27
- package/dist/main/services/views/simulator-session-policy.js +1 -98
- package/dist/main/services/workbench-context.d.ts +4 -1
- package/dist/main/services/workbench-context.js +17 -0
- package/dist/main/utils/ipc-registry.d.ts +21 -3
- package/dist/main/utils/ipc-registry.js +26 -23
- package/dist/main/utils/paths.d.ts +14 -0
- package/dist/main/utils/paths.js +14 -0
- package/dist/main/windows/service-host-window/create.d.ts +1 -92
- package/dist/main/windows/service-host-window/create.js +1 -154
- package/dist/native-host/common/common.js +154 -58
- package/dist/native-host/container/pageFrame.css +1 -1
- package/dist/native-host/render/render.js +6611 -3312
- package/dist/native-host/service/service.js +2 -2
- package/dist/preload/index.d.ts +2 -0
- package/dist/preload/index.js +1 -0
- package/dist/preload/runtime/clipboard.d.ts +6 -0
- package/dist/preload/runtime/clipboard.js +12 -0
- package/dist/preload/runtime/native-host.d.ts +2 -0
- package/dist/preload/runtime/native-host.js +5 -10
- package/dist/preload/runtime/node-bindings.d.ts +18 -0
- package/dist/preload/runtime/node-bindings.js +44 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +1195 -897
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/simulator.cjs +149 -60
- package/dist/preload/windows/simulator.cjs.map +4 -4
- package/dist/preload/windows/simulator.js +149 -60
- package/dist/render-host/pageFrame.html +3 -3
- package/dist/shared/bridge-channels.d.ts +1 -419
- package/dist/shared/bridge-channels.js +1 -89
- package/dist/shared/dmb-resource-url.d.ts +2 -0
- package/dist/shared/dmb-resource-url.js +2 -0
- package/dist/shared/ipc-channels.d.ts +2 -32
- package/dist/shared/ipc-channels.js +2 -5
- package/dist/shared/node-bindings.d.ts +2 -0
- package/dist/shared/node-bindings.js +2 -0
- package/dist/shared/request-core.d.ts +1 -83
- package/dist/shared/request-core.js +1 -167
- package/dist/simulator/assets/device-shell-Cw7c-IkG.js +2 -0
- package/dist/simulator/assets/{device-shell-BHz1v3Yo.css → device-shell-DbC5ccs8.css} +1 -1
- package/dist/simulator/assets/{jsx-runtime-hBv-rHqQ.js → jsx-runtime-DkKmJyBb.js} +2 -2
- package/dist/simulator/assets/simulator-COIE6R49.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-BLn7XV8e.js +2 -0
- package/dist/simulator/simulator.html +2 -2
- package/dist/simulator/temp-files.d.ts +29 -0
- package/dist/simulator/temp-files.js +79 -0
- package/dist/simulator/types.d.ts +43 -0
- package/dist/simulator/types.js +2 -0
- package/package.json +6 -5
- package/dist/shared/simulator-api-metadata.d.ts +0 -26
- package/dist/shared/simulator-api-metadata.js +0 -59
- package/dist/shared/vpath.d.ts +0 -42
- package/dist/shared/vpath.js +0 -105
- package/dist/simulator/assets/device-shell-CH_DMYaz.js +0 -2
- package/dist/simulator/assets/simulator-DXraYerh.js +0 -10
- package/dist/simulator/assets/simulator-mini-app-w1Bhwkld.js +0 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BrowserWindow } from 'electron';
|
|
2
|
+
import { type RuntimeContext, type RuntimeEvents } from '@dimina-kit/electron-runtime/main/runtime-context';
|
|
2
3
|
import type { CompilationAdapter, CustomFileTypes, WorkbenchConfig } from '../../shared/types.js';
|
|
3
4
|
import type { BridgeRouterHandle } from '../ipc/bridge-router.js';
|
|
4
5
|
import type { ConsoleForwarder } from './console-forward/index.js';
|
|
@@ -26,7 +27,7 @@ import type { CustomCreateProjectDialogResult } from '../../shared/types.js';
|
|
|
26
27
|
* Shared mutable state for the workbench application.
|
|
27
28
|
* Passed to each IPC module so they can read/write shared state without closures.
|
|
28
29
|
*/
|
|
29
|
-
export interface WorkbenchContext {
|
|
30
|
+
export interface WorkbenchContext extends RuntimeContext {
|
|
30
31
|
adapter: CompilationAdapter;
|
|
31
32
|
/** Absolute path to the preload script loaded into the simulator webview */
|
|
32
33
|
preloadPath: string;
|
|
@@ -131,6 +132,8 @@ export interface WorkbenchContext {
|
|
|
131
132
|
simulatorUiExtensions: SimulatorUiExtensionRegistry;
|
|
132
133
|
/** Aggregates dispose handlers for every IPC handler, listener, watcher, and CDP session registered by the workbench. */
|
|
133
134
|
registry: DisposableRegistry;
|
|
135
|
+
/** Runtime-domain events consumed by optional DevTools panels and renderer notifications. */
|
|
136
|
+
events: RuntimeEvents;
|
|
134
137
|
/**
|
|
135
138
|
* Per-webContents connection registry. Each trusted webContents (main window,
|
|
136
139
|
* overlay views, native simulator/render guests) is one `Connection` that
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRuntimeEvents, } from '@dimina-kit/electron-runtime/main/runtime-context';
|
|
1
2
|
import { createCdpSessionBroker } from './cdp-session/index.js';
|
|
2
3
|
import { createConnectionRegistry, DisposableRegistry, } from '@dimina-kit/electron-deck/main';
|
|
3
4
|
import { createWorkbenchSenderPolicy } from '../utils/sender-policy.js';
|
|
@@ -15,9 +16,11 @@ import { createSimulatorApiRegistry, } from './simulator/custom-apis.js';
|
|
|
15
16
|
import { createSimulatorUiExtensionRegistry, } from './simulator/ui-extensions.js';
|
|
16
17
|
import { resolveTemplates, sanitizeTemplates } from './projects/templates.js';
|
|
17
18
|
import { BUILTIN_TEMPLATES } from './projects/builtin-templates.js';
|
|
19
|
+
import { runtimeAssetPaths } from '../utils/paths.js';
|
|
18
20
|
export function createWorkbenchContext(opts) {
|
|
19
21
|
const ctx = {
|
|
20
22
|
adapter: opts.adapter ?? defaultAdapter,
|
|
23
|
+
assets: runtimeAssetPaths,
|
|
21
24
|
preloadPath: opts.preloadPath,
|
|
22
25
|
rendererDir: opts.rendererDir,
|
|
23
26
|
apiNamespaces: opts.apiNamespaces ?? [],
|
|
@@ -26,6 +29,20 @@ export function createWorkbenchContext(opts) {
|
|
|
26
29
|
brandingProvider: opts.brandingProvider,
|
|
27
30
|
};
|
|
28
31
|
ctx.registry = new DisposableRegistry();
|
|
32
|
+
ctx.events = createRuntimeEvents();
|
|
33
|
+
ctx.registry.add(ctx.events.on('session-status', (payload) => {
|
|
34
|
+
ctx.notify?.sessionRuntimeStatus(payload);
|
|
35
|
+
}));
|
|
36
|
+
ctx.registry.add(ctx.events.on('app-data-evict', ({ appId, bridgeId }) => {
|
|
37
|
+
ctx.appData?.evictBridge(appId, bridgeId);
|
|
38
|
+
}));
|
|
39
|
+
ctx.registry.add(ctx.events.on('app-data-message', ({ appId, message }) => {
|
|
40
|
+
ctx.appData?.onServiceToRender(appId, message);
|
|
41
|
+
}));
|
|
42
|
+
ctx.registry.add(ctx.events.on('storage-changed', ({ appId, change }) => {
|
|
43
|
+
ctx.onServiceStorageChanged?.(appId, change);
|
|
44
|
+
}));
|
|
45
|
+
ctx.registry.add(() => ctx.events.clear());
|
|
29
46
|
// Empty connection registry as a first-class context field. Connections are
|
|
30
47
|
// acquired by real wiring points (app bootstrap, view-manager endpoints),
|
|
31
48
|
// NOT here — the constructor stays side-effect-free so focused unit tests can
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { IpcMainEvent
|
|
1
|
+
import type { IpcMainEvent } from 'electron';
|
|
2
2
|
import { ipcMain } from 'electron';
|
|
3
|
-
import { type Disposable } from '@dimina-kit/electron-deck/main';
|
|
3
|
+
import { type Disposable, type IpcSenderPolicy } from '@dimina-kit/electron-deck/main';
|
|
4
4
|
type HandleFn = Parameters<typeof ipcMain.handle>[1];
|
|
5
5
|
type ListenerFn = Parameters<typeof ipcMain.on>[1];
|
|
6
6
|
/**
|
|
@@ -9,7 +9,25 @@ type ListenerFn = Parameters<typeof ipcMain.on>[1];
|
|
|
9
9
|
* Returns true when the calling WebContents is allowed to invoke / emit on
|
|
10
10
|
* the channel, false otherwise.
|
|
11
11
|
*/
|
|
12
|
-
export type SenderPolicy =
|
|
12
|
+
export type SenderPolicy = IpcSenderPolicy;
|
|
13
|
+
/**
|
|
14
|
+
* Defense-in-depth frame check (sits alongside the sender white-list). A trusted
|
|
15
|
+
* webContents could embed a sub-frame of arbitrary origin; only its top (main)
|
|
16
|
+
* frame should reach gated IPC, so a sub-frame can't spoof the trusted sender.
|
|
17
|
+
*
|
|
18
|
+
* Verified on Electron 41 that `event.senderFrame` is reliably present on
|
|
19
|
+
* invoke / send / sendSync — including the editor's `beforeunload` sendSync
|
|
20
|
+
* write — and equals `sender.mainFrame` for top-frame traffic, so this never
|
|
21
|
+
* mis-rejects legitimate callers.
|
|
22
|
+
*
|
|
23
|
+
* Fail-closed on a null frame for REAL events: a sub-frame can send a message
|
|
24
|
+
* and immediately navigate/destroy itself, so by delivery time `senderFrame`
|
|
25
|
+
* resolves to null — allowing that would let the navigate-after-send trick
|
|
26
|
+
* bypass the boundary. A real Electron event always exposes the `senderFrame`
|
|
27
|
+
* property and `sender.mainFrame`, so we can tell a real event (frame-modeled,
|
|
28
|
+
* possibly null) from a frame-unaware unit-test stub (neither present) and only
|
|
29
|
+
* skip the check for the latter (the sender-id white-list still gates tests).
|
|
30
|
+
*/
|
|
13
31
|
/**
|
|
14
32
|
* Tiny fluent helper that wraps every `ipcMain.handle` / `ipcMain.on` with a
|
|
15
33
|
* matching removeHandler/removeListener registered into an internal registry.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ipcMain } from 'electron';
|
|
2
|
+
import { isMainFrameIpcSender, } from '@dimina-kit/electron-deck/main';
|
|
2
3
|
import { IpcValidationError } from './ipc-schema.js';
|
|
3
4
|
import { createLogger } from './logger.js';
|
|
4
5
|
const log = createLogger('ipc');
|
|
@@ -29,23 +30,24 @@ function summarizeSender(sender) {
|
|
|
29
30
|
const url = sender.getURL();
|
|
30
31
|
return `id=${sender.id} ${url.slice(0, 120)}`;
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Defense-in-depth frame check (sits alongside the sender white-list). A trusted
|
|
35
|
+
* webContents could embed a sub-frame of arbitrary origin; only its top (main)
|
|
36
|
+
* frame should reach gated IPC, so a sub-frame can't spoof the trusted sender.
|
|
37
|
+
*
|
|
38
|
+
* Verified on Electron 41 that `event.senderFrame` is reliably present on
|
|
39
|
+
* invoke / send / sendSync — including the editor's `beforeunload` sendSync
|
|
40
|
+
* write — and equals `sender.mainFrame` for top-frame traffic, so this never
|
|
41
|
+
* mis-rejects legitimate callers.
|
|
42
|
+
*
|
|
43
|
+
* Fail-closed on a null frame for REAL events: a sub-frame can send a message
|
|
44
|
+
* and immediately navigate/destroy itself, so by delivery time `senderFrame`
|
|
45
|
+
* resolves to null — allowing that would let the navigate-after-send trick
|
|
46
|
+
* bypass the boundary. A real Electron event always exposes the `senderFrame`
|
|
47
|
+
* property and `sender.mainFrame`, so we can tell a real event (frame-modeled,
|
|
48
|
+
* possibly null) from a frame-unaware unit-test stub (neither present) and only
|
|
49
|
+
* skip the check for the latter (the sender-id white-list still gates tests).
|
|
50
|
+
*/
|
|
49
51
|
/**
|
|
50
52
|
* Tiny fluent helper that wraps every `ipcMain.handle` / `ipcMain.on` with a
|
|
51
53
|
* matching removeHandler/removeListener registered into an internal registry.
|
|
@@ -84,7 +86,7 @@ export class IpcRegistry {
|
|
|
84
86
|
const guarded = policy
|
|
85
87
|
? async (event, ...args) => {
|
|
86
88
|
const sender = event.sender;
|
|
87
|
-
if (!policy(sender) || !
|
|
89
|
+
if (!policy(sender) || !isMainFrameIpcSender(event)) {
|
|
88
90
|
console.warn(`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`);
|
|
89
91
|
throw new Error(`IPC sender rejected for channel ${channel}`);
|
|
90
92
|
}
|
|
@@ -106,26 +108,27 @@ export class IpcRegistry {
|
|
|
106
108
|
handleSync(channel, fn) {
|
|
107
109
|
const policy = this.policy;
|
|
108
110
|
const listener = (event, ...args) => {
|
|
111
|
+
const syncEvent = event;
|
|
109
112
|
// Everything — including the policy check — runs inside the try so EVERY
|
|
110
113
|
// path sets `event.returnValue`. sendSync blocks the renderer until it is
|
|
111
114
|
// set, so an unset value (e.g. a throwing policy fn) would hang the
|
|
112
115
|
// renderer forever; the catch guarantees a sentinel instead.
|
|
113
116
|
try {
|
|
114
|
-
if (policy && (!policy(event.sender) || !
|
|
117
|
+
if (policy && (!policy(event.sender) || !isMainFrameIpcSender(event))) {
|
|
115
118
|
console.warn(`[ipc] sender rejected for channel '${channel}' (${summarizeSender(event.sender)})`);
|
|
116
|
-
|
|
119
|
+
syncEvent.returnValue = {
|
|
117
120
|
ok: false,
|
|
118
121
|
code: 'EREJECTED',
|
|
119
122
|
message: `IPC sender rejected for channel ${channel}`,
|
|
120
123
|
};
|
|
121
124
|
return;
|
|
122
125
|
}
|
|
123
|
-
|
|
126
|
+
syncEvent.returnValue = fn(event, ...args);
|
|
124
127
|
}
|
|
125
128
|
catch (err) {
|
|
126
129
|
reportListenerError(channel, err);
|
|
127
130
|
const code = err?.code;
|
|
128
|
-
|
|
131
|
+
syncEvent.returnValue = {
|
|
129
132
|
ok: false,
|
|
130
133
|
code: typeof code === 'string' ? code : 'EUNKNOWN',
|
|
131
134
|
message: err instanceof Error ? err.message : String(err),
|
|
@@ -156,7 +159,7 @@ export class IpcRegistry {
|
|
|
156
159
|
const guarded = policy
|
|
157
160
|
? (event, ...args) => {
|
|
158
161
|
const sender = event.sender;
|
|
159
|
-
if (!policy(sender) || !
|
|
162
|
+
if (!policy(sender) || !isMainFrameIpcSender(event)) {
|
|
160
163
|
console.warn(`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`);
|
|
161
164
|
return;
|
|
162
165
|
}
|
|
@@ -32,6 +32,20 @@ export declare const mainPreloadPath: string;
|
|
|
32
32
|
*/
|
|
33
33
|
export declare const hostToolbarRuntimePreloadPath: string;
|
|
34
34
|
export declare const simulatorDir: string;
|
|
35
|
+
/**
|
|
36
|
+
* DevTools builds its own copy of every native mini-app runtime asset. Pin the
|
|
37
|
+
* shared runtime bridge to that copy so a packaged client never depends on the
|
|
38
|
+
* standalone npm package's optional asset assembly step.
|
|
39
|
+
*/
|
|
40
|
+
export declare const runtimeAssetPaths: {
|
|
41
|
+
root: string;
|
|
42
|
+
simulatorDir: string;
|
|
43
|
+
simulatorPreloadPath: string;
|
|
44
|
+
renderHostHtmlPath: string;
|
|
45
|
+
renderHostPreloadPath: string;
|
|
46
|
+
serviceHostHtmlPath: string;
|
|
47
|
+
serviceHostPreloadPath: string;
|
|
48
|
+
};
|
|
35
49
|
export declare function getRendererDir(): string;
|
|
36
50
|
export declare function getPreloadDir(): string;
|
|
37
51
|
export declare function getRendererHtml(filename: string): string;
|
package/dist/main/utils/paths.js
CHANGED
|
@@ -60,6 +60,20 @@ export const mainPreloadPath = path.join(devtoolsPackageRoot, 'dist/preload/wind
|
|
|
60
60
|
*/
|
|
61
61
|
export const hostToolbarRuntimePreloadPath = path.join(devtoolsPackageRoot, 'dist/preload/windows/host-toolbar-runtime.cjs');
|
|
62
62
|
export const simulatorDir = path.join(devtoolsPackageRoot, 'dist/simulator');
|
|
63
|
+
/**
|
|
64
|
+
* DevTools builds its own copy of every native mini-app runtime asset. Pin the
|
|
65
|
+
* shared runtime bridge to that copy so a packaged client never depends on the
|
|
66
|
+
* standalone npm package's optional asset assembly step.
|
|
67
|
+
*/
|
|
68
|
+
export const runtimeAssetPaths = {
|
|
69
|
+
root: path.join(devtoolsPackageRoot, 'dist'),
|
|
70
|
+
simulatorDir,
|
|
71
|
+
simulatorPreloadPath: path.join(devtoolsPackageRoot, 'dist/preload/windows/simulator.cjs'),
|
|
72
|
+
renderHostHtmlPath: path.join(devtoolsPackageRoot, 'dist/render-host/pageFrame.html'),
|
|
73
|
+
renderHostPreloadPath: path.join(devtoolsPackageRoot, 'dist/render-host/preload.cjs'),
|
|
74
|
+
serviceHostHtmlPath: path.join(devtoolsPackageRoot, 'dist/service-host/service.html'),
|
|
75
|
+
serviceHostPreloadPath: path.join(devtoolsPackageRoot, 'dist/service-host/preload.cjs'),
|
|
76
|
+
};
|
|
63
77
|
export function getRendererDir() {
|
|
64
78
|
return rendererDir;
|
|
65
79
|
}
|
|
@@ -1,93 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import type { ServiceHostSpec } from '../../services/service-host-pool/pool.js';
|
|
3
|
-
/**
|
|
4
|
-
* Default service-host partition when no project `appId` is supplied (the
|
|
5
|
-
* pre-warm pool's shared, isolation-UNAWARE session — see `serviceHostSpec`).
|
|
6
|
-
* Per-project service hosts get a `persist:miniapp-<key>` partition instead so
|
|
7
|
-
* their localStorage/cookies are shared with that project's render side only.
|
|
8
|
-
*/
|
|
9
|
-
export declare const SERVICE_HOST_PARTITION = "persist:simulator";
|
|
10
|
-
/** Absolute path to the service-host preload (loaded at runtime by path). */
|
|
11
|
-
export declare const serviceHostPreloadPath: string;
|
|
12
|
-
export interface ServiceHostWindowOptions {
|
|
13
|
-
bridgeId: string;
|
|
14
|
-
appId: string;
|
|
15
|
-
/** Current project root. Folded into the session partition (with appId) so a
|
|
16
|
-
* different project declaring the same appId gets isolated storage. Omitted on
|
|
17
|
-
* the pre-warm pool path (no project known yet → shared partition). */
|
|
18
|
-
projectPath?: string;
|
|
19
|
-
pagePath: string;
|
|
20
|
-
pkgRoot: string;
|
|
21
|
-
root?: string;
|
|
22
|
-
resourceBaseUrl: string;
|
|
23
|
-
hostEnvSnapshot?: Record<string, unknown>;
|
|
24
|
-
/**
|
|
25
|
-
* Custom API namespace names (host config `apiNamespaces`, e.g. `['qd']`).
|
|
26
|
-
* Encoded into the spawn URL so the service-host preload can install them as
|
|
27
|
-
* `globalThis.__diminaApiNamespaces` before service.js evaluates — without
|
|
28
|
-
* which page logic referencing `qd.*` throws `ReferenceError: qd is not defined`.
|
|
29
|
-
*/
|
|
30
|
-
apiNamespaces?: string[];
|
|
31
|
-
/**
|
|
32
|
-
* Observes a spawn-navigation `loadURL` rejection (NOT encoded into the
|
|
33
|
-
* spawn URL). Without it the fresh-window path swallows the failure entirely
|
|
34
|
-
* — `did-finish-load` never fires, the session hangs, and the only signal is
|
|
35
|
-
* the launch watchdog's late timeout instead of the real cause.
|
|
36
|
-
*/
|
|
37
|
-
onLoadFailed?: (err: unknown) => void;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Construct an (un-navigated) service-host BrowserWindow. Split out from
|
|
41
|
-
* `createServiceHostWindow` so a pre-warm pool can build the window once and
|
|
42
|
-
* navigate it per spawn (the spawn context lives in the URL — see
|
|
43
|
-
* `buildServiceHostSpawnUrl`). The process-model flags here are the contract
|
|
44
|
-
* the service-host preload depends on (it `require('electron')` and writes
|
|
45
|
-
* globals onto the page realm).
|
|
46
|
-
*/
|
|
47
|
-
export declare function constructServiceHostWindow(opts?: {
|
|
48
|
-
appId?: string;
|
|
49
|
-
partition?: string;
|
|
50
|
-
}): BrowserWindow;
|
|
51
|
-
/**
|
|
52
|
-
* Build the file:// spawn URL carrying the per-spawn context the preload reads
|
|
53
|
-
* from `location.search` at eval time (bridgeId/appId/pagePath/pkgRoot/
|
|
54
|
-
* resourceBaseUrl/hostEnv).
|
|
55
|
-
*/
|
|
56
|
-
export declare function buildServiceHostSpawnUrl(opts: ServiceHostWindowOptions): string;
|
|
57
|
-
/**
|
|
58
|
-
* Navigate a (constructed or pooled) service-host window to its spawn URL. The
|
|
59
|
-
* preload re-evaluates on this navigation, rebuilding `__diminaSpawnContext`.
|
|
60
|
-
* Returns the load promise (resolves on did-finish-load). Attaches the dev-only
|
|
61
|
-
* detached-DevTools hook, matching the original behavior.
|
|
62
|
-
*
|
|
63
|
-
* A `loadURL` rejection is swallowed — the pooled-window recycle path expects
|
|
64
|
-
* a failed navigation not to reject the caller — but `opts.onLoadFailed`, when
|
|
65
|
-
* given, still observes the original rejection reason (routed by the caller
|
|
66
|
-
* into the diagnostics bus) before this resolves.
|
|
67
|
-
*/
|
|
68
|
-
export declare function navigateServiceHost(win: BrowserWindow, url: string, opts?: {
|
|
69
|
-
onLoadFailed?: (err: unknown) => void;
|
|
70
|
-
}): Promise<void>;
|
|
71
|
-
/**
|
|
72
|
-
* The spec a `ServiceHostPool` uses to pre-warm service-host windows. Keeps the
|
|
73
|
-
* pooled window byte-equivalent to `constructServiceHostWindow`'s output, and
|
|
74
|
-
* opts out of the reset storage-clear because the service host shares its
|
|
75
|
-
* session with live projects (see ServiceHostSpec docs).
|
|
76
|
-
*
|
|
77
|
-
* KNOWN BLOCKER (intentional): the pre-warm pool is NOT per-project isolation
|
|
78
|
-
* aware. It warms windows on ONE `defaultSpec` partition before any project is
|
|
79
|
-
* known, so a pooled window cannot carry a project-derived partition — handing
|
|
80
|
-
* one project's warmed window to another project would cross-contaminate
|
|
81
|
-
* storage. Therefore the pool's `defaultSpec` (and pooled `acquire`) call this
|
|
82
|
-
* with NO appId → the shared `persist:simulator` partition; per-project
|
|
83
|
-
* isolation only applies on the default (non-pooled) `createServiceHostWindow`
|
|
84
|
-
* path, which passes the project's appId. Reconciling pooling with per-project
|
|
85
|
-
* partitions (e.g. per-partition sub-pools) is out of scope for this change.
|
|
86
|
-
*/
|
|
87
|
-
export declare function serviceHostSpec(appId?: string, projectPath?: string): ServiceHostSpec;
|
|
88
|
-
/**
|
|
89
|
-
* Construct + navigate a fresh service-host window (the non-pool path).
|
|
90
|
-
* Behavior-identical to the pre-refactor implementation.
|
|
91
|
-
*/
|
|
92
|
-
export declare function createServiceHostWindow(opts: ServiceHostWindowOptions): BrowserWindow;
|
|
1
|
+
export * from '@dimina-kit/electron-runtime/main/service-host-window';
|
|
93
2
|
//# sourceMappingURL=create.d.ts.map
|
|
@@ -1,155 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
4
|
-
import { devtoolsPackageRoot } from '../../utils/paths.js';
|
|
5
|
-
import { configureMiniappSession, miniappPartition, SHARED_MINIAPP_PARTITION } from '../../services/views/miniapp-partition.js';
|
|
6
|
-
/**
|
|
7
|
-
* Default service-host partition when no project `appId` is supplied (the
|
|
8
|
-
* pre-warm pool's shared, isolation-UNAWARE session — see `serviceHostSpec`).
|
|
9
|
-
* Per-project service hosts get a `persist:miniapp-<key>` partition instead so
|
|
10
|
-
* their localStorage/cookies are shared with that project's render side only.
|
|
11
|
-
*/
|
|
12
|
-
export const SERVICE_HOST_PARTITION = SHARED_MINIAPP_PARTITION;
|
|
13
|
-
/** Absolute path to the service-host preload (loaded at runtime by path). */
|
|
14
|
-
export const serviceHostPreloadPath = path.join(devtoolsPackageRoot, 'dist/service-host/preload.cjs');
|
|
15
|
-
const serviceHostHtmlPath = path.join(devtoolsPackageRoot, 'dist/service-host/service.html');
|
|
16
|
-
/**
|
|
17
|
-
* Construct an (un-navigated) service-host BrowserWindow. Split out from
|
|
18
|
-
* `createServiceHostWindow` so a pre-warm pool can build the window once and
|
|
19
|
-
* navigate it per spawn (the spawn context lives in the URL — see
|
|
20
|
-
* `buildServiceHostSpawnUrl`). The process-model flags here are the contract
|
|
21
|
-
* the service-host preload depends on (it `require('electron')` and writes
|
|
22
|
-
* globals onto the page realm).
|
|
23
|
-
*/
|
|
24
|
-
export function constructServiceHostWindow(opts = {}) {
|
|
25
|
-
const partition = opts.partition ?? SERVICE_HOST_PARTITION;
|
|
26
|
-
// Apply the simulator runtime's protocol handlers + webRequest policies to
|
|
27
|
-
// THIS project's session before the window loads. Idempotent per partition.
|
|
28
|
-
configureMiniappSession(partition);
|
|
29
|
-
return new BrowserWindow({
|
|
30
|
-
width: 980,
|
|
31
|
-
height: 720,
|
|
32
|
-
show: false,
|
|
33
|
-
title: opts.appId ? `Dimina Service Host: ${opts.appId}` : 'Dimina Service Host',
|
|
34
|
-
webPreferences: {
|
|
35
|
-
partition,
|
|
36
|
-
nodeIntegration: false,
|
|
37
|
-
contextIsolation: false,
|
|
38
|
-
sandbox: false,
|
|
39
|
-
preload: serviceHostPreloadPath,
|
|
40
|
-
devTools: true,
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Build the file:// spawn URL carrying the per-spawn context the preload reads
|
|
46
|
-
* from `location.search` at eval time (bridgeId/appId/pagePath/pkgRoot/
|
|
47
|
-
* resourceBaseUrl/hostEnv).
|
|
48
|
-
*/
|
|
49
|
-
export function buildServiceHostSpawnUrl(opts) {
|
|
50
|
-
const url = new URL(pathToFileURL(serviceHostHtmlPath).toString());
|
|
51
|
-
url.searchParams.set('bridgeId', opts.bridgeId);
|
|
52
|
-
url.searchParams.set('appId', opts.appId);
|
|
53
|
-
url.searchParams.set('pagePath', opts.pagePath);
|
|
54
|
-
url.searchParams.set('pkgRoot', opts.pkgRoot);
|
|
55
|
-
url.searchParams.set('root', opts.root || 'main');
|
|
56
|
-
url.searchParams.set('resourceBaseUrl', opts.resourceBaseUrl);
|
|
57
|
-
if (opts.hostEnvSnapshot) {
|
|
58
|
-
// Encode the resolved host-env so preload can hydrate
|
|
59
|
-
// `__diminaSpawnContext.hostEnvSnapshot` before service.js loads.
|
|
60
|
-
// Without this, sync-impls/system-info.ts falls back to generic defaults.
|
|
61
|
-
url.searchParams.set('hostEnv', encodeURIComponent(JSON.stringify(opts.hostEnvSnapshot)));
|
|
62
|
-
}
|
|
63
|
-
if (opts.apiNamespaces && opts.apiNamespaces.length > 0) {
|
|
64
|
-
// CSV of namespace names; the preload reads this back into
|
|
65
|
-
// `globalThis.__diminaApiNamespaces`. URLSearchParams round-trips the comma.
|
|
66
|
-
url.searchParams.set('apiNamespaces', opts.apiNamespaces.join(','));
|
|
67
|
-
}
|
|
68
|
-
return url.toString();
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Navigate a (constructed or pooled) service-host window to its spawn URL. The
|
|
72
|
-
* preload re-evaluates on this navigation, rebuilding `__diminaSpawnContext`.
|
|
73
|
-
* Returns the load promise (resolves on did-finish-load). Attaches the dev-only
|
|
74
|
-
* detached-DevTools hook, matching the original behavior.
|
|
75
|
-
*
|
|
76
|
-
* A `loadURL` rejection is swallowed — the pooled-window recycle path expects
|
|
77
|
-
* a failed navigation not to reject the caller — but `opts.onLoadFailed`, when
|
|
78
|
-
* given, still observes the original rejection reason (routed by the caller
|
|
79
|
-
* into the diagnostics bus) before this resolves.
|
|
80
|
-
*/
|
|
81
|
-
export function navigateServiceHost(win, url, opts) {
|
|
82
|
-
const loaded = Promise.resolve(win.loadURL(url)).then(() => undefined, (err) => {
|
|
83
|
-
opts?.onLoadFailed?.(err);
|
|
84
|
-
return undefined;
|
|
85
|
-
});
|
|
86
|
-
if (!app.isPackaged && process.env.NODE_ENV !== 'test') {
|
|
87
|
-
// Recycled pooled windows get navigated repeatedly. A `once` hook that never
|
|
88
|
-
// fires (the spawn was disposed before its service.html settled) stays on the
|
|
89
|
-
// reused webContents and the next spawn's hook fires alongside it → DevTools
|
|
90
|
-
// can open twice and stale hooks accumulate. Remove the prior hook before
|
|
91
|
-
// attaching a fresh one. (Audit A6; dev-only.)
|
|
92
|
-
const w = win;
|
|
93
|
-
if (w.__diminaDevToolsHook) {
|
|
94
|
-
try {
|
|
95
|
-
win.webContents.removeListener('did-finish-load', w.__diminaDevToolsHook);
|
|
96
|
-
}
|
|
97
|
-
catch { /* wc gone */ }
|
|
98
|
-
}
|
|
99
|
-
const hook = () => {
|
|
100
|
-
// URL-guard so a reset's about:blank load doesn't pop DevTools.
|
|
101
|
-
if (!win.isDestroyed() && win.webContents.getURL().includes('service.html')) {
|
|
102
|
-
win.webContents.openDevTools({ mode: 'detach' });
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
w.__diminaDevToolsHook = hook;
|
|
106
|
-
win.webContents.once('did-finish-load', hook);
|
|
107
|
-
}
|
|
108
|
-
return loaded;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* The spec a `ServiceHostPool` uses to pre-warm service-host windows. Keeps the
|
|
112
|
-
* pooled window byte-equivalent to `constructServiceHostWindow`'s output, and
|
|
113
|
-
* opts out of the reset storage-clear because the service host shares its
|
|
114
|
-
* session with live projects (see ServiceHostSpec docs).
|
|
115
|
-
*
|
|
116
|
-
* KNOWN BLOCKER (intentional): the pre-warm pool is NOT per-project isolation
|
|
117
|
-
* aware. It warms windows on ONE `defaultSpec` partition before any project is
|
|
118
|
-
* known, so a pooled window cannot carry a project-derived partition — handing
|
|
119
|
-
* one project's warmed window to another project would cross-contaminate
|
|
120
|
-
* storage. Therefore the pool's `defaultSpec` (and pooled `acquire`) call this
|
|
121
|
-
* with NO appId → the shared `persist:simulator` partition; per-project
|
|
122
|
-
* isolation only applies on the default (non-pooled) `createServiceHostWindow`
|
|
123
|
-
* path, which passes the project's appId. Reconciling pooling with per-project
|
|
124
|
-
* partitions (e.g. per-partition sub-pools) is out of scope for this change.
|
|
125
|
-
*/
|
|
126
|
-
export function serviceHostSpec(appId, projectPath) {
|
|
127
|
-
return {
|
|
128
|
-
// Per-project partition when an appId is known (so this project's service
|
|
129
|
-
// host shares storage ONLY with its own render side); the shared partition
|
|
130
|
-
// for the pre-warm pool's default spec (no appId — see KNOWN BLOCKER below).
|
|
131
|
-
// projectPath joins the key so same-appId/different-path projects isolate;
|
|
132
|
-
// it must match the simulator WCV's partition (view-manager passes the same
|
|
133
|
-
// (appId, projectPath)).
|
|
134
|
-
partition: appId ? miniappPartition(appId, projectPath) : SERVICE_HOST_PARTITION,
|
|
135
|
-
preloadPath: serviceHostPreloadPath,
|
|
136
|
-
size: { width: 980, height: 720 },
|
|
137
|
-
contextIsolation: false,
|
|
138
|
-
sandbox: false,
|
|
139
|
-
nodeIntegration: false,
|
|
140
|
-
clearStorageOnReset: false,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Construct + navigate a fresh service-host window (the non-pool path).
|
|
145
|
-
* Behavior-identical to the pre-refactor implementation.
|
|
146
|
-
*/
|
|
147
|
-
export function createServiceHostWindow(opts) {
|
|
148
|
-
// Default (non-pooled) path: pin the service host to THIS project's partition
|
|
149
|
-
// so its logic-layer localStorage/cookies are shared with the project's render
|
|
150
|
-
// side only, never with other projects.
|
|
151
|
-
const win = constructServiceHostWindow({ appId: opts.appId, partition: miniappPartition(opts.appId, opts.projectPath) });
|
|
152
|
-
void navigateServiceHost(win, buildServiceHostSpawnUrl(opts), { onLoadFailed: opts.onLoadFailed });
|
|
153
|
-
return win;
|
|
154
|
-
}
|
|
1
|
+
export * from '@dimina-kit/electron-runtime/main/service-host-window';
|
|
155
2
|
//# sourceMappingURL=create.js.map
|