@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,99 +1,2 @@
1
- import http from 'node:http';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import { pathToFileURL } from 'node:url';
5
- const MIME_TYPES = {
6
- '.css': 'text/css; charset=utf-8',
7
- '.html': 'text/html; charset=utf-8',
8
- '.js': 'text/javascript; charset=utf-8',
9
- '.json': 'application/json; charset=utf-8',
10
- '.map': 'application/json; charset=utf-8',
11
- '.svg': 'image/svg+xml',
12
- '.png': 'image/png',
13
- '.jpg': 'image/jpeg',
14
- '.jpeg': 'image/jpeg',
15
- '.gif': 'image/gif',
16
- '.webp': 'image/webp',
17
- '.wasm': 'application/wasm',
18
- };
19
- export async function startDiminaResourceServer(rootDir) {
20
- const root = path.resolve(rootDir);
21
- const server = http.createServer((req, res) => {
22
- void handleRequest(root, req, res);
23
- });
24
- await new Promise((resolve, reject) => {
25
- server.once('error', reject);
26
- server.listen(0, '127.0.0.1', () => {
27
- server.off('error', reject);
28
- resolve();
29
- });
30
- });
31
- const address = server.address();
32
- return {
33
- baseUrl: `http://127.0.0.1:${address.port}/`,
34
- close() {
35
- return new Promise((resolve, reject) => {
36
- server.close((err) => err ? reject(err) : resolve());
37
- });
38
- },
39
- };
40
- }
41
- async function handleRequest(root, req, res) {
42
- setCorsHeaders(res);
43
- if (req.method === 'OPTIONS') {
44
- res.writeHead(204);
45
- res.end();
46
- return;
47
- }
48
- if (req.method !== 'GET' && req.method !== 'HEAD') {
49
- res.writeHead(405);
50
- res.end('Method Not Allowed');
51
- return;
52
- }
53
- const url = new URL(req.url ?? '/', 'http://127.0.0.1');
54
- const filePath = resolveContainedPath(root, decodeURIComponent(url.pathname));
55
- if (!filePath) {
56
- res.writeHead(403);
57
- res.end('Forbidden');
58
- return;
59
- }
60
- try {
61
- const stat = await fs.promises.stat(filePath);
62
- if (!stat.isFile()) {
63
- res.writeHead(404);
64
- res.end('Not Found');
65
- return;
66
- }
67
- res.writeHead(200, {
68
- 'content-type': MIME_TYPES[path.extname(filePath)] ?? 'application/octet-stream',
69
- 'content-length': stat.size,
70
- 'cache-control': 'no-store',
71
- });
72
- if (req.method === 'HEAD') {
73
- res.end();
74
- return;
75
- }
76
- fs.createReadStream(filePath).pipe(res);
77
- }
78
- catch {
79
- res.writeHead(404);
80
- res.end('Not Found');
81
- }
82
- }
83
- function resolveContainedPath(root, pathname) {
84
- const relative = pathname.replace(/^\/+/, '') || 'index.html';
85
- const resolved = path.resolve(root, relative);
86
- if (resolved !== root && !resolved.startsWith(root + path.sep))
87
- return null;
88
- return resolved;
89
- }
90
- function setCorsHeaders(res) {
91
- res.setHeader('access-control-allow-origin', '*');
92
- res.setHeader('access-control-allow-headers', '*');
93
- res.setHeader('access-control-allow-methods', 'GET,HEAD,OPTIONS');
94
- res.setHeader('cross-origin-resource-policy', 'cross-origin');
95
- }
96
- export function toSourceUrl(filePath) {
97
- return pathToFileURL(filePath).toString();
98
- }
1
+ export * from '@dimina-kit/electron-runtime/main/dimina-resource-server';
99
2
  //# sourceMappingURL=dimina-resource-server.js.map
@@ -11,10 +11,14 @@
11
11
  */
12
12
  import CDP from 'chrome-remote-interface';
13
13
  import { DEFAULT_CDP_PORT } from '../../../shared/constants.js';
14
+ import { DMB_PAGEFRAME_DOC_NAME } from '../../../shared/dmb-resource-url.js';
14
15
  const SIMULATOR_URL_PATTERN = 'localhost:7788';
