@dimina-kit/devtools 0.4.0-dev.20260907055341 → 0.5.0-dev.20260908120530

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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/main/app/window-runtime-services.d.ts +12 -12
  3. package/dist/main/app/window-runtime-services.js +34 -15
  4. package/dist/main/index.bundle.js +6307 -6050
  5. package/dist/main/services/elements-forward/index.d.ts +2 -2
  6. package/dist/main/services/elements-forward/index.js +6 -6
  7. package/dist/main/services/network-forward/body-cache.d.ts +2 -0
  8. package/dist/main/services/network-forward/body-cache.js +4 -0
  9. package/dist/main/services/network-forward/global-body-gate.d.ts +1 -1
  10. package/dist/main/services/network-forward/global-body-gate.js +4 -4
  11. package/dist/main/services/network-forward/http.d.ts +47 -0
  12. package/dist/main/services/network-forward/http.js +162 -0
  13. package/dist/main/services/network-forward/index.d.ts +19 -13
  14. package/dist/main/services/network-forward/index.js +220 -134
  15. package/dist/main/services/network-forward/request-ids.d.ts +5 -0
  16. package/dist/main/services/network-forward/request-ids.js +5 -0
  17. package/dist/main/services/safe-area/index.d.ts +7 -9
  18. package/dist/main/services/safe-area/index.js +7 -14
  19. package/dist/main/services/views/native-simulator-view.js +7 -8
  20. package/dist/main/services/workbench-context.d.ts +3 -3
  21. package/dist/main/services/workbench-context.js +7 -1
  22. package/dist/native-host/render/render.js +525 -482
  23. package/dist/native-host/service/service.js +2 -2
  24. package/dist/preload/shared/api-compat.js +63 -41
  25. package/dist/preload/windows/main.cjs +15 -1
  26. package/dist/preload/windows/main.cjs.map +2 -2
  27. package/dist/preload/windows/simulator.cjs +55 -134
  28. package/dist/preload/windows/simulator.cjs.map +4 -4
  29. package/dist/preload/windows/simulator.js +55 -134
  30. package/dist/renderer/assets/workbench-C0Jkdlps.js +5 -0
  31. package/dist/renderer/entries/workbench/index.html +1 -1
  32. package/dist/shared/types.d.ts +2 -0
  33. package/dist/simulator/assets/device-shell-DucLepkm.js +431 -0
  34. package/dist/simulator/assets/simulator-B4rbcOVt.js +10 -0
  35. package/dist/simulator/assets/simulator-ui-i3GCc7YJ.js +2 -0
  36. package/dist/simulator/simulator.html +2 -2
  37. package/package.json +12 -12
  38. package/dist/renderer/assets/workbench-BLJ0wKsg.js +0 -5
  39. package/dist/shared/request-core.d.ts +0 -2
  40. package/dist/shared/request-core.js +0 -2
  41. package/dist/simulator/assets/device-shell-pxnZZp10.js +0 -431
  42. package/dist/simulator/assets/simulator-Dwp7lnvu.js +0 -10
  43. package/dist/simulator/assets/simulator-ui-B2ddJB1R.js +0 -2
@@ -1,20 +1,15 @@
1
- import { SyncDisposableRegistry, toDisposable } from '@dimina-kit/electron-deck/main';
2
- import { isFrontendSettled } from '../views/inject-when-ready.js';
3
- import { packDispatchBatch } from './dispatch-batch.js';
4
- import { PrefetchCache, DEFAULT_PER_ENTRY_MAX_CHARS } from './body-cache.js';
5
- import { PROBE_DEVTOOLS_API, MAX_SINGLE_DISPATCH_CHARS, CHUNK_CHARS, buildChunkedDispatchScript } from './frontend-dispatch.js';
6
- import { createCdpSessionBroker } from '../cdp-session/index.js';
7
- import { isUserFacingRequest } from './user-facing.js';
8
- import { installGlobalNetworkBodyGate } from './global-body-gate.js';
9
- import { WebSocketTraceSynthesizer } from './websocket.js';
10
- /**
11
- * Namespace prefix of every virtual requestId this forwarder injects into the
12
- * DevTools front-end. SINGLE SOURCE for the literal: the front-end outbound
13
- * gate (elements-forward) keys its `Network.getResponseBody` /
14
- * `Network.getRequestPostData` interception on this exact prefix, so the two
15
- * modules can never drift apart on what counts as "one of ours".
16
- */
17
- export const VIRTUAL_REQUEST_ID_PREFIX = 'dimina:sim:';
1
+ import { SyncDisposableRegistry, toDisposable, } from "@dimina-kit/electron-deck/main";
2
+ import { isFrontendSettled } from "../views/inject-when-ready.js";
3
+ import { packDispatchBatch } from "./dispatch-batch.js";
4
+ import { PrefetchCache, DEFAULT_PER_ENTRY_MAX_CHARS } from "./body-cache.js";
5
+ import { PROBE_DEVTOOLS_API, MAX_SINGLE_DISPATCH_CHARS, CHUNK_CHARS, buildChunkedDispatchScript, } from "./frontend-dispatch.js";
6
+ import { createCdpSessionBroker, } from "../cdp-session/index.js";
7
+ import { isUserFacingRequest } from "./user-facing.js";
8
+ import { installGlobalNetworkBodyGate } from "./global-body-gate.js";
9
+ import { WebSocketTraceSynthesizer } from "./websocket.js";
10
+ import { RequestTraceSynthesizer } from "./http.js";
11
+ import { VIRTUAL_REQUEST_ID_PREFIX } from "./request-ids.js";
12
+ export { VIRTUAL_REQUEST_ID_PREFIX } from "./request-ids.js";
18
13
  // ── requestId namespacing (pure, testable) ──────────────────────────────────
