@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.
Files changed (104) hide show
  1. package/dist/main/app/app.js +7 -0
  2. package/dist/main/app/bootstrap.d.ts +8 -5
  3. package/dist/main/app/bootstrap.js +15 -15
  4. package/dist/main/index.bundle.js +2207 -5152
  5. package/dist/main/ipc/app-lifecycle.d.ts +1 -31
  6. package/dist/main/ipc/app-lifecycle.js +1 -53
  7. package/dist/main/ipc/bridge-router.d.ts +19 -152
  8. package/dist/main/ipc/bridge-router.js +28 -2186
  9. package/dist/main/ipc/page-scroll.d.ts +1 -13
  10. package/dist/main/ipc/page-scroll.js +1 -16
  11. package/dist/main/ipc/session-listener-bag.d.ts +1 -40
  12. package/dist/main/ipc/session-listener-bag.js +1 -39
  13. package/dist/main/services/console-forward/index.d.ts +1 -60
  14. package/dist/main/services/console-forward/index.js +1 -225
  15. package/dist/main/services/console-forward/internal-log.d.ts +1 -10
  16. package/dist/main/services/console-forward/internal-log.js +1 -22
  17. package/dist/main/services/diagnostics/index.d.ts +1 -58
  18. package/dist/main/services/diagnostics/index.js +1 -60
  19. package/dist/main/services/dimina-resource-server.d.ts +1 -6
  20. package/dist/main/services/dimina-resource-server.js +1 -98
  21. package/dist/main/services/mcp/target-manager.js +5 -1
  22. package/dist/main/services/network-forward/index.d.ts +15 -3
  23. package/dist/main/services/network-forward/index.js +30 -4
  24. package/dist/main/services/network-forward/websocket.d.ts +36 -0
  25. package/dist/main/services/network-forward/websocket.js +120 -0
  26. package/dist/main/services/service-host-pool/pool.d.ts +1 -196
  27. package/dist/main/services/service-host-pool/pool.js +1 -363
  28. package/dist/main/services/simulator/custom-apis.d.ts +1 -42
  29. package/dist/main/services/simulator/custom-apis.js +1 -46
  30. package/dist/main/services/simulator/referer.d.ts +1 -9
  31. package/dist/main/services/simulator/referer.js +1 -19
  32. package/dist/main/services/simulator-storage/service-storage-ops.d.ts +1 -39
  33. package/dist/main/services/simulator-storage/service-storage-ops.js +1 -66
  34. package/dist/main/services/simulator-temp-files/disk.d.ts +1 -54
  35. package/dist/main/services/simulator-temp-files/disk.js +1 -237
  36. package/dist/main/services/simulator-temp-files/fs-channels.d.ts +1 -68
  37. package/dist/main/services/simulator-temp-files/fs-channels.js +1 -107
  38. package/dist/main/services/simulator-temp-files/index.d.ts +1 -32
  39. package/dist/main/services/simulator-temp-files/index.js +1 -181
  40. package/dist/main/services/simulator-temp-files/request-handler.d.ts +1 -29
  41. package/dist/main/services/simulator-temp-files/request-handler.js +1 -177
  42. package/dist/main/services/simulator-temp-files/resolver.d.ts +1 -21
  43. package/dist/main/services/simulator-temp-files/resolver.js +1 -21
  44. package/dist/main/services/simulator-temp-files/store.d.ts +1 -13
  45. package/dist/main/services/simulator-temp-files/store.js +1 -23
  46. package/dist/main/services/views/miniapp-partition.d.ts +1 -89
  47. package/dist/main/services/views/miniapp-partition.js +1 -158
  48. package/dist/main/services/views/simulator-session-policy.d.ts +1 -27
  49. package/dist/main/services/views/simulator-session-policy.js +1 -98
  50. package/dist/main/services/workbench-context.d.ts +4 -1
  51. package/dist/main/services/workbench-context.js +17 -0
  52. package/dist/main/utils/ipc-registry.d.ts +21 -3
  53. package/dist/main/utils/ipc-registry.js +26 -23
  54. package/dist/main/utils/paths.d.ts +14 -0
  55. package/dist/main/utils/paths.js +14 -0
  56. package/dist/main/windows/service-host-window/create.d.ts +1 -92
  57. package/dist/main/windows/service-host-window/create.js +1 -154
  58. package/dist/native-host/common/common.js +154 -58
  59. package/dist/native-host/container/pageFrame.css +1 -1
  60. package/dist/native-host/render/render.js +6611 -3312
  61. package/dist/native-host/service/service.js +2 -2
  62. package/dist/preload/index.d.ts +2 -0
  63. package/dist/preload/index.js +1 -0
  64. package/dist/preload/runtime/clipboard.d.ts +6 -0
  65. package/dist/preload/runtime/clipboard.js +12 -0
  66. package/dist/preload/runtime/native-host.d.ts +2 -0
  67. package/dist/preload/runtime/native-host.js +5 -10
  68. package/dist/preload/runtime/node-bindings.d.ts +18 -0
  69. package/dist/preload/runtime/node-bindings.js +44 -0
  70. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  71. package/dist/preload/windows/main.cjs +1195 -897
  72. package/dist/preload/windows/main.cjs.map +4 -4
  73. package/dist/preload/windows/simulator.cjs +149 -60
  74. package/dist/preload/windows/simulator.cjs.map +4 -4
  75. package/dist/preload/windows/simulator.js +149 -60
  76. package/dist/render-host/pageFrame.html +3 -3
  77. package/dist/shared/bridge-channels.d.ts +1 -419
  78. package/dist/shared/bridge-channels.js +1 -89
  79. package/dist/shared/dmb-resource-url.d.ts +2 -0
  80. package/dist/shared/dmb-resource-url.js +2 -0
  81. package/dist/shared/ipc-channels.d.ts +2 -32
  82. package/dist/shared/ipc-channels.js +2 -5
  83. package/dist/shared/node-bindings.d.ts +2 -0
  84. package/dist/shared/node-bindings.js +2 -0
  85. package/dist/shared/request-core.d.ts +1 -83
  86. package/dist/shared/request-core.js +1 -167
  87. package/dist/simulator/assets/device-shell-Cw7c-IkG.js +2 -0
  88. package/dist/simulator/assets/{device-shell-BHz1v3Yo.css → device-shell-DbC5ccs8.css} +1 -1
  89. package/dist/simulator/assets/{jsx-runtime-hBv-rHqQ.js → jsx-runtime-DkKmJyBb.js} +2 -2
  90. package/dist/simulator/assets/simulator-COIE6R49.js +10 -0
  91. package/dist/simulator/assets/simulator-mini-app-BLn7XV8e.js +2 -0
  92. package/dist/simulator/simulator.html +2 -2
  93. package/dist/simulator/temp-files.d.ts +29 -0
  94. package/dist/simulator/temp-files.js +79 -0
  95. package/dist/simulator/types.d.ts +43 -0
  96. package/dist/simulator/types.js +2 -0
  97. package/package.json +6 -5
  98. package/dist/shared/simulator-api-metadata.d.ts +0 -26
  99. package/dist/shared/simulator-api-metadata.js +0 -59
  100. package/dist/shared/vpath.d.ts +0 -42
  101. package/dist/shared/vpath.js +0 -105
  102. package/dist/simulator/assets/device-shell-CH_DMYaz.js +0 -2
  103. package/dist/simulator/assets/simulator-DXraYerh.js +0 -10
  104. 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, WebContents } from 'electron';
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 = (sender: WebContents) => boolean;
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
- function isMainFrameSender(event) {
33
- // Read via loose casts: the Electron types declare these always-present, but
34
- // unit-test stubs legitimately omit them and real events can carry a null
35
- // senderFrame after navigation/destruction.
36
- const frame = event.senderFrame;
37
- const main = event.sender.mainFrame;
38
- // Frame-unaware stub (NEITHER field modeled) not a real frame boundary; the
39
- // sender-id white-list is the gate. Real events always have both, so they fall
40
- // through to the strict check; a partial/malformed event (only one field) also
41
- // falls through and fail-closes below rather than escaping here.
42
- if (frame === undefined && main === undefined)
43
- return true;
44
- // Real event, unresolvable frame (navigate-after-send / destroyed) → reject.
45
- if (frame == null || main == null)
46
- return false;
47
- return frame.routingId === main.routingId && frame.processId === main.processId;
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) || !isMainFrameSender(event)) {
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) || !isMainFrameSender(event))) {
117
+ if (policy && (!policy(event.sender) || !isMainFrameIpcSender(event))) {
115
118
  console.warn(`[ipc] sender rejected for channel '${channel}' (${summarizeSender(event.sender)})`);
116
- event.returnValue = {
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
- event.returnValue = fn(event, ...args);
126
+ syncEvent.returnValue = fn(event, ...args);
124
127
  }
125
128
  catch (err) {
126
129
  reportListenerError(channel, err);
127
130
  const code = err?.code;
128
- event.returnValue = {
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) || !isMainFrameSender(event)) {
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;
@@ -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
- import { BrowserWindow } from 'electron';
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
- import { app, BrowserWindow } from 'electron';
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