15
16
  // Native-host: the real mini-app page runs in a nested render-host <webview>
16
17
  // guest whose CDP target URL carries the render frame + the page's bridgeId.
17
- const RENDER_GUEST_PATTERN = 'pageFrame.html';
18
+ // Import the shared reserved doc name rather than hand-writing a second
19
+ // literal — that duplication is exactly what let this pattern drift out of
20
+ // sync with the actual document URL shape (dmb-resource-url.ts).
21
+ const RENDER_GUEST_PATTERN = DMB_PAGEFRAME_DOC_NAME;
18
22
  const MAX_BUFFER = 500;
19
23
  const RECONNECT_INTERVAL_MS = 3000;
20
24
  let cdpPort = DEFAULT_CDP_PORT;
@@ -68,13 +68,16 @@
68
68
  * Chrome DevTools window) self-heals via the lease's `onDetach` — capture
69
69
  * re-wires itself even though `attachRenderGuest` is only ever called ONCE
70
70
  * per guest (at webview creation).
71
- * ⚠️ NOT observable by any `webContents.debugger`: a request a host module issues
72
- * directly from the MAIN process. Those need an explicit `report()` call
73
- * (exposed below; it uses the console fallback path).
71
+ * ⚠️ NOT observable by any `webContents.debugger`: traffic a host module issues
72
+ * directly from the MAIN process. Plain requests need an explicit `report()`
73
+ * call (console fallback path); `wx.connectSocket` sockets (Node `ws`
74
+ * transport) arrive as trace events via `reportWebSocketTrace` and are
75
+ * synthesized into `Network.webSocket*` CDP messages (see websocket.ts).
74
76
  */
75
77
  import type { WebContents } from 'electron';
76
78
  import { type ConnectionRegistry, type Disposable } from '@dimina-kit/electron-deck/main';
77
79
  import { type CdpSessionBroker } from '../cdp-session/index.js';
80
+ import type { NativeWebSocketTrace } from '../../ipc/bridge-router.js';
78
81
  /**
79
82
  * Namespace prefix of every virtual requestId this forwarder injects into the
80
83
  * DevTools front-end. SINGLE SOURCE for the literal: the front-end outbound
@@ -215,6 +218,15 @@ export interface NetworkForwarder extends Disposable {
215
218
  * (e.g. a main-process direct send). Uses the console fallback sink.
216
219
  */
217
220
  report(record: NetworkRequestRecord): void;
221
+ /**
222
+ * Surface one main-process WebSocket trace event (wx.connectSocket traffic
223
+ * lives on the Node `ws` transport, invisible to any debugger) as a
224
+ * synthesized `Network.webSocket*` CDP event, injected through the SAME
225
+ * channels as forwarded simulator traffic: the global mirror unfiltered,
226
+ * plus the user-facing native sink when the socket's url classified
227
+ * user-facing. Best-effort like every injection point — never throws.
228
+ */
229
+ reportWebSocketTrace(sessionId: string, event: NativeWebSocketTrace): void;
218
230
  /**
219
231
  * Body/post-data lookups for the virtual requestIds this forwarder injected,
220
232
  * backed by the loadingFinished-time prefetch cache. Keyed by virtual id, so
@@ -6,6 +6,7 @@ import { PROBE_DEVTOOLS_API, MAX_SINGLE_DISPATCH_CHARS, CHUNK_CHARS, buildChunke
6
6
  import { createCdpSessionBroker } from '../cdp-session/index.js';
7
7
  import { isUserFacingRequest } from './user-facing.js';
8
8
  import { installGlobalNetworkBodyGate } from './global-body-gate.js';
9
+ import { WebSocketTraceSynthesizer } from './websocket.js';
9
10
  /**
10
11
  * Namespace prefix of every virtual requestId this forwarder injects into the
11
12
  * DevTools front-end. SINGLE SOURCE for the literal: the front-end outbound
@@ -553,14 +554,17 @@ export function createNetworkForwarder(bridge) {
553
554
  queueMicrotask(flushDispatch);
554
555
  }
555
556
  /** Trim the native queue to its cap, preferring to keep request-opening events.
556
- * Active requests' first events (requestWillBeSent / ...ExtraInfo) are retained
557
- * so later responseReceived/loadingFinished never become orphans in the panel;
558
- * we drop the oldest NON-opening (low-value / completion) events first. */
557
+ * Active requests' first events (requestWillBeSent / ...ExtraInfo /
558
+ * webSocketCreated) are retained so later responseReceived/loadingFinished/
559
+ * webSocket* events never become orphans in the panel (the front-end
560
+ * silently drops events for an unknown requestId); we drop the oldest
561
+ * NON-opening (low-value / completion) events first. */
559
562
  function trimQueue() {
560
563
  if (dispatchQueue.length <= MAX_DISPATCH_QUEUE)
561
564
  return;
562
565
  const isOpener = (json) => json.includes('"Network.requestWillBeSent"')
563
- || json.includes('"Network.requestWillBeSentExtraInfo"');
566
+ || json.includes('"Network.requestWillBeSentExtraInfo"')
567
+ || json.includes('"Network.webSocketCreated"');
564
568
  // First pass: drop oldest non-opener events.
565
569
  const kept = [];
566
570
  let over = dispatchQueue.length - MAX_DISPATCH_QUEUE;
@@ -1293,6 +1297,27 @@ export function createNetworkForwarder(bridge) {
1293
1297
  if (ownsBroker)
1294
1298
  broker.dispose();
1295
1299
  });