19
14
  /**
20
15
  * CDP events whose `params.requestId` we rewrite into the virtual namespace.
@@ -24,32 +19,32 @@ export const VIRTUAL_REQUEST_ID_PREFIX = 'dimina:sim:';
24
19
  * included for that reason (rewrite-only today, not forwarded).
25
20
  */
26
21
  export const REWRITE_REQUEST_ID_METHODS = new Set([
27
- 'Network.requestWillBeSent',
28
- 'Network.requestWillBeSentExtraInfo',
29
- 'Network.responseReceived',
30
- 'Network.responseReceivedExtraInfo',
31
- 'Network.dataReceived',
32
- 'Network.loadingFinished',
33
- 'Network.loadingFailed',
34
- 'Network.requestServedFromCache',
35
- 'Network.resourceChangedPriority',
22
+ "Network.requestWillBeSent",
23
+ "Network.requestWillBeSentExtraInfo",
24
+ "Network.responseReceived",
25
+ "Network.responseReceivedExtraInfo",
26
+ "Network.dataReceived",
27
+ "Network.loadingFinished",
28
+ "Network.loadingFailed",
29
+ "Network.requestServedFromCache",
30
+ "Network.resourceChangedPriority",
36
31
  // 二期 (when WebSocket/EventSource forwarding lands): Network.webSocket*,
37
32
  // Network.eventSourceMessageReceived — keep ids namespaced once added here.
38
33
  ]);
39
34
  /** The Network.* methods this one-shot pass forwards to the front-end. */
40
35
  export const FORWARDED_METHODS = new Set([
41
- 'Network.requestWillBeSent',
42
- 'Network.requestWillBeSentExtraInfo',
43
- 'Network.responseReceived',
44
- 'Network.responseReceivedExtraInfo',
45
- 'Network.loadingFinished',
46
- 'Network.loadingFailed',
36
+ "Network.requestWillBeSent",
37
+ "Network.requestWillBeSentExtraInfo",
38
+ "Network.responseReceived",
39
+ "Network.responseReceivedExtraInfo",
40
+ "Network.loadingFinished",
41
+ "Network.loadingFailed",
47
42
  // `dataReceived` deliberately omitted (二期) — see header.
48
43
  ]);
49
44
  /** Methods that mark a request finished, so its id mapping can age out. */
50
45
  const TERMINAL_METHODS = new Set([
51
- 'Network.loadingFinished',
52
- 'Network.loadingFailed',
46
+ "Network.loadingFinished",
47
+ "Network.loadingFailed",
53
48
  ]);
54
49
  /**
55
50
  * Bounded, TTL'd raw→virtual requestId map with an active/retired split.
@@ -157,7 +152,7 @@ export function rewriteRequestId(method, params, ns) {
157
152
  if (!REWRITE_REQUEST_ID_METHODS.has(method))
158
153
  return { method, params };
159
154
  const p = params;
160
- if (!p || typeof p.requestId !== 'string')
155
+ if (!p || typeof p.requestId !== "string")
161
156
  return { method, params };
162
157
  const virtual = ns.resolve(p.requestId);
163
158
  if (TERMINAL_METHODS.has(method))
@@ -174,12 +169,12 @@ export function rewriteRequestId(method, params, ns) {
174
169
  */
175
170
  function buildDispatchScript(messages) {
176
171
  const arr = JSON.stringify(messages);
177
- return `(()=>{try{`
178
- + `if(!${PROBE_DEVTOOLS_API})return false;`
179
- + `const ms=JSON.parse(${JSON.stringify(arr)});`
180
- + `for(const m of ms){try{window.DevToolsAPI.dispatchMessage(m)}catch(_){}}`
181
- + `return true;`
182
- + `}catch(_){return false}})()`;
172
+ return (`(()=>{try{` +
173
+ `if(!${PROBE_DEVTOOLS_API})return false;` +
174
+ `const ms=JSON.parse(${JSON.stringify(arr)});` +
175
+ `for(const m of ms){try{window.DevToolsAPI.dispatchMessage(m)}catch(_){}}` +
176
+ `return true;` +
177
+ `}catch(_){return false}})()`);
183
178
  }
184
179
  /**
185
180
  * Upper bound on the COMBINED size (in UTF-16 chars) of the messages packed into
@@ -197,13 +192,13 @@ const MAX_BATCH_CHARS = 512 * 1024;
197
192
  */
198
193
  function buildForwardScript(record) {
199
194
  const json = JSON.stringify(record);
200
- return `(()=>{try{const r=JSON.parse(${JSON.stringify(json)});`
201
- + `const tag='[网络]['+r.source+']';`
202
- + `const head=r.method+' '+(r.status||'-')+' '+r.url;`
203
- + `if(r.errorText){console.warn(tag,head,r.errorText)}`
204
- + `else if(r.status>=400){console.warn(tag,head)}`
205
- + `else{console.log(tag,head)}`
206
- + `}catch(_){}})()`;
195
+ return (`(()=>{try{const r=JSON.parse(${JSON.stringify(json)});` +
196
+ `const tag='[网络]['+r.source+']';` +
197
+ `const head=r.method+' '+(r.status||'-')+' '+r.url;` +
198
+ `if(r.errorText){console.warn(tag,head,r.errorText)}` +
199
+ `else if(r.status>=400){console.warn(tag,head)}` +
200
+ `else{console.log(tag,head)}` +
201
+ `}catch(_){}})()`);
207
202
  }
208
203
  /** Cap so a long session can't grow the fallback pending map unboundedly. */
209
204
  const MAX_PENDING = 1000;
@@ -261,11 +256,19 @@ export function createNetworkForwarder(bridge) {
261
256
  return;
262
257
  pendingPrefetchCount++;
263
258
  let released = false;
264
- const release = () => { if (!released) {
265
- released = true;
266
- pendingPrefetchCount--;
267
- } };
268
- const started = cache.prime(id, () => fetch().then((v) => { release(); return v; }, (err) => { release(); throw err; }));
259
+ const release = () => {
260
+ if (!released) {
261
+ released = true;
262
+ pendingPrefetchCount--;
263
+ }
264
+ };
265
+ const started = cache.prime(id, () => fetch().then((v) => {
266
+ release();
267
+ return v;
268
+ }, (err) => {
269
+ release();
270
+ throw err;
271
+ }));
269
272
  if (!started)
270
273
  release();
271
274
  }