1300
+ // ── Main-process WebSocket trace → synthesized Network.webSocket* CDP ─────
1301
+ // One synthesizer for the forwarder's lifetime: its virtual-id epoch keeps
1302
+ // `dimina:ws:` ids collision-free across simulator re-attaches, and its
1303
+ // per-socket verdict cache mirrors resolveUserFacing's decide-once rule.
1304
+ const wsSynthesizer = new WebSocketTraceSynthesizer({
1305
+ epoch: String(Date.now()),
1306
+ internalOrigins: () => [bridge.getResourceServerBaseUrl?.(), bridge.getSimulatorServerBaseUrl?.()],
1307
+ });
1308
+ function reportWebSocketTrace(sessionId, event) {
1309
+ if (disposed)
1310
+ return;
1311
+ const message = wsSynthesizer.synthesize(sessionId, event);
1312
+ if (!message)
1313
+ return;
1314
+ // Global mirror first (full, unfiltered), then the user-facing native
1315
+ // sink gated on the created-time verdict — the same routing
1316
+ // wireNetworkCapture's onMessage uses for simulator traffic.
1317
+ dispatchToGlobal(message.method, message.params);
1318
+ if (message.userFacing)
1319
+ enqueueNative(message.method, message.params);
1320
+ }
1296
1321
  return {
1297
1322
  attachSimulator,
1298
1323
  detachSimulator,
@@ -1302,6 +1327,7 @@ export function createNetworkForwarder(bridge) {
1302
1327
  // report() has no observing debugger, so there's no live CDP event to push
1303
1328
  // natively — surface it via the console fallback line.
1304
1329
  report: (record) => forwardToConsole(record),
1330
+ reportWebSocketTrace,
1305
1331
  bodies: {
1306
1332
  getResponseBody: (requestId) => bodyCache.lookup(requestId),
1307
1333
  getRequestPostData: (requestId) => postDataCache.lookup(requestId),
@@ -0,0 +1,36 @@
1
+ import type { NativeWebSocketTrace } from '../../ipc/bridge-router.js';
2
+ /** One CDP message ready for `DevToolsAPI.dispatchMessage` injection. */
3
+ export interface SynthesizedWebSocketMessage {
4
+ method: string;
5
+ params: unknown;
6
+ /**
7
+ * The verdict `isUserFacingRequest` produced for this socket's url at
8
+ * `created` time, cached for the socket's whole lifetime — every later
9
+ * event reuses it (they carry no url of their own). The user-facing sink
10
+ * gates on this; the global mirror ignores it.
11
+ */
12
+ userFacing: boolean;
13
+ }
14
+ export interface WebSocketSynthesizerOptions {
15
+ /** Per-forwarder instance tag keeping virtual ids collision-free. */
16
+ epoch: string;
17
+ /**
18
+ * Origins the app itself serves (resource server / simulator shell), the
19
+ * same inputs `resolveUserFacing` feeds `isUserFacingRequest` for HTTP
20
+ * capture. Re-read at each `created`.
21
+ */
22
+ internalOrigins?: () => ReadonlyArray<string | null | undefined>;
23
+ }
24
+ export declare class WebSocketTraceSynthesizer {
25
+ private readonly options;
26
+ private readonly sockets;
27
+ private seq;
28
+ constructor(options: WebSocketSynthesizerOptions);
29
+ /**
30
+ * Map one trace event to its CDP message, or null when the event must be
31
+ * dropped (a non-`created` event for a socketId this synthesizer never saw
32
+ * created — defensive against out-of-order delivery).
33
+ */
34
+ synthesize(sessionId: string, event: NativeWebSocketTrace): SynthesizedWebSocketMessage | null;
35
+ }
36
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Synthesize Chrome DevTools Protocol `Network.webSocket*` messages from the
3
+ * main-process WebSocket trace stream (see electron-runtime
4
+ * `native-websocket/trace.ts`).
5
+ *
6
+ * Why this exists: `wx.connectSocket` sockets live on the Node `ws` transport
7
+ * in the MAIN process — no `webContents.debugger` can observe them — so the
8
+ * embedded DevTools Network tab would never show them. The trace stream gives
9
+ * one ordered fact per lifecycle moment; this module re-shapes each fact into
10
+ * the exact CDP event the DevTools front-end already renders natively
11
+ * (`webSocketCreated` creates the row, handshake events fill it, frames land
12
+ * in the Messages view, `webSocketClosed` settles it).
13
+ *
14
+ * Ordering contract this module relies on (guaranteed by the trace layer):
15
+ * `created` strictly precedes every other event for its socketId, and exactly
16
+ * one `closed` terminates each created socket. The front-end silently drops
17
+ * frame/closed events for an unknown requestId, so this module mirrors that
18
+ * discipline defensively: any non-created event for an unknown socketId is
19
+ * dropped rather than synthesized.
20
+ *
21
+ * Pure and self-contained (no Electron imports) so it is unit-testable.
22
+ */
23
+ import { isUserFacingRequest } from './user-facing.js';
24
+ export class WebSocketTraceSynthesizer {
25
+ options;
26
+ sockets = new Map();
27
+ seq = 0;
28
+ constructor(options) {
29
+ this.options = options;
30
+ }
31
+ /**
32
+ * Map one trace event to its CDP message, or null when the event must be
33
+ * dropped (a non-`created` event for a socketId this synthesizer never saw
34
+ * created — defensive against out-of-order delivery).
35
+ */
36
+ synthesize(sessionId, event) {
37
+ const key = `${sessionId} ${event.socketId}`;
38
+ if (event.type === 'created') {
39
+ const requestId = `dimina:ws:${this.options.epoch}:${this.seq++}`;
40
+ const userFacing = isUserFacingRequest(event.url, this.options.internalOrigins?.());
41
+ this.sockets.set(key, { requestId, userFacing });
42
+ return {
43
+ method: 'Network.webSocketCreated',
44
+ params: { requestId, url: event.url },
45
+ userFacing,
46
+ };
47
+ }
48
+ const state = this.sockets.get(key);
49
+ if (!state)
50
+ return null;
51
+ const { requestId, userFacing } = state;
52
+ const timestamp = event.time / 1000;
53
+ switch (event.type) {
54
+ case 'handshake-request':
55
+ return {
56
+ method: 'Network.webSocketWillSendHandshakeRequest',
57
+ params: {
58
+ requestId,
59
+ timestamp,
60
+ wallTime: timestamp,
61
+ request: { headers: event.headers },
62
+ },
63
+ userFacing,
64
+ };
65
+ case 'handshake-response':
66
+ return {
67
+ method: 'Network.webSocketHandshakeResponseReceived',
68
+ params: {
69
+ requestId,
70
+ timestamp,
71
+ wallTime: timestamp,
72
+ response: {
73
+ status: event.status,
74
+ statusText: event.statusText,
75
+ headers: event.headers,
76
+ },
77
+ },
78
+ userFacing,
79
+ };
80
+ case 'frame-sent':
81
+ return {
82
+ method: 'Network.webSocketFrameSent',
83
+ params: { requestId, timestamp, response: frame(event, true) },
84
+ userFacing,
85
+ };
86
+ case 'frame-received':
87
+ return {
88
+ method: 'Network.webSocketFrameReceived',
89
+ params: { requestId, timestamp, response: frame(event, false) },
90
+ userFacing,
91
+ };
92
+ case 'frame-error':
93
+ return {
94
+ method: 'Network.webSocketFrameError',
95
+ params: { requestId, timestamp, errorMessage: event.errorMessage },
96
+ userFacing,
97
+ };
98
+ case 'closed':
99
+ // Terminal: release the id mapping so a long session can't grow it.
100
+ this.sockets.delete(key);
101
+ return {
102
+ method: 'Network.webSocketClosed',
103
+ params: { requestId, timestamp },
104
+ userFacing,
105
+ };
106
+ }
107
+ }
108
+ }
109
+ /** CDP `Network.WebSocketFrame`: client-sent frames are masked, received are not. */
110
+ function frame(event, mask) {
111
+ const result = {
112
+ opcode: event.opcode,
113
+ mask,
114
+ payloadData: event.payloadData,
115
+ };
116
+ if (event.base64Encoded)
117
+ result.base64Encoded = true;
118
+ return result;
119
+ }
120
+ //# sourceMappingURL=websocket.js.map
@@ -1,197 +1,2 @@
1
- import { BrowserWindow } from 'electron';
2
- /**
3
- * ServiceHostPool — main-process pre-warm pool for simulator host windows.
4
- *
5
- * Design source: `packages/devtools/docs/prewarm-webview.md`. WIRED into
6
- * native-host via `bridge-router.handleSpawn` (acquire) + `disposeAppSession`
7
- * (release / releaseDestroyed), opt-in behind `DIMINA_PREWARM_POOL_SIZE`
8
- * (default OFF). Only the SERVICE-HOST window is pooled; the simulator content
9
- * WebContentsView (`view-manager.attachNativeSimulator`) is created fresh per
10
- * attach. (A `<webview>`-tag arch is not poolable — Electron can't reparent a
11
- * pre-warmed WebContents into a `<webview>`.)
12
- *
13
- * Lifecycle of a pool entry:
14
- * warming — BrowserWindow constructed, page loading
15
- * ready — load settled; can be handed to a caller immediately
16
- * in-use — acquired by a caller (caller owns the window until release)
17
- * resetting — released; navigated back to a blank page, then storage cleared
18
- * disposing — being torn down; removed from the pool
19
- *
20
- * ── Phase-3 blockers, both RESOLVED in the wiring (kept here for context) ──
21
- * 1. Shared-session storage wipe — the service host shares the
22
- * `persist:simulator` partition with live projects, so clearing it on reset
23
- * would wipe other projects. Resolved by `ServiceHostSpec.clearStorageOnReset`:
24
- * `serviceHostSpec()` sets it `false`, so reset only navigates the
25
- * window to blank (resetting the JS realm) and never clears the shared
26
- * session; cross-spawn isolation rides on appId-namespacing + fresh nav,
27
- * matching the already-shared simulator `<webview>` behavior.
28
- * 2. Warming vs preload contract — warming loads `about:blank` (no bridgeId).
29
- * `src/service-host/preload.cjs` now idles (early `return`) when `bridgeId`
30
- * is absent instead of throwing, so a warmed window survives until the real
31
- * spawn navigation re-runs the preload with a bridgeId.
32
- *
33
- * ── Window-death observation ───────────────────────────────────────────────
34
- * A pooled/in-use window can die three ways: a render crash
35
- * (`render-process-gone`), a graceful close (`'closed'`), or the owner closing
36
- * an acquired window (reported via `releaseDestroyed`). All three funnel through
37
- * `reclaim()`: a POOLED death frees a spare → refill toward target; an IN-USE
38
- * death is the owner's window → drop the bookkeeping WITHOUT destroying it from
39
- * under them, and do not refill (the spare count is unaffected).
40
- */
41
- /** Entry state machine (see class doc). */
42
- export type EntryState = 'warming' | 'ready' | 'in-use' | 'resetting' | 'disposing';
43
- /**
44
- * A spec fully determines whether a warm window can serve a caller. Reuse is
45
- * keyed on `preloadPath`: a different preload means a different runtime surface,
46
- * so the pool will not hand a mismatched entry to a caller.
47
- *
48
- * The process-model flags (`contextIsolation` / `sandbox` / `nodeIntegration`)
49
- * default to the simulator service-host contract (all `false`, mirroring
50
- * `createServiceHostWindow`) so a warmed window is a faithful stand-in for the
51
- * window the caller will actually use. They are part of the spec — not
52
- * hardcoded — so a caller with a different runtime can override them.
53
- */
54
- export interface ServiceHostSpec {
55
- /** Session partition for the window. Opaque to the pool (see KNOWN BLOCKER 1). */
56
- partition: string;
57
- /** Absolute path to the preload bundle. The reuse key. */
58
- preloadPath: string;
59
- /** Initial window size (affects first layout). */
60
- size?: {
61
- width: number;
62
- height: number;
63
- };
64
- /** Open DevTools on the window (dev only). */
65
- devTools?: boolean;
66
- /** webPreferences.contextIsolation. Default false (service-host contract). */
67
- contextIsolation?: boolean;
68
- /** webPreferences.sandbox. Default false (service-host contract). */
69
- sandbox?: boolean;
70
- /** webPreferences.nodeIntegration. Default false (service-host contract). */
71
- nodeIntegration?: boolean;
72
- /**
73
- * Whether `release` clears this window's session storage. Default `true`.
74
- * Set `false` when the window shares a session with live consumers (e.g. the
75
- * service host on the shared `persist:simulator` partition) — clearing there
76
- * would wipe other projects' storage. With `false`, reset still navigates the
77
- * window to a blank page (tearing down the old JS realm); cross-spawn state is
78
- * handled by appId-namespacing + the fresh navigation, matching the
79
- * already-shared, never-cleared simulator `<webview>` behavior.
80
- */
81
- clearStorageOnReset?: boolean;
82
- }
83
- /** Snapshot of pool occupancy, for dev panels / IPC `prewarm:status`. */
84
- export interface ServiceHostPoolStats {
85
- total: number;
86
- ready: number;
87
- inUse: number;
88
- warming: number;
89
- resetting: number;
90
- spec: ServiceHostSpec | null;
91
- }
92
- export interface ServiceHostPoolInitOptions {
93
- /** Number of entries to keep warm. Default target after init. */
94
- defaultPoolSize: number;
95
- /** Spec used to warm the initial entries. */
96
- defaultSpec: ServiceHostSpec;
97
- /** Hard ceiling on pooled entries. Clamped to ≤ 4 (see prewarm-webview.md). Default 3. */
98
- maxPoolSize?: number;
99
- }
100
- export declare class ServiceHostPool {
101
- private readonly entries;
102
- private currentSpec;
103
- private targetSize;
104
- private maxPoolSize;
105
- private nextId;
106
- private disposed;
107
- /**
108
- * Warm `defaultPoolSize` entries with `defaultSpec`. Normally resolves once the
109
- * pool has reached `defaultPoolSize` ready entries — even if a warming window
110
- * crashes mid-init and is refilled. NOTE: `warmUpToTarget` caps its refill loop
111
- * with a bounded guard, so under SUSTAINED warm crashes `init` resolves
112
- * best-effort (possibly under-filled) rather than spinning forever — treat
113
- * `getStats().ready` as advisory after init, not a hard invariant.
114
- */
115
- init(opts: ServiceHostPoolInitOptions): Promise<void>;
116
- /**
117
- * Hand a ready window matching `spec` to the caller. If none is ready, fall
118
- * back to synchronously constructing a fresh window — never blocking on warm
119
- * (see prewarm-webview.md's invariants section). Fallback windows carry `entryId === null` and
120
- * are destroyed (not pooled) on release.
121
- *
122
- * A spec change (different `preloadPath`) tears down all *pooled* entries and
123
- * re-targets the pool to the new spec; in-use entries are left to their owners
124
- * and disposed when returned (see `release`).
125
- */
126
- acquire(spec: ServiceHostSpec): Promise<{
127
- win: BrowserWindow;
128
- entryId: string | null;
129
- }>;
130
- /**
131
- * Return a window. Fallback windows (`entryId === null`) and entries whose
132
- * spec no longer matches the current pool spec are torn down. Otherwise the
133
- * window is reset (navigated to blank, then storage cleared) and re-pooled as
134
- * `ready`, unless that would exceed `maxPoolSize`.
135
- */
136
- release(entryId: string | null, win: BrowserWindow): Promise<void>;
137
- /**
138
- * The owner of an acquired window reports it died externally (graceful close /
139
- * crash) — reclaim its in-use slot so it isn't leaked in `entries` forever
140
- * (bridge-router calls this on the `serviceAlreadyClosed` teardown path, where
141
- * `release` is intentionally skipped). No-op for an unknown id; idempotent;
142
- * never touches the (already-gone) window.
143
- */
144
- releaseDestroyed(entryId: string | null): void;
145
- /**
146
- * Re-target the pool. `target` is clamped to `[0, maxPoolSize]`. Pooled
147
- * entries beyond the target are disposed OLDEST-FIRST (see prewarm-webview.md); in-use
148
- * entries are untouched. Does not warm new entries up (use `init` / refill).
149
- */
150
- resize(target: number): void;
151
- /** Occupancy snapshot. */
152
- getStats(): ServiceHostPoolStats;
153
- /** Tear down every entry and stop accepting work. */
154
- dispose(): Promise<void>;
155
- private matches;
156
- /** ready + warming + resetting entries are "pooled" (owned by the pool). */
157
- private isWarm;
158
- private warmCount;
159
- private readyCount;
160
- /**
161
- * Drive the pool to `targetSize` ready entries and resolve only then. Loops so
162
- * a crash that drops a warming entry mid-init (which refills asynchronously
163
- * via `onGone`) is awaited too, instead of resolving on a stale snapshot. A
164
- * generous iteration guard prevents an unbounded spin if windows keep dying.
165
- */
166
- private warmUpToTarget;
167
- private createWindow;
168
- private loadBlank;
169
- /** Construct + load a fresh pooled entry, registering crash recovery. */
170
- private warmOne;
171
- /**
172
- * Reset a released window back to a clean, blank state. Order matters
173
- * (see prewarm-webview.md's state-machine and reset-ordering sections): navigate to blank FIRST so the old document
174
- * stops running and in-flight requests are aborted, THEN clear storage — so a
175
- * still-live old page cannot re-populate storage after the clear.
176
- */
177
- private reset;
178
- /** A spec change tears down pooled entries and re-targets the pool. */
179
- private onSpecChange;
180
- /**
181
- * Reclaim an entry whose window died externally (render crash, graceful close,
182
- * or an owner-reported destruction). A POOLED death frees a spare → refill
183
- * toward target; an IN-USE death is the caller's window → drop the bookkeeping
184
- * WITHOUT destroying it from under them, and do not refill (the spare count is
185
- * unaffected). `destroyIfPooled` destroys the husk for a pooled render crash
186
- * (the BrowserWindow object can outlive a dead render process); skip it for a
187
- * `'closed'`/owner-reported window that is already gone. No-op if the entry was
188
- * already disposed by us (disposeEntry deletes before its own destroy fires
189
- * `'closed'`, so this finds nothing).
190
- */
191
- private reclaim;
192
- /** A pooled window's render process died — reclaim + refill (destroy the husk). */
193
- private onGone;
194
- private disposeEntry;
195
- private destroyWindow;
196
- }
1
+ export * from '@dimina-kit/electron-runtime/main/service-host-pool';
197
2
  //# sourceMappingURL=pool.d.ts.map