@@ -300,7 +303,8 @@ export function createNetworkForwarder(bridge) {
300
303
  // Own (and dispose on this forwarder's own dispose()) a private broker only
301
304
  // when the caller didn't supply a shared one.
302
305
  const ownsBroker = !bridge.broker;
303
- const broker = bridge.broker ?? createCdpSessionBroker({ connections: bridge.connections });
306
+ const broker = bridge.broker ??
307
+ createCdpSessionBroker({ connections: bridge.connections });
304
308
  // The DevTools front-end host wc (primary sink), set by the ViewManager.
305
309
  let devtoolsWc = null;
306
310
  // Teardown for the wc 'destroyed' watcher on the current host (clears the host).
@@ -380,7 +384,9 @@ export function createNetworkForwarder(bridge) {
380
384
  for (const msg of chunked)
381
385
  dispatchChunked(target, msg);
382
386
  if (batch.length > 0) {
383
- target.executeJavaScript(buildDispatchScript(batch), true).catch(() => { });
387
+ target.executeJavaScript(buildDispatchScript(batch), true).catch(() => {
388
+ /* best-effort */
389
+ });
384
390
  }
385
391
  }
386
392
  globalPendingOverflowWarned = false;
@@ -418,18 +424,20 @@ export function createNetworkForwarder(bridge) {
418
424
  clearGlobalPending();
419
425
  };
420
426
  const reg = bridge.connections;
421
- if (reg && typeof target.once === 'function') {
427
+ if (reg && typeof target.once === "function") {
422
428
  const owned = reg.acquire(target).own(onHostDestroyed);
423
429
  globalDevtoolsHostDisposable = toDisposable(() => owned.dispose());
424
430
  }
425
431
  else {
426
- if (typeof target.once === 'function')
427
- target.once('destroyed', onHostDestroyed);
432
+ if (typeof target.once === "function")
433
+ target.once("destroyed", onHostDestroyed);
428
434
  globalDevtoolsHostDisposable = toDisposable(() => {
429
435
  try {
430
- target.removeListener?.('destroyed', onHostDestroyed);
436
+ target.removeListener?.("destroyed", onHostDestroyed);
437
+ }
438
+ catch {
439
+ /* gone */
431
440
  }
432
- catch { /* gone */ }
433
441
  });
434
442
  }
435
443
  globalBodyGateStop = installGlobalNetworkBodyGate(target, {
@@ -466,10 +474,14 @@ export function createNetworkForwarder(bridge) {
466
474
  dispatchChunked(globalDevtoolsWc, json);
467
475
  return;
468
476
  }
469
- globalDevtoolsWc.executeJavaScript(buildDispatchScript([json]), true).catch(() => { });
477
+ globalDevtoolsWc
478
+ .executeJavaScript(buildDispatchScript([json]), true)
479
+ .catch(() => {
480
+ /* best-effort */
481
+ });
470
482
  }
471
483
  // ── Native-sink state machine ─────────────────────────────────────────────
472
- let sink = 'idle';
484
+ let sink = "idle";
473
485
  // Buffered completed-request records while 'probing' — flushed to console if we
474
486
  // degrade, dropped if we go ready (so a request shows in exactly one sink).
475
487
  let probeConsoleBuffer = [];
@@ -497,22 +509,22 @@ export function createNetworkForwarder(bridge) {
497
509
  }
498
510
  /** Enter the 'probing' state and arm the ready-timeout (idempotent). */
499
511
  function beginProbing() {
500
- if (sink === 'probing')
512
+ if (sink === "probing")
501
513
  return;
502
- sink = 'probing';
514
+ sink = "probing";
503
515
  probeDeadline = Date.now() + DEVTOOLS_READY_TIMEOUT_MS;
504
516
  if (readyTimeoutTimer)
505
517
  clearTimeout(readyTimeoutTimer);
506
518
  readyTimeoutTimer = setTimeout(() => {
507
519
  readyTimeoutTimer = null;
508
520
  // Still not ready after the grace period → abandon native for this host.
509
- if (sink === 'probing')
521
+ if (sink === "probing")
510
522
  degradeToConsole();
511
523
  }, DEVTOOLS_READY_TIMEOUT_MS);
512
524
  }
513
525
  /** Native path confirmed live: console buffer is moot, drop it. */
514
526
  function markReady() {
515
- sink = 'ready';
527
+ sink = "ready";
516
528
  probeDeadline = null;
517
529
  if (readyTimeoutTimer) {
518
530
  clearTimeout(readyTimeoutTimer);
@@ -531,7 +543,7 @@ export function createNetworkForwarder(bridge) {
531
543
  * and flush the buffered completed-request records to the console sink.
532
544
  */
533
545
  function degradeToConsole() {
534
- sink = 'degraded';
546
+ sink = "degraded";
535
547
  probeDeadline = null;
536
548
  dispatchQueue = [];
537
549
  if (readyTimeoutTimer) {
@@ -562,9 +574,9 @@ export function createNetworkForwarder(bridge) {
562
574
  function trimQueue() {
563
575
  if (dispatchQueue.length <= MAX_DISPATCH_QUEUE)
564
576
  return;
565
- const isOpener = (json) => json.includes('"Network.requestWillBeSent"')
566
- || json.includes('"Network.requestWillBeSentExtraInfo"')
567
- || json.includes('"Network.webSocketCreated"');
577
+ const isOpener = (json) => json.includes('"Network.requestWillBeSent"') ||
578
+ json.includes('"Network.requestWillBeSentExtraInfo"') ||
579
+ json.includes('"Network.webSocketCreated"');
568
580
  // First pass: drop oldest non-opener events.
569
581
  const kept = [];
570
582
  let over = dispatchQueue.length - MAX_DISPATCH_QUEUE;
@@ -587,7 +599,7 @@ export function createNetworkForwarder(bridge) {
587
599
  flushScheduled = false;
588
600
  if (dispatchQueue.length === 0)
589
601
  return;
590
- if (sink === 'degraded') {
602
+ if (sink === "degraded") {
591
603
  dispatchQueue = [];
592
604
  return;
593
605
  }
@@ -610,7 +622,7 @@ export function createNetworkForwarder(bridge) {
610
622
  scheduleReadyRetry();
611
623
  return;
612
624
  }
613
- if (sink === 'idle')
625
+ if (sink === "idle")
614
626
  beginProbing();
615
627
  // Pack greedily up to MAX_BATCH_CHARS so one executeJavaScript stays sized;
616
628
  // oversized single messages go via the chunked transport (see packDispatchBatch).
@@ -639,24 +651,26 @@ export function createNetworkForwarder(bridge) {
639
651
  dispatchQueue = remaining;
640
652
  if (remaining.length > 0)
641
653
  scheduleFlush();
642
- wc.executeJavaScript(script, true).then((ok) => {
654
+ wc.executeJavaScript(script, true)
655
+ .then((ok) => {
643
656
  if (ok === true) {
644
- if (sink !== 'ready')
657
+ if (sink !== "ready")
645
658
  markReady();
646
659
  return;
647
660
  }
648
661
  // API not present yet — front-end still booting. Re-queue this batch ahead
649
662
  // of any newer events and poll until ready (or the timeout degrades us).
650
- if (sink === 'degraded')
663
+ if (sink === "degraded")
651
664
  return;
652
665
  dispatchQueue = batch.concat(dispatchQueue);
653
666
  trimQueue();
654
667
  scheduleReadyRetry();
655
- }).catch(() => {
668
+ })
669
+ .catch(() => {
656
670
  // wc navigated / torn down mid-call, OR the script overflowed IPC. Re-queue
657
671
  // (bounded) and let the next flush re-resolve the host. Best-effort; the
658
672
  // ready-timeout still governs giving up. Backoff is via the retry timer.
659
- if (sink === 'degraded')
673
+ if (sink === "degraded")
660
674
  return;
661
675
  dispatchQueue = batch.concat(dispatchQueue);
662
676
  trimQueue();
@@ -676,10 +690,12 @@ export function createNetworkForwarder(bridge) {
676
690
  catch {
677
691
  return;
678
692
  }
679
- wc.executeJavaScript(script, true).catch(() => { });
693
+ wc.executeJavaScript(script, true).catch(() => {
694
+ /* best-effort */
695
+ });
680
696
  }
681
697
  function scheduleReadyRetry() {
682
- if (readyRetryTimer || sink === 'ready' || sink === 'degraded')
698
+ if (readyRetryTimer || sink === "ready" || sink === "degraded")
683
699
  return;
684
700
  // Self-terminate on the same deadline readyTimeoutTimer enforces, instead of
685
701
  // trusting that timer to win a same-instant race against this one (see the
@@ -690,7 +706,7 @@ export function createNetworkForwarder(bridge) {
690
706
  }
691
707
  readyRetryTimer = setTimeout(() => {
692
708
  readyRetryTimer = null;
693
- if (sink === 'degraded')
709
+ if (sink === "degraded")
694
710
  return;
695
711
  if (probeDeadline !== null && Date.now() >= probeDeadline) {
696
712
  degradeToConsole();
@@ -707,9 +723,9 @@ export function createNetworkForwarder(bridge) {
707
723
  // requests — queueing them would let them double-render if a host later
708
724
  // arrives / swaps in. resolveDevtoolsWc() guards the idle case where a host
709
725
  // is set on the bridge but applyDevtoolsHost hasn't run yet.
710
- if (sink === 'degraded')
726
+ if (sink === "degraded")
711
727
  return;
712
- if (sink === 'idle' && !resolveDevtoolsWc())
728
+ if (sink === "idle" && !resolveDevtoolsWc())
713
729
  return;
714
730
  let json;
715
731
  try {
@@ -759,7 +775,9 @@ export function createNetworkForwarder(bridge) {
759
775
  try {
760
776
  ad.disposeAll();
761
777
  }
762
- catch { /* best-effort teardown */ }
778
+ catch {
779
+ /* best-effort teardown */
780
+ }
763
781
  }
764
782
  lease?.dispose();
765
783
  // Drop any queued-but-unflushed messages so a new attach starts clean, and
@@ -768,7 +786,7 @@ export function createNetworkForwarder(bridge) {
768
786
  probeConsoleBuffer = [];
769
787
  // A new simulator attach restarts the native sink: 'idle' until the next
770
788
  // event re-probes the (possibly already-set) host.
771
- sink = 'idle';
789
+ sink = "idle";
772
790
  probeDeadline = null;
773
791
  if (readyRetryTimer) {
774
792
  clearTimeout(readyRetryTimer);
@@ -819,7 +837,7 @@ export function createNetworkForwarder(bridge) {
819
837
  // — global-body-gate answers Network.getResponseBody/getRequestPostData
820
838
  // from this same cache, and it must not 404 just because the
821
839
  // user-facing sink happens to be closed/degraded.
822
- const userSinkUsable = sink !== 'degraded' && !(sink === 'idle' && !resolveDevtoolsWc());
840
+ const userSinkUsable = sink !== "degraded" && !(sink === "idle" && !resolveDevtoolsWc());
823
841
  const globalUsable = globalDevtoolsWc !== null && !globalDevtoolsWc.isDestroyed();
824
842
  if (!userSinkUsable && !globalUsable)
825
843
  return;
@@ -831,23 +849,24 @@ export function createNetworkForwarder(bridge) {
831
849
  // is a best-effort heuristic (encodedDataLength is the ON-THE-WIRE size;
832
850
  // a decompressed body can be larger), not a hard guarantee — the
833
851
  // concurrency cap below is what actually bounds worst-case memory.
834
- const knownOversized = typeof encodedDataLength === 'number' && encodedDataLength > DEFAULT_PER_ENTRY_MAX_CHARS;
852
+ const knownOversized = typeof encodedDataLength === "number" &&
853
+ encodedDataLength > DEFAULT_PER_ENTRY_MAX_CHARS;
835
854
  if (!knownOversized) {
836
855
  primeWithAdmission(bodyCache, virtualId, async () => {
837
- const raw = await wc.debugger.sendCommand('Network.getResponseBody', { requestId: rawId });
856
+ const raw = await wc.debugger.sendCommand("Network.getResponseBody", { requestId: rawId });
838
857
  const r = raw;
839
- if (!r || typeof r.body !== 'string')
840
- throw new Error('response body unavailable');
858
+ if (!r || typeof r.body !== "string")
859
+ throw new Error("response body unavailable");
841
860
  return { body: r.body, base64Encoded: r.base64Encoded === true };
842
861
  });
843
862
  }
844
863
  if (!postDataWanted.delete(rawId))
845
864
  return;
846
865
  primeWithAdmission(postDataCache, virtualId, async () => {
847
- const raw = await wc.debugger.sendCommand('Network.getRequestPostData', { requestId: rawId });
866
+ const raw = await wc.debugger.sendCommand("Network.getRequestPostData", { requestId: rawId });
848
867
  const r = raw;
849
- if (!r || typeof r.postData !== 'string')
850
- throw new Error('post data unavailable');
868
+ if (!r || typeof r.postData !== "string")
869
+ throw new Error("post data unavailable");
851
870
  return { postData: r.postData };
852
871
  });
853
872
  };
@@ -859,10 +878,15 @@ export function createNetworkForwarder(bridge) {
859
878
  return;
860
879
  if (pending.size >= MAX_PENDING)
861
880
  pending.clear();
862
- pending.set(p.requestId, { url: p.request.url, method: p.request.method, status: 0 });
881
+ pending.set(p.requestId, {
882
+ url: p.request.url,
883
+ method: p.request.method,
884
+ status: 0,
885
+ });
863
886
  // A body announced but not inlined is the one case the panel will
864
887
  // round-trip `Network.getRequestPostData` — flag it for prefetch.
865
- if (p.request.hasPostData === true && typeof p.request.postData !== 'string') {
888
+ if (p.request.hasPostData === true &&
889
+ typeof p.request.postData !== "string") {
866
890
  if (postDataWanted.size >= MAX_PENDING)
867
891
  postDataWanted.clear();
868
892
  postDataWanted.add(p.requestId);
@@ -887,12 +911,17 @@ export function createNetworkForwarder(bridge) {
887
911
  }
888
912
  function onLoadingFinished(params) {
889
913
  const p = params;
890
- if (typeof p?.requestId === 'string')
914
+ if (typeof p?.requestId === "string")
891
915
  prefetchBodies(p.requestId, p.encodedDataLength);
892
916
  const req = retirePending(p?.requestId);
893
917
  if (!req)
894
918
  return;
895
- maybeFallback({ source, url: req.url, method: req.method, status: req.status });
919
+ maybeFallback({
920
+ source,
921
+ url: req.url,
922
+ method: req.method,
923
+ status: req.status,
924
+ });
896
925
  }
897
926
  function onLoadingFailed(params) {
898
927
  const p = params;
@@ -905,14 +934,14 @@ export function createNetworkForwarder(bridge) {
905
934
  url: req.url,
906
935
  method: req.method,
907
936
  status: req.status,
908
- errorText: p.canceled ? 'canceled' : (p.errorText || 'failed'),
937
+ errorText: p.canceled ? "canceled" : p.errorText || "failed",
909
938
  });
910
939
  }
911
940
  const FALLBACK_HANDLERS = {
912
- 'Network.requestWillBeSent': onRequestWillBeSent,
913
- 'Network.responseReceived': onResponseReceived,
914
- 'Network.loadingFinished': onLoadingFinished,
915
- 'Network.loadingFailed': onLoadingFailed,
941
+ "Network.requestWillBeSent": onRequestWillBeSent,
942
+ "Network.responseReceived": onResponseReceived,
943
+ "Network.loadingFinished": onLoadingFinished,
944
+ "Network.loadingFailed": onLoadingFailed,
916
945
  };
917
946
  /**
918
947
  * Resolve (and, on `requestWillBeSent`, record) whether `rawId` is
@@ -925,10 +954,14 @@ export function createNetworkForwarder(bridge) {
925
954
  * than silently hiding it.
926
955
  */
927
956
  function resolveUserFacing(method, params, rawId) {
928
- if (method === 'Network.requestWillBeSent') {
929
- const url = params?.request?.url;
930
- const verdict = typeof url === 'string'
931
- ? isUserFacingRequest(url, [bridge.getResourceServerBaseUrl?.(), bridge.getSimulatorServerBaseUrl?.()])
957
+ if (method === "Network.requestWillBeSent") {
958
+ const url = params?.request
959
+ ?.url;
960
+ const verdict = typeof url === "string"
961
+ ? isUserFacingRequest(url, [
962
+ bridge.getResourceServerBaseUrl?.(),
963
+ bridge.getSimulatorServerBaseUrl?.(),
964
+ ])
932
965
  : true;
933
966
  if (rawId) {
934
967
  if (userFacingByRawId.size >= MAX_PENDING)
@@ -950,7 +983,7 @@ export function createNetworkForwarder(bridge) {
950
983
  // classification is even known. Every OTHER "rawId never seen" case
951
984
  // (e.g. capture attached mid-flight, no requestWillBeSent ever coming)
952
985
  // keeps failing open — there is no better signal available there.
953
- if (method === 'Network.requestWillBeSentExtraInfo')
986
+ if (method === "Network.requestWillBeSentExtraInfo")
954
987
  return false;
955
988
  return true;
956
989
  }
@@ -964,8 +997,9 @@ export function createNetworkForwarder(bridge) {
964
997
  // User-facing sink: only requests isUserFacingRequest() judged as the
965
998
  // developer's own business traffic — internal/framework resource
966
999
  // loads are mirrored to the global window ONLY.
967
- const rawId = params?.requestId;
968
- const userFacing = resolveUserFacing(method, params, typeof rawId === 'string' ? rawId : undefined);
1000
+ const rawId = params
1001
+ ?.requestId;
1002
+ const userFacing = resolveUserFacing(method, params, typeof rawId === "string" ? rawId : undefined);
969
1003
  if (userFacing) {
970
1004
  enqueueNative(rewritten.method, rewritten.params);
971
1005
  }
@@ -978,12 +1012,14 @@ export function createNetworkForwarder(bridge) {
978
1012
  // ── Fallback bookkeeping (used only when native dispatch is unusable) ──
979
1013
  FALLBACK_HANDLERS[method]?.(params);
980
1014
  };
981
- wc.debugger.on('message', onMessage);
1015
+ wc.debugger.on("message", onMessage);
982
1016
  attach.add(() => {
983
1017
  try {
984
- wc.debugger.removeListener('message', onMessage);
1018
+ wc.debugger.removeListener("message", onMessage);
1019
+ }
1020
+ catch {
1021
+ /* wc gone */
985
1022
  }
986
- catch { /* wc gone */ }
987
1023
  });
988
1024
  }
989
1025
  /** Drop one guest's wiring (message listener + broker lease). Attach/detach
@@ -1093,7 +1129,7 @@ export function createNetworkForwarder(bridge) {
1093
1129
  guestRetryGeneration.delete(wc.id);
1094
1130
  const teardown = new SyncDisposableRegistry();
1095
1131
  guestWired.set(wc.id, teardown);
1096
- wireNetworkCapture(wc, 'render', teardown, `g${wc.id}-${Date.now()}`);
1132
+ wireNetworkCapture(wc, "render", teardown, `g${wc.id}-${Date.now()}`);
1097
1133
  const detachSub = lease.onDetach(() => {
1098
1134
  cleanupGuest(wc.id);
1099
1135
  scheduleRenderGuestRetry(wc);
@@ -1102,8 +1138,8 @@ export function createNetworkForwarder(bridge) {
1102
1138
  detachSub.dispose();
1103
1139
  lease.dispose();
1104
1140
  });
1105
- void lease.send('Network.enable').catch((err) => {
1106
- console.warn('[network-forward] guest Network.enable failed:', err instanceof Error ? err.message : err);
1141
+ void lease.send("Network.enable").catch((err) => {
1142
+ console.warn("[network-forward] guest Network.enable failed:", err instanceof Error ? err.message : err);
1107
1143
  });
1108
1144
  }
1109
1145
  function attachRenderGuest(wc) {
@@ -1127,14 +1163,14 @@ export function createNetworkForwarder(bridge) {
1127
1163
  detachSimulator();
1128
1164
  const lease = broker.acquire(wc);
1129
1165
  if (!lease) {
1130
- console.warn('[network-forward] debugger session unavailable; simulator network not captured');
1166
+ console.warn("[network-forward] debugger session unavailable; simulator network not captured");
1131
1167
  return;
1132
1168
  }
1133
1169
  simWc = wc;
1134
1170
  simLease = lease;
1135
1171
  const attach = new SyncDisposableRegistry();
1136
1172
  attachDisposables = attach;
1137
- wireNetworkCapture(wc, 'service', attach, String(Date.now()));
1173
+ wireNetworkCapture(wc, "service", attach, String(Date.now()));
1138
1174
  const detachSub = lease.onDetach(() => {
1139
1175
  if (simWc !== wc)
1140
1176
  return;
@@ -1149,12 +1185,14 @@ export function createNetworkForwarder(bridge) {
1149
1185
  try {
1150
1186
  ad.disposeAll();
1151
1187
  }
1152
- catch { /* best-effort teardown */ }
1188
+ catch {
1189
+ /* best-effort teardown */
1190
+ }
1153
1191
  }
1154
1192
  });
1155
1193
  attach.add(() => detachSub.dispose());
1156
- void lease.send('Network.enable').catch((err) => {
1157
- console.warn('[network-forward] Network.enable failed:', err instanceof Error ? err.message : err);
1194
+ void lease.send("Network.enable").catch((err) => {
1195
+ console.warn("[network-forward] Network.enable failed:", err instanceof Error ? err.message : err);
1158
1196
  });
1159
1197
  }
1160
1198
  /**
@@ -1170,18 +1208,18 @@ export function createNetworkForwarder(bridge) {
1170
1208
  // 'idle' but a host is resolvable (set via the bridge, applyDevtoolsHost not
1171
1209
  // run): the native path is in play, so promote to 'probing' instead of
1172
1210
  // console — otherwise this completion would later double-render natively.
1173
- if (sink === 'idle' && resolveDevtoolsWc())
1211
+ if (sink === "idle" && resolveDevtoolsWc())
1174
1212
  beginProbing();
1175
1213
  switch (sink) {
1176
- case 'ready':
1214
+ case "ready":
1177
1215
  return;
1178
- case 'probing':
1216
+ case "probing":
1179
1217
  if (probeConsoleBuffer.length >= MAX_PENDING)
1180
1218
  probeConsoleBuffer.shift();
1181
1219
  probeConsoleBuffer.push(record);
1182
1220
  return;
1183
- case 'degraded':
1184
- case 'idle':
1221
+ case "degraded":
1222
+ case "idle":
1185
1223
  default:
1186
1224
  forwardToConsole(record);
1187
1225
  }
@@ -1207,7 +1245,7 @@ export function createNetworkForwarder(bridge) {
1207
1245
  if (!devtoolsWc) {
1208
1246
  // No host → 'idle': completions go straight to console; native queue is
1209
1247
  // moot, drop it so it can't double-render if a host later appears.
1210
- sink = 'idle';
1248
+ sink = "idle";
1211
1249
  dispatchQueue = [];
1212
1250
  return;
1213
1251
  }
@@ -1215,7 +1253,9 @@ export function createNetworkForwarder(bridge) {
1215
1253
  // WITHOUT touching view-manager (host-destroyed cleanup lives here). Begin
1216
1254
  // probing and flush anything already queued.
1217
1255
  const host = devtoolsWc;
1218
- const onHostDestroyed = () => { applyDevtoolsHost(null); };
1256
+ const onHostDestroyed = () => {
1257
+ applyDevtoolsHost(null);
1258
+ };
1219
1259
  // Route host-destroyed teardown through the connection registry when present
1220
1260
  // (the Connection fires onHostDestroyed on wc destroy / reset, and the
1221
1261
  // returned Disposable releases the ownership early on host swap/clear);
@@ -1227,18 +1267,20 @@ export function createNetworkForwarder(bridge) {
1227
1267
  // gated by the SAME `typeof host.once === 'function'` guard the fallback
1228
1268
  // uses — otherwise a minimal/fake DevTools host (no emitter) throws on the
1229
1269
  // connection path where the fallback would safely no-op.
1230
- if (reg && typeof host.once === 'function') {
1270
+ if (reg && typeof host.once === "function") {
1231
1271
  const owned = reg.acquire(host).own(onHostDestroyed);
1232
1272
  devtoolsHostDisposable = toDisposable(() => owned.dispose());
1233
1273
  }
1234
1274
  else {
1235
- if (typeof host.once === 'function')
1236
- host.once('destroyed', onHostDestroyed);
1275
+ if (typeof host.once === "function")
1276
+ host.once("destroyed", onHostDestroyed);
1237
1277
  devtoolsHostDisposable = toDisposable(() => {
1238
1278
  try {
1239
- host.removeListener?.('destroyed', onHostDestroyed);
1279
+ host.removeListener?.("destroyed", onHostDestroyed);
1280
+ }
1281
+ catch {
1282
+ /* gone */
1240
1283
  }
1241
- catch { /* gone */ }
1242
1284
  });
1243
1285
  }
1244
1286
  // A host swap while still 'probing' the OLD host must not inherit its
@@ -1246,12 +1288,14 @@ export function createNetworkForwarder(bridge) {
1246
1288
  // (re-)arming readyTimeoutTimer/probeDeadline for the NEW host, leaving it
1247
1289
  // probing forever with no timeout. Force through 'idle' so beginProbing()
1248
1290
  // always arms a fresh window for whichever host is now current.
1249
- sink = 'idle';
1291
+ sink = "idle";
1250
1292
  beginProbing();
1251
1293
  if (dispatchQueue.length > 0)
1252
1294
  scheduleFlush();
1253
1295
  }
1254
- registry.add(() => { disposed = true; });
1296
+ registry.add(() => {
1297
+ disposed = true;
1298
+ });
1255
1299
  registry.add(() => {
1256
1300
  devtoolsHostDisposable?.dispose();
1257
1301
  devtoolsHostDisposable = null;
@@ -1303,7 +1347,10 @@ export function createNetworkForwarder(bridge) {
1303
1347
  // per-socket verdict cache mirrors resolveUserFacing's decide-once rule.
1304
1348
  const wsSynthesizer = new WebSocketTraceSynthesizer({
1305
1349
  epoch: String(Date.now()),
1306
- internalOrigins: () => [bridge.getResourceServerBaseUrl?.(), bridge.getSimulatorServerBaseUrl?.()],
1350
+ internalOrigins: () => [
1351
+ bridge.getResourceServerBaseUrl?.(),
1352
+ bridge.getSimulatorServerBaseUrl?.(),
1353
+ ],
1307
1354
  });
1308
1355
  function reportWebSocketTrace(sessionId, event) {
1309
1356
  if (disposed)
@@ -1318,6 +1365,44 @@ export function createNetworkForwarder(bridge) {
1318
1365
  if (message.userFacing)
1319
1366
  enqueueNative(message.method, message.params);
1320
1367
  }
1368
+ // ── Main-process HTTP request trace → synthesized Network.request*/
1369
+ // Network.loading* CDP ──────────────────────────────────────────────────
1370
+ // Same shape as the WebSocket trace bridge above, for the same reason:
1371
+ // wx.request now runs on Node http/https in this process (that's what
1372
+ // stops Chromium's Fetch/CORS algorithm from attaching a spurious OPTIONS
1373
+ // preflight to it), so no webContents.debugger can observe it either.
1374
+ const httpSynthesizer = new RequestTraceSynthesizer({
1375
+ epoch: String(Date.now()),
1376
+ internalOrigins: () => [
1377
+ bridge.getResourceServerBaseUrl?.(),
1378
+ bridge.getSimulatorServerBaseUrl?.(),
1379
+ ],
1380
+ });
1381
+ function reportNativeRequestTrace(sessionId, event) {
1382
+ if (disposed)
1383
+ return;
1384
+ const message = httpSynthesizer.synthesize(sessionId, event);
1385
+ if (!message)
1386
+ return;
1387
+ const params = message.params;
1388
+ if (event.type === "redirect") {
1389
+ bodyCache.delete(params.requestId);
1390
+ postDataCache.delete(params.requestId);
1391
+ }
1392
+ // The response is already fully buffered in-process at `finished` — prime
1393
+ // the SAME cache a simulator-CDP prefetch would populate, so the
1394
+ // front-end's Get Response Body click resolves without a debugger
1395
+ // round-trip that (for this requestId) has nowhere to go.
1396
+ if (message.body) {
1397
+ bodyCache.prime(params.requestId, () => Promise.resolve(message.body));
1398
+ }
1399
+ if (message.postData !== undefined) {
1400
+ postDataCache.prime(params.requestId, () => Promise.resolve({ postData: message.postData }));
1401
+ }
1402
+ dispatchToGlobal(message.method, message.params);
1403
+ if (message.userFacing)
1404
+ enqueueNative(message.method, message.params);
1405
+ }
1321
1406
  return {
1322
1407
  attachSimulator,
1323
1408
  detachSimulator,
@@ -1328,6 +1413,7 @@ export function createNetworkForwarder(bridge) {
1328
1413
  // natively — surface it via the console fallback line.
1329
1414
  report: (record) => forwardToConsole(record),
1330
1415
  reportWebSocketTrace,
1416
+ reportNativeRequestTrace,
1331
1417
  bodies: {
1332
1418
  getResponseBody: (requestId) => bodyCache.lookup(requestId),
1333
1419
  getRequestPostData: (requestId) => postDataCache.lookup(requestId),