@dimina-kit/devtools 0.3.2-dev.20260525152421 → 0.3.2-dev.20260610082053

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 (295) hide show
  1. package/README.md +43 -40
  2. package/dist/main/api.d.ts +0 -1
  3. package/dist/main/api.js +5 -1
  4. package/dist/main/app/app.d.ts +15 -5
  5. package/dist/main/app/app.js +247 -110
  6. package/dist/main/app/bootstrap.d.ts +16 -0
  7. package/dist/main/app/bootstrap.js +20 -0
  8. package/dist/main/app/launch.d.ts +8 -4
  9. package/dist/main/app/launch.js +9 -4
  10. package/dist/main/index.bundle.js +6607 -2102
  11. package/dist/main/index.js +9 -1
  12. package/dist/main/ipc/app.d.ts +1 -1
  13. package/dist/main/ipc/bridge-router.d.ts +61 -0
  14. package/dist/main/ipc/bridge-router.js +1300 -0
  15. package/dist/main/ipc/panels.d.ts +1 -1
  16. package/dist/main/ipc/panels.js +6 -2
  17. package/dist/main/ipc/popover.d.ts +1 -1
  18. package/dist/main/ipc/project-fs.d.ts +57 -0
  19. package/dist/main/ipc/project-fs.js +448 -0
  20. package/dist/main/ipc/projects.d.ts +1 -1
  21. package/dist/main/ipc/session.d.ts +1 -1
  22. package/dist/main/ipc/settings.d.ts +1 -1
  23. package/dist/main/ipc/simulator-module.d.ts +6 -0
  24. package/dist/main/ipc/simulator-module.js +11 -1
  25. package/dist/main/ipc/simulator.d.ts +2 -2
  26. package/dist/main/ipc/simulator.js +49 -5
  27. package/dist/main/ipc/toolbar.d.ts +1 -1
  28. package/dist/main/ipc/views.d.ts +19 -0
  29. package/dist/main/ipc/views.js +65 -0
  30. package/dist/main/runtime/devtools-backend.d.ts +17 -0
  31. package/dist/main/runtime/devtools-backend.js +49 -0
  32. package/dist/main/runtime/miniapp-runtime.d.ts +39 -0
  33. package/dist/main/runtime/miniapp-runtime.js +17 -0
  34. package/dist/main/services/automation/exec.d.ts +27 -2
  35. package/dist/main/services/automation/exec.js +37 -3
  36. package/dist/main/services/automation/handlers/app.js +127 -11
  37. package/dist/main/services/automation/handlers/page.js +36 -19
  38. package/dist/main/services/automation/index.js +40 -13
  39. package/dist/main/services/automation/wait-active-page.d.ts +56 -0
  40. package/dist/main/services/automation/wait-active-page.js +71 -0
  41. package/dist/main/services/console-forward/index.d.ts +36 -0
  42. package/dist/main/services/console-forward/index.js +81 -0
  43. package/dist/main/services/dimina-resource-server.d.ts +7 -0
  44. package/dist/main/services/dimina-resource-server.js +99 -0
  45. package/dist/main/services/elements-forward/index.d.ts +66 -0
  46. package/dist/main/services/elements-forward/index.js +583 -0
  47. package/dist/main/services/layout/index.d.ts +23 -1
  48. package/dist/main/services/layout/index.js +28 -3
  49. package/dist/main/services/mcp/index.d.ts +1 -0
  50. package/dist/main/services/mcp/index.js +3 -0
  51. package/dist/main/services/mcp/server.d.ts +1 -1
  52. package/dist/main/services/mcp/server.js +1 -1
  53. package/dist/main/services/mcp/target-manager.d.ts +35 -0
  54. package/dist/main/services/mcp/target-manager.js +63 -1
  55. package/dist/main/services/mcp/tools/context-tools.js +15 -1
  56. package/dist/main/services/module.d.ts +1 -1
  57. package/dist/main/services/network-forward/index.d.ts +190 -0
  58. package/dist/main/services/network-forward/index.js +765 -0
  59. package/dist/main/services/notifications/renderer-notifier.d.ts +11 -0
  60. package/dist/main/services/notifications/renderer-notifier.js +7 -1
  61. package/dist/main/services/render-inspect/index.d.ts +41 -0
  62. package/dist/main/services/render-inspect/index.js +88 -0
  63. package/dist/main/services/safe-area/index.d.ts +17 -0
  64. package/dist/main/services/safe-area/index.js +59 -0
  65. package/dist/main/services/service-host-pool/pool.d.ts +197 -0
  66. package/dist/main/services/service-host-pool/pool.js +364 -0
  67. package/dist/main/services/simulator/custom-apis.d.ts +34 -0
  68. package/dist/main/services/simulator/custom-apis.js +22 -0
  69. package/dist/main/services/simulator-appdata/index.d.ts +35 -0
  70. package/dist/main/services/simulator-appdata/index.js +71 -0
  71. package/dist/main/services/simulator-current-page/index.d.ts +24 -0
  72. package/dist/main/services/simulator-current-page/index.js +19 -0
  73. package/dist/main/services/simulator-storage/index.d.ts +37 -2
  74. package/dist/main/services/simulator-storage/index.js +209 -32
  75. package/dist/main/services/simulator-storage/service-storage-ops.d.ts +40 -0
  76. package/dist/main/services/simulator-storage/service-storage-ops.js +67 -0
  77. package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
  78. package/dist/main/services/simulator-temp-files/index.d.ts +1 -1
  79. package/dist/main/services/simulator-temp-files/index.js +1 -1
  80. package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
  81. package/dist/main/services/simulator-wxml/index.d.ts +29 -0
  82. package/dist/main/services/simulator-wxml/index.js +28 -0
  83. package/dist/main/services/update/update-manager.d.ts +10 -0
  84. package/dist/main/services/update/update-manager.js +16 -3
  85. package/dist/main/services/views/devtools-tabs.d.ts +47 -0
  86. package/dist/main/services/views/devtools-tabs.js +95 -0
  87. package/dist/main/services/views/view-manager.d.ts +142 -4
  88. package/dist/main/services/views/view-manager.js +909 -17
  89. package/dist/main/services/workbench-context.d.ts +66 -1
  90. package/dist/main/services/workbench-context.js +6 -1
  91. package/dist/main/services/workspace/workspace-service.d.ts +8 -0
  92. package/dist/main/services/workspace/workspace-service.js +12 -0
  93. package/dist/main/utils/ipc-registry.d.ts +11 -2
  94. package/dist/main/utils/ipc-registry.js +63 -3
  95. package/dist/main/utils/paths.d.ts +15 -0
  96. package/dist/main/utils/paths.js +19 -0
  97. package/dist/main/utils/sender-policy.d.ts +9 -3
  98. package/dist/main/utils/sender-policy.js +18 -4
  99. package/dist/main/utils/theme.d.ts +1 -1
  100. package/dist/main/utils/theme.js +1 -1
  101. package/dist/main/windows/main-window/create.d.ts +0 -6
  102. package/dist/main/windows/main-window/create.js +11 -59
  103. package/dist/main/windows/main-window/events.d.ts +1 -1
  104. package/dist/main/windows/main-window/events.js +1 -1
  105. package/dist/main/windows/service-host-window/create.d.ts +52 -0
  106. package/dist/main/windows/service-host-window/create.js +112 -0
  107. package/dist/native-host/common/common.js +368 -0
  108. package/dist/native-host/container/pageFrame.css +1 -0
  109. package/dist/native-host/render/render.js +9334 -0
  110. package/dist/native-host/service/service.js +6 -0
  111. package/dist/preload/index.d.ts +2 -0
  112. package/dist/preload/index.js +6 -0
  113. package/dist/preload/instrumentation/app-data.d.ts +18 -20
  114. package/dist/preload/instrumentation/app-data.js +23 -160
  115. package/dist/preload/instrumentation/wxml-extract.d.ts +1 -1
  116. package/dist/preload/instrumentation/wxml-extract.js +1 -1
  117. package/dist/preload/runtime/bridge.d.ts +3 -8
  118. package/dist/preload/runtime/bridge.js +2 -32
  119. package/dist/preload/runtime/custom-apis.js +20 -17
  120. package/dist/preload/runtime/host-toolbar-advertiser.d.ts +27 -0
  121. package/dist/preload/runtime/host-toolbar-advertiser.js +55 -0
  122. package/dist/preload/runtime/main-api-runner.d.ts +9 -0
  123. package/dist/preload/runtime/main-api-runner.js +85 -0
  124. package/dist/preload/runtime/native-host.d.ts +43 -0
  125. package/dist/preload/runtime/native-host.js +83 -0
  126. package/dist/preload/shared/expose.d.ts +10 -5
  127. package/dist/preload/shared/expose.js +29 -11
  128. package/dist/preload/shared/sid-registry.d.ts +11 -0
  129. package/dist/preload/shared/sid-registry.js +38 -0
  130. package/dist/preload/windows/host-toolbar.cjs +161 -0
  131. package/dist/preload/windows/host-toolbar.cjs.map +7 -0
  132. package/dist/preload/windows/host-toolbar.d.ts +2 -0
  133. package/dist/preload/windows/host-toolbar.js +12 -0
  134. package/dist/preload/windows/main.cjs +1939 -0
  135. package/dist/preload/windows/main.cjs.map +4 -4
  136. package/dist/preload/windows/simulator.cjs +925 -0
  137. package/dist/preload/windows/simulator.cjs.map +7 -0
  138. package/dist/preload/windows/simulator.js +243 -573
  139. package/dist/render-host/pageFrame.html +29 -0
  140. package/dist/render-host/preload.cjs +154 -0
  141. package/dist/render-host/render-inspect.js +295 -0
  142. package/dist/renderer/assets/abap-DiwvWnMr.js +2 -0
  143. package/dist/renderer/assets/apex-CmtZjKlf.js +2 -0
  144. package/dist/renderer/assets/azcli-DL2My_i-.js +2 -0
  145. package/dist/renderer/assets/bat-B-nC98wG.js +2 -0
  146. package/dist/renderer/assets/bicep-Ju5MwOgh.js +3 -0
  147. package/dist/renderer/assets/cameligo-8Eu1TyBr.js +2 -0
  148. package/dist/renderer/assets/chunk-DPI0Y_ll.js +1 -0
  149. package/dist/renderer/assets/clojure-u-RpMkH3.js +2 -0
  150. package/dist/renderer/assets/codicon-ngg6Pgfi.ttf +0 -0
  151. package/dist/renderer/assets/coffee-CdA7bbTe.js +2 -0
  152. package/dist/renderer/assets/constants-CLAQX6VZ.js +2 -0
  153. package/dist/renderer/assets/cpp-CzNFP8ks.js +2 -0
  154. package/dist/renderer/assets/csharp-j1LThmcE.js +2 -0
  155. package/dist/renderer/assets/csp-CLRC61y6.js +2 -0
  156. package/dist/renderer/assets/css-r6rC_7P2.js +4 -0
  157. package/dist/renderer/assets/css.worker-DyDu5ynT.js +90 -0
  158. package/dist/renderer/assets/cssMode-BCBgxrnA.js +2 -0
  159. package/dist/renderer/assets/cypher-CW08XVUh.js +2 -0
  160. package/dist/renderer/assets/dart-Cs9aL5T_.js +2 -0
  161. package/dist/renderer/assets/dockerfile-BWM0M184.js +2 -0
  162. package/dist/renderer/assets/ecl-MJJuer5P.js +2 -0
  163. package/dist/renderer/assets/editor-CWI9TnMM.css +1 -0
  164. package/dist/renderer/assets/editor.api2-BTaYmRq7.js +873 -0
  165. package/dist/renderer/assets/editor.worker-Dpt_xPrs.js +27 -0
  166. package/dist/renderer/assets/elixir-D2AIuXqn.js +2 -0
  167. package/dist/renderer/assets/flow9-B2H24giC.js +2 -0
  168. package/dist/renderer/assets/freemarker2-DGDGzimW.js +4 -0
  169. package/dist/renderer/assets/fsharp-CMk2OIJN.js +2 -0
  170. package/dist/renderer/assets/go-BrMkuJg0.js +2 -0
  171. package/dist/renderer/assets/graphql-PSR1UKGv.js +2 -0
  172. package/dist/renderer/assets/handlebars-bOHhw3EG.js +2 -0
  173. package/dist/renderer/assets/hcl-DAQrbDOW.js +2 -0
  174. package/dist/renderer/assets/html-BMyuX0_c.js +2 -0
  175. package/dist/renderer/assets/html.worker-IWNXhnIC.js +503 -0
  176. package/dist/renderer/assets/htmlMode-DR4kXJ7l.js +2 -0
  177. package/dist/renderer/assets/index-CcJKzatF.js +50 -0
  178. package/dist/renderer/assets/ini-0TG5BxW0.js +2 -0
  179. package/dist/renderer/assets/input-QGx6FFX1.js +2 -0
  180. package/dist/renderer/assets/ipc-transport-BT4QYQqu.css +1 -0
  181. package/dist/renderer/assets/{ipc-transport-BzPzxPwf.js → ipc-transport-DvZvwK-Z.js} +5 -5
  182. package/dist/renderer/assets/java-rgorz17v.js +2 -0
  183. package/dist/renderer/assets/javascript-Bu-tl1P7.js +2 -0
  184. package/dist/renderer/assets/json.worker-c4h5s80L.js +59 -0
  185. package/dist/renderer/assets/jsonMode-MQV_ffLR.js +8 -0
  186. package/dist/renderer/assets/julia-C8VMdHm8.js +2 -0
  187. package/dist/renderer/assets/kotlin-AhhC4Qhm.js +2 -0
  188. package/dist/renderer/assets/less-Cgca25AP.js +3 -0
  189. package/dist/renderer/assets/lexon-D0GHdBaw.js +2 -0
  190. package/dist/renderer/assets/liquid-ClkfOvhB.js +2 -0
  191. package/dist/renderer/assets/lspLanguageFeatures-Bke_4Ie9.js +5 -0
  192. package/dist/renderer/assets/lua-DmRsNG-P.js +2 -0
  193. package/dist/renderer/assets/m3-BgL5dNKT.js +2 -0
  194. package/dist/renderer/assets/markdown-BuJfycGS.js +2 -0
  195. package/dist/renderer/assets/mdx-RZdUskPu.js +2 -0
  196. package/dist/renderer/assets/mips-C9m_93PR.js +2 -0
  197. package/dist/renderer/assets/monaco.contribution-B7rUMaEv.js +3 -0
  198. package/dist/renderer/assets/msdax-CpFHC9OI.js +2 -0
  199. package/dist/renderer/assets/mysql-qFvltsqN.js +2 -0
  200. package/dist/renderer/assets/objective-c-Bnmr858J.js +2 -0
  201. package/dist/renderer/assets/pascal-WP0_D5AO.js +2 -0
  202. package/dist/renderer/assets/pascaligo-Blom4Rij.js +2 -0
  203. package/dist/renderer/assets/perl-B-vk8g64.js +2 -0
  204. package/dist/renderer/assets/pgsql-Cgvz6v67.js +2 -0
  205. package/dist/renderer/assets/php-8a3Lrw9m.js +2 -0
  206. package/dist/renderer/assets/pla-DuFqEZ8V.js +2 -0
  207. package/dist/renderer/assets/popover-BOH1If1J.js +2 -0
  208. package/dist/renderer/assets/postiats-DkLtSgkp.js +2 -0
  209. package/dist/renderer/assets/powerquery-BJ1aNepW.js +2 -0
  210. package/dist/renderer/assets/powershell-rE98k687.js +2 -0
  211. package/dist/renderer/assets/protobuf-CUheFacr.js +3 -0
  212. package/dist/renderer/assets/pug-LDcAMD8w.js +2 -0
  213. package/dist/renderer/assets/python-Cobrm_-8.js +2 -0
  214. package/dist/renderer/assets/qsharp-DUKSQoR1.js +2 -0
  215. package/dist/renderer/assets/r-D-QApv87.js +2 -0
  216. package/dist/renderer/assets/razor-BEUIQFNJ.js +2 -0
  217. package/dist/renderer/assets/redis-SXdDyWR9.js +2 -0
  218. package/dist/renderer/assets/redshift-Y6lsCryn.js +2 -0
  219. package/dist/renderer/assets/restructuredtext-edObr9a8.js +2 -0
  220. package/dist/renderer/assets/ruby-CNnUfF-8.js +2 -0
  221. package/dist/renderer/assets/rust-IHUZWzBr.js +2 -0
  222. package/dist/renderer/assets/sb-DrUvY44N.js +2 -0
  223. package/dist/renderer/assets/scala-B4hbXGLM.js +2 -0
  224. package/dist/renderer/assets/scheme-BGrd12j3.js +2 -0
  225. package/dist/renderer/assets/scss-x5G1ES4U.js +4 -0
  226. package/dist/renderer/assets/select-C7JQ8G5N.js +2 -0
  227. package/dist/renderer/assets/settings-DpAbTJyF.js +2 -0
  228. package/dist/renderer/assets/{settings-api-BXCw9vOZ.js → settings-api-RD2IZF53.js} +2 -2
  229. package/dist/renderer/assets/shell-DOehe2Y8.js +2 -0
  230. package/dist/renderer/assets/solidity-BeRvcwWV.js +2 -0
  231. package/dist/renderer/assets/sophia-DZbkUNjy.js +2 -0
  232. package/dist/renderer/assets/sparql-B7_oi5-h.js +2 -0
  233. package/dist/renderer/assets/sql-CTlsFWVE.js +2 -0
  234. package/dist/renderer/assets/st-DJVEJdPE.js +2 -0
  235. package/dist/renderer/assets/swift-CwhT3fYa.js +2 -0
  236. package/dist/renderer/assets/systemverilog-BQN63pkN.js +2 -0
  237. package/dist/renderer/assets/tcl-DqwfpskA.js +2 -0
  238. package/dist/renderer/assets/ts.worker-BwM5Ha3L.js +67720 -0
  239. package/dist/renderer/assets/tsMode-BHa7dl9v.js +12 -0
  240. package/dist/renderer/assets/twig-BiyenUgc.js +2 -0
  241. package/dist/renderer/assets/typescript-15aT0Yj8.js +2 -0
  242. package/dist/renderer/assets/typespec-CWOJribt.js +2 -0
  243. package/dist/renderer/assets/vb-Cq5F87m3.js +2 -0
  244. package/dist/renderer/assets/wgsl-BAvW2lVr.js +299 -0
  245. package/dist/renderer/assets/workbenchSettings-CNJX4Qna.js +8 -0
  246. package/dist/renderer/assets/workers-CRBS0W-v.js +2 -0
  247. package/dist/renderer/assets/xml-bR2Zh-HV.js +2 -0
  248. package/dist/renderer/assets/yaml-Bdk54Qrb.js +2 -0
  249. package/dist/renderer/entries/main/index.html +29 -6
  250. package/dist/renderer/entries/popover/index.html +7 -6
  251. package/dist/renderer/entries/settings/index.html +6 -5
  252. package/dist/renderer/entries/workbench-settings/index.html +6 -5
  253. package/dist/service-host/preload.cjs +254 -0
  254. package/dist/service-host/service.html +10 -0
  255. package/dist/service-host/sourcemap-rewrite.cjs +58 -0
  256. package/dist/service-host/sync-api-patch.js +20 -0
  257. package/dist/service-host/sync-impls/menu-button.js +16 -0
  258. package/dist/service-host/sync-impls/storage.js +50 -0
  259. package/dist/service-host/sync-impls/system-info.js +54 -0
  260. package/dist/shared/appdata-accumulator.d.ts +95 -0
  261. package/dist/shared/appdata-accumulator.js +213 -0
  262. package/dist/shared/bridge-channels.d.ts +364 -0
  263. package/dist/shared/bridge-channels.js +54 -0
  264. package/dist/shared/constants.d.ts +0 -4
  265. package/dist/shared/constants.js +0 -4
  266. package/dist/shared/ipc-channels.d.ts +108 -1
  267. package/dist/shared/ipc-channels.js +127 -10
  268. package/dist/shared/ipc-schemas.d.ts +65 -2
  269. package/dist/shared/ipc-schemas.js +79 -3
  270. package/dist/shared/open-in-editor.d.ts +71 -0
  271. package/dist/shared/open-in-editor.js +138 -0
  272. package/dist/shared/simulator-api-metadata.d.ts +21 -0
  273. package/dist/shared/simulator-api-metadata.js +23 -0
  274. package/dist/shared/types.d.ts +2 -2
  275. package/dist/shared/vpath.d.ts +42 -0
  276. package/dist/shared/vpath.js +105 -0
  277. package/dist/simulator/assets/device-shell-DsdcNEkt.css +1 -0
  278. package/dist/simulator/assets/device-shell-vwYSOVrh.js +2 -0
  279. package/dist/simulator/assets/jsx-runtime-P6ZegaOf.js +2 -0
  280. package/dist/simulator/assets/simulator-DddliUL5.js +10 -0
  281. package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +2 -0
  282. package/dist/simulator/device-shell/menu-button-geometry.js +8 -0
  283. package/dist/simulator/simulator.html +2 -112
  284. package/package.json +9 -9
  285. package/dist/main/utils/disposable.d.ts +0 -18
  286. package/dist/main/utils/disposable.js +0 -67
  287. package/dist/renderer/assets/constants-DI382mTP.js +0 -2
  288. package/dist/renderer/assets/index-4lkyF644.js +0 -46
  289. package/dist/renderer/assets/input-D4byFOLS.js +0 -2
  290. package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +0 -1
  291. package/dist/renderer/assets/popover-cxKrg_os.js +0 -2
  292. package/dist/renderer/assets/select-BgSkkdHW.js +0 -2
  293. package/dist/renderer/assets/settings-oD2K1jdT.js +0 -2
  294. package/dist/renderer/assets/workbenchSettings-CTNBpTft.js +0 -8
  295. package/dist/simulator/assets/simulator-CVOFTpSj.js +0 -9
@@ -0,0 +1,765 @@
1
+ import { DisposableRegistry, toDisposable } from '@dimina-kit/electron-deck/main';
2
+ // ── requestId namespacing (pure, testable) ──────────────────────────────────
3
+ /**
4
+ * CDP events whose `params.requestId` we rewrite into the virtual namespace.
5
+ * Anything carrying a requestId must be namespaced consistently — even methods
6
+ * we don't (yet) forward — so the raw→virtual map stays coherent if forwarding
7
+ * is widened later. `requestServedFromCache` and `resourceChangedPriority` are
8
+ * included for that reason (MINOR: rewrite-only today, not forwarded).
9
+ */
10
+ export const REWRITE_REQUEST_ID_METHODS = new Set([
11
+ 'Network.requestWillBeSent',
12
+ 'Network.requestWillBeSentExtraInfo',
13
+ 'Network.responseReceived',
14
+ 'Network.responseReceivedExtraInfo',
15
+ 'Network.dataReceived',
16
+ 'Network.loadingFinished',
17
+ 'Network.loadingFailed',
18
+ 'Network.requestServedFromCache',
19
+ 'Network.resourceChangedPriority',
20
+ // 二期 (when WebSocket/EventSource forwarding lands): Network.webSocket*,
21
+ // Network.eventSourceMessageReceived — keep ids namespaced once added here.
22
+ ]);
23
+ /** The Network.* methods this one-shot pass forwards to the front-end. */
24
+ export const FORWARDED_METHODS = new Set([
25
+ 'Network.requestWillBeSent',
26
+ 'Network.requestWillBeSentExtraInfo',
27
+ 'Network.responseReceived',
28
+ 'Network.responseReceivedExtraInfo',
29
+ 'Network.loadingFinished',
30
+ 'Network.loadingFailed',
31
+ // `dataReceived` deliberately omitted (二期) — see header.
32
+ ]);
33
+ /** Methods that mark a request finished, so its id mapping can age out. */
34
+ const TERMINAL_METHODS = new Set([
35
+ 'Network.loadingFinished',
36
+ 'Network.loadingFailed',
37
+ ]);
38
+ /**
39
+ * Bounded, TTL'd raw→virtual requestId map with an active/retired split.
40
+ *
41
+ * An ACTIVE request (seen `requestWillBeSent`/ExtraInfo, not yet terminal) must
42
+ * NEVER lose its mapping — otherwise a later `responseReceived`/`loadingFinished`
43
+ * would mint a fresh virtual id and the front-end would see an orphaned event.
44
+ * So active entries are exempt from both TTL and the LRU cap.
45
+ *
46
+ * Only once a request goes terminal (`loadingFinished`/`loadingFailed`) is it
47
+ * RETIRED: its mapping is kept a while longer so genuinely-late events (an
48
+ * extra-info after completion) still correlate, but it now ages out by TTL and
49
+ * by an LRU cap so a long session never grows unbounded. Eviction only ever
50
+ * touches the retired pool.
51
+ */
52
+ export class RequestIdNamespace {
53
+ epoch;
54
+ ttlMs;
55
+ max;
56
+ now;
57
+ map = new Map();
58
+ seq = 0;
59
+ constructor(epoch, ttlMs = 5 * 60_000, max = 1000, now = () => Date.now()) {
60
+ this.epoch = epoch;
61
+ this.ttlMs = ttlMs;
62
+ this.max = max;
63
+ this.now = now;
64
+ }
65
+ /**
66
+ * Resolve (lazily creating) the virtual id for a raw id. Lazy creation lets an
67
+ * ExtraInfo event that arrives BEFORE its `requestWillBeSent` still get a
68
+ * stable id that the later events reuse — a redirect chain reuses the same raw
69
+ * id and therefore the same virtual id (we never mint a new one per redirect).
70
+ *
71
+ * A freshly-resolved (or any non-retired) entry is ACTIVE and thus immune to
72
+ * eviction. We only TTL-expire entries that have already been retired; an
73
+ * active entry never "expires" no matter how long it stays in flight.
74
+ */
75
+ resolve(rawId) {
76
+ const t = this.now();
77
+ const existing = this.map.get(rawId);
78
+ if (existing && (existing.active || existing.expires > t)) {
79
+ // Refresh recency (LRU) on touch; refresh TTL only for retired entries
80
+ // (active entries don't use TTL at all).
81
+ if (!existing.active)
82
+ existing.expires = t + this.ttlMs;
83
+ this.map.delete(rawId);
84
+ this.map.set(rawId, existing);
85
+ return existing.virtual;
86
+ }
87
+ const virtual = `dimina:sim:${this.epoch}:${this.seq++}:${rawId}`;
88
+ this.map.set(rawId, { virtual, expires: t + this.ttlMs, active: true });
89
+ this.evict(t);
90
+ return virtual;
91
+ }
92
+ /**
93
+ * Mark a request terminal: it leaves the active set and enters the retired
94
+ * pool with a fresh TTL, where it becomes eligible for TTL/LRU eviction.
95
+ */
96
+ retire(rawId) {
97
+ const e = this.map.get(rawId);
98
+ if (e) {
99
+ e.active = false;
100
+ e.expires = this.now() + this.ttlMs;
101
+ }
102
+ }
103
+ evict(t) {
104
+ // Drop expired retired entries first (active entries never expire).
105
+ for (const [k, v] of this.map) {
106
+ if (!v.active && v.expires <= t)
107
+ this.map.delete(k);
108
+ }
109
+ // Then LRU-trim the RETIRED pool to the cap. Active entries are exempt:
110
+ // we walk in insertion/refresh order and skip any still-active entry, so an
111
+ // in-flight request is never evicted even past the cap.
112
+ if (this.map.size <= this.max)
113
+ return;
114
+ for (const [k, v] of this.map) {
115
+ if (this.map.size <= this.max)
116
+ break;
117
+ if (!v.active)
118
+ this.map.delete(k);
119
+ }
120
+ }
121
+ /** Total entries (active + retired). */
122
+ get size() {
123
+ return this.map.size;
124
+ }
125
+ /** Entries still in flight (resolved, not yet retired). */
126
+ get activeSize() {
127
+ let n = 0;
128
+ for (const v of this.map.values())
129
+ if (v.active)
130
+ n++;
131
+ return n;
132
+ }
133
+ }
134
+ /**
135
+ * Rewrite a single CDP message's `requestId` into the namespace, returning a new
136
+ * `{ method, params }` (never mutating the input). Returns the message unchanged
137
+ * when the method carries no requestId we track or params is malformed. Pure
138
+ * aside from the namespace map it threads through.
139
+ */
140
+ export function rewriteRequestId(method, params, ns) {
141
+ if (!REWRITE_REQUEST_ID_METHODS.has(method))
142
+ return { method, params };
143
+ const p = params;
144
+ if (!p || typeof p.requestId !== 'string')
145
+ return { method, params };
146
+ const virtual = ns.resolve(p.requestId);
147
+ if (TERMINAL_METHODS.has(method))
148
+ ns.retire(p.requestId);
149
+ return { method, params: { ...p, requestId: virtual } };
150
+ }
151
+ // ── DevTools front-end injection (the primary sink) ─────────────────────────
152
+ /** Probe the front-end realm exposes `DevToolsAPI.dispatchMessage`. */
153
+ const PROBE_DEVTOOLS_API = `(window.DevToolsAPI && typeof window.DevToolsAPI.dispatchMessage === 'function')`;
154
+ /**
155
+ * Build the `executeJavaScript` source that dispatches a BATCH of raw CDP
156
+ * messages into the DevTools front-end. Each message is carried as a JSON
157
+ * literal (data, never interpolated code — same discipline as console-forward)
158
+ * and dispatched via `DevToolsAPI.dispatchMessage`. Returns false out of the IIFE
159
+ * when the API isn't there yet, so the main side knows to retry / fall back.
160
+ */
161
+ function buildDispatchScript(messages) {
162
+ const arr = JSON.stringify(messages);
163
+ return `(()=>{try{`
164
+ + `if(!${PROBE_DEVTOOLS_API})return false;`
165
+ + `const ms=JSON.parse(${JSON.stringify(arr)});`
166
+ + `for(const m of ms){try{window.DevToolsAPI.dispatchMessage(m)}catch(_){}}`
167
+ + `return true;`
168
+ + `}catch(_){return false}})()`;
169
+ }
170
+ /**
171
+ * Build the `executeJavaScript` source for a chunked dispatch of one large CDP
172
+ * message — DevTools' own transport caps a single `dispatchMessage` payload, so
173
+ * the front-end exposes `dispatchMessageChunk(messageChunk, messageSize)` where
174
+ * the FIRST chunk carries the total size and EVERY SUBSEQUENT chunk is called
175
+ * with the chunk ONLY (second arg omitted). This matches Chromium's
176
+ * `devtools_ui_bindings.cc` DispatchProtocolMessage, whose front-end treats a
177
+ * call with a second argument as the start of a new message and a call without
178
+ * one as a continuation. Passing 0 (the previous behaviour) risked the
179
+ * front-end mis-reading a continuation as a fresh 0-size message. Returns false
180
+ * when the API isn't available.
181
+ */
182
+ function buildChunkedDispatchScript(chunks, totalSize) {
183
+ const arr = JSON.stringify(chunks);
184
+ return `(()=>{try{`
185
+ + `if(!(window.DevToolsAPI&&typeof window.DevToolsAPI.dispatchMessageChunk==='function'))return false;`
186
+ + `const cs=JSON.parse(${JSON.stringify(arr)});`
187
+ + `for(let i=0;i<cs.length;i++){`
188
+ + `try{`
189
+ // First chunk: (chunk, totalSize). Subsequent chunks: (chunk) — second arg
190
+ // omitted, NOT 0, per Chromium's continuation contract.
191
+ + `if(i===0){window.DevToolsAPI.dispatchMessageChunk(cs[i], ${totalSize})}`
192
+ + `else{window.DevToolsAPI.dispatchMessageChunk(cs[i])}`
193
+ + `}catch(_){}`
194
+ + `}`
195
+ + `return true;`
196
+ + `}catch(_){return false}})()`;
197
+ }
198
+ /** A single dispatch payload may not exceed this many UTF-16 chars; chunk above. */
199
+ const MAX_SINGLE_DISPATCH_CHARS = 1_000_000;
200
+ const CHUNK_CHARS = 256 * 1024;
201
+ /**
202
+ * Upper bound on the COMBINED size (in UTF-16 chars) of the messages packed into
203
+ * one `executeJavaScript` batch. 2000 small messages otherwise stitch into a
204
+ * single script that can blow past the IPC / script-size limit and reject the
205
+ * whole batch. We pack greedily up to this many chars, then flush and start a
206
+ * new batch, so each `executeJavaScript` stays well-sized.
207
+ */
208
+ const MAX_BATCH_CHARS = 512 * 1024;
209
+ // ── console fallback sink ───────────────────────────────────────────────────
210
+ /**
211
+ * Build the `executeJavaScript` source that logs one request line into the
212
+ * service host. Carried as a JSON literal and re-parsed service-side — no
213
+ * captured value is ever interpolated into executable JS (data-not-code).
214
+ */
215
+ function buildForwardScript(record) {
216
+ const json = JSON.stringify(record);
217
+ return `(()=>{try{const r=JSON.parse(${JSON.stringify(json)});`
218
+ + `const tag='[网络]['+r.source+']';`
219
+ + `const head=r.method+' '+(r.status||'-')+' '+r.url;`
220
+ + `if(r.errorText){console.warn(tag,head,r.errorText)}`
221
+ + `else if(r.status>=400){console.warn(tag,head)}`
222
+ + `else{console.log(tag,head)}`
223
+ + `}catch(_){}})()`;
224
+ }
225
+ /** Cap so a long session can't grow the fallback pending map unboundedly. */
226
+ const MAX_PENDING = 1000;
227
+ /** Cap on the native dispatch queue (chars-agnostic count) while no/ready host. */
228
+ const MAX_DISPATCH_QUEUE = 2000;
229
+ /**
230
+ * How long we wait for `window.DevToolsAPI.dispatchMessage` to answer ready once
231
+ * a host wc IS set, before giving up on the native path for that host and
232
+ * degrading to the console sink. Prevents the infinite-requeue-never-fallback
233
+ * loop when a host exists but its front-end never finishes booting.
234
+ */
235
+ const DEVTOOLS_READY_TIMEOUT_MS = 5_000;
236
+ /** Poll interval while probing for the front-end API to become ready. */
237
+ const READY_RETRY_MS = 100;
238
+ export function createNetworkForwarder(bridge) {
239
+ const registry = new DisposableRegistry();
240
+ // The simulator WCV we currently have a debugger session on, and the
241
+ // per-attach teardown (debugger listeners + detach). Null when not attached.
242
+ let simWc = null;
243
+ let attachDisposables = null;
244
+ // The DevTools front-end host wc (primary sink), set by the ViewManager.
245
+ let devtoolsWc = null;
246
+ // Teardown for the wc 'destroyed' watcher on the current host (clears the host
247
+ // here, in this file — view-manager is owned by another change and untouched).
248
+ let devtoolsHostDisposable = null;
249
+ // ── Native-sink state machine (MAJOR 1) ───────────────────────────────────
250
+ let sink = 'idle';
251
+ // Buffered completed-request records while 'probing' — flushed to console if we
252
+ // degrade, dropped if we go ready (so a request shows in exactly one sink).
253
+ let probeConsoleBuffer = [];
254
+ let readyTimeoutTimer = null;
255
+ // ── Batched native dispatch into the DevTools front-end ───────────────────
256
+ // Queued raw CDP messages (already namespaced + JSON-stringified) awaiting a
257
+ // microtask flush — coalescing many events into one executeJavaScript avoids
258
+ // high-frequency IPC.
259
+ let dispatchQueue = [];
260
+ let flushScheduled = false;
261
+ let readyRetryTimer = null;
262
+ function resolveDevtoolsWc() {
263
+ const wc = devtoolsWc ?? bridge.getDevtoolsWc?.() ?? null;
264
+ return wc && !wc.isDestroyed() ? wc : null;
265
+ }
266
+ /** Enter the 'probing' state and arm the ready-timeout (idempotent). */
267
+ function beginProbing() {
268
+ if (sink === 'probing')
269
+ return;
270
+ sink = 'probing';
271
+ if (readyTimeoutTimer)
272
+ clearTimeout(readyTimeoutTimer);
273
+ readyTimeoutTimer = setTimeout(() => {
274
+ readyTimeoutTimer = null;
275
+ // Still not ready after the grace period → abandon native for this host.
276
+ if (sink === 'probing')
277
+ degradeToConsole();
278
+ }, DEVTOOLS_READY_TIMEOUT_MS);
279
+ }
280
+ /** Native path confirmed live: console buffer is moot, drop it. */
281
+ function markReady() {
282
+ sink = 'ready';
283
+ if (readyTimeoutTimer) {
284
+ clearTimeout(readyTimeoutTimer);
285
+ readyTimeoutTimer = null;
286
+ }
287
+ if (readyRetryTimer) {
288
+ clearTimeout(readyRetryTimer);
289
+ readyRetryTimer = null;
290
+ }
291
+ // Native rendered these requests; their buffered console copies would dup.
292
+ probeConsoleBuffer = [];
293
+ }
294
+ /**
295
+ * Give up on the native path for the current host: drop the native queue so it
296
+ * can't later double-render, mark 'degraded' so the hot path stops retrying,
297
+ * and flush the buffered completed-request records to the console sink.
298
+ */
299
+ function degradeToConsole() {
300
+ sink = 'degraded';
301
+ dispatchQueue = [];
302
+ if (readyTimeoutTimer) {
303
+ clearTimeout(readyTimeoutTimer);
304
+ readyTimeoutTimer = null;
305
+ }
306
+ if (readyRetryTimer) {
307
+ clearTimeout(readyRetryTimer);
308
+ readyRetryTimer = null;
309
+ }
310
+ const buffered = probeConsoleBuffer;
311
+ probeConsoleBuffer = [];
312
+ for (const r of buffered)
313
+ forwardToConsole(r);
314
+ }
315
+ function scheduleFlush() {
316
+ if (flushScheduled)
317
+ return;
318
+ flushScheduled = true;
319
+ queueMicrotask(flushDispatch);
320
+ }
321
+ /** Trim the native queue to its cap, preferring to keep request-opening events.
322
+ * Active requests' first events (requestWillBeSent / ...ExtraInfo) are retained
323
+ * so later responseReceived/loadingFinished never become orphans in the panel;
324
+ * we drop the oldest NON-opening (low-value / completion) events first. (MAJOR 2) */
325
+ function trimQueue() {
326
+ if (dispatchQueue.length <= MAX_DISPATCH_QUEUE)
327
+ return;
328
+ const isOpener = (json) => json.includes('"Network.requestWillBeSent"')
329
+ || json.includes('"Network.requestWillBeSentExtraInfo"');
330
+ // First pass: drop oldest non-opener events.
331
+ const kept = [];
332
+ let over = dispatchQueue.length - MAX_DISPATCH_QUEUE;
333
+ for (const json of dispatchQueue) {
334
+ if (over > 0 && !isOpener(json)) {
335
+ over--;
336
+ continue;
337
+ }
338
+ kept.push(json);
339
+ }
340
+ // If openers alone still exceed the cap, fall back to dropping oldest openers.
341
+ if (kept.length > MAX_DISPATCH_QUEUE) {
342
+ dispatchQueue = kept.slice(kept.length - MAX_DISPATCH_QUEUE);
343
+ }
344
+ else {
345
+ dispatchQueue = kept;
346
+ }
347
+ }
348
+ function flushDispatch() {
349
+ flushScheduled = false;
350
+ if (dispatchQueue.length === 0)
351
+ return;
352
+ if (sink === 'degraded') {
353
+ dispatchQueue = [];
354
+ return;
355
+ }
356
+ const wc = resolveDevtoolsWc();
357
+ if (!wc) {
358
+ // Host went away mid-flight. Keep the queue bounded (MAJOR 2: cap applies on
359
+ // EVERY path, not just no-host) and wait — setDevtoolsHost re-arms probing.
360
+ trimQueue();
361
+ return;
362
+ }
363
+ if (sink === 'idle')
364
+ beginProbing();
365
+ // Pack greedily up to MAX_BATCH_CHARS so one executeJavaScript stays sized
366
+ // (MAJOR 5); oversized single messages go via the chunked transport.
367
+ const batch = [];
368
+ let batchChars = 0;
369
+ let i = 0;
370
+ for (; i < dispatchQueue.length; i++) {
371
+ const msg = dispatchQueue[i];
372
+ if (msg.length > MAX_SINGLE_DISPATCH_CHARS) {
373
+ // Flush whatever's accumulated first to preserve ordering, then chunk.
374
+ if (batch.length > 0)
375
+ break;
376
+ dispatchChunked(wc, msg);
377
+ continue;
378
+ }
379
+ if (batch.length > 0 && batchChars + msg.length > MAX_BATCH_CHARS)
380
+ break;
381
+ batch.push(msg);
382
+ batchChars += msg.length;
383
+ }
384
+ // Everything up to i was either batched or chunk-dispatched; keep the rest.
385
+ const remaining = dispatchQueue.slice(i);
386
+ if (batch.length === 0) {
387
+ // Only chunked messages were processed this turn; continue with the rest.
388
+ dispatchQueue = remaining;
389
+ if (dispatchQueue.length > 0)
390
+ scheduleFlush();
391
+ return;
392
+ }
393
+ let script;
394
+ try {
395
+ script = buildDispatchScript(batch);
396
+ }
397
+ catch {
398
+ dispatchQueue = remaining;
399
+ if (dispatchQueue.length > 0)
400
+ scheduleFlush();
401
+ return;
402
+ }
403
+ // Hold the rest of the queue (un-flushed) until this batch resolves so a
404
+ // not-ready answer can re-queue the in-flight batch ahead of it in order.
405
+ dispatchQueue = remaining;
406
+ if (remaining.length > 0)
407
+ scheduleFlush();
408
+ wc.executeJavaScript(script, true).then((ok) => {
409
+ if (ok === true) {
410
+ if (sink !== 'ready')
411
+ markReady();
412
+ return;
413
+ }
414
+ // API not present yet — front-end still booting. Re-queue this batch ahead
415
+ // of any newer events and poll until ready (or the timeout degrades us).
416
+ if (sink === 'degraded')
417
+ return;
418
+ dispatchQueue = batch.concat(dispatchQueue);
419
+ trimQueue();
420
+ scheduleReadyRetry();
421
+ }).catch(() => {
422
+ // wc navigated / torn down mid-call, OR the script overflowed IPC. Re-queue
423
+ // (bounded) and let the next flush re-resolve the host. Best-effort; the
424
+ // ready-timeout still governs giving up. (MAJOR 5: backoff via retry timer.)
425
+ if (sink === 'degraded')
426
+ return;
427
+ dispatchQueue = batch.concat(dispatchQueue);
428
+ trimQueue();
429
+ scheduleReadyRetry();
430
+ });
431
+ }
432
+ function dispatchChunked(wc, msg) {
433
+ const totalSize = msg.length;
434
+ const chunks = [];
435
+ for (let i = 0; i < msg.length; i += CHUNK_CHARS) {
436
+ chunks.push(msg.slice(i, i + CHUNK_CHARS));
437
+ }
438
+ let script;
439
+ try {
440
+ script = buildChunkedDispatchScript(chunks, totalSize);
441
+ }
442
+ catch {
443
+ return;
444
+ }
445
+ wc.executeJavaScript(script, true).catch(() => { });
446
+ }
447
+ function scheduleReadyRetry() {
448
+ if (readyRetryTimer || sink === 'ready' || sink === 'degraded')
449
+ return;
450
+ readyRetryTimer = setTimeout(() => {
451
+ readyRetryTimer = null;
452
+ if (sink === 'degraded')
453
+ return;
454
+ if (dispatchQueue.length > 0)
455
+ scheduleFlush();
456
+ }, READY_RETRY_MS);
457
+ }
458
+ /** Queue one raw (already-namespaced) CDP message for native dispatch. */
459
+ function enqueueNative(method, params) {
460
+ // The native queue only accumulates when a host EXISTS (probing/ready). In
461
+ // 'idle' (no host) or 'degraded' the console is the sole sink for these
462
+ // requests — queueing them would let them double-render if a host later
463
+ // arrives / swaps in. resolveDevtoolsWc() guards the idle case where a host
464
+ // is set on the bridge but applyDevtoolsHost hasn't run yet.
465
+ if (sink === 'degraded')
466
+ return;
467
+ if (sink === 'idle' && !resolveDevtoolsWc())
468
+ return;
469
+ let json;
470
+ try {
471
+ json = JSON.stringify({ method, params });
472
+ }
473
+ catch {
474
+ // A value CDP serialized but we can't re-serialize — drop, never throw.
475
+ return;
476
+ }
477
+ dispatchQueue.push(json);
478
+ trimQueue();
479
+ scheduleFlush();
480
+ }
481
+ // ── console fallback ──────────────────────────────────────────────────────
482
+ /** Re-emit a completed request into the service host's console (fallback). */
483
+ function forwardToConsole(record) {
484
+ const wc = bridge.getServiceWc();
485
+ if (!wc || wc.isDestroyed())
486
+ return;
487
+ let script;
488
+ try {
489
+ script = buildForwardScript(record);
490
+ }
491
+ catch {
492
+ return;
493
+ }
494
+ wc.executeJavaScript(script, true).catch(() => { });
495
+ }
496
+ function detachSimulator() {
497
+ const wc = simWc;
498
+ simWc = null;
499
+ const ad = attachDisposables;
500
+ attachDisposables = null;
501
+ if (ad)
502
+ void ad.disposeAll().catch(() => { });
503
+ // Drop any queued-but-unflushed messages so a new attach starts clean, and
504
+ // reset the native-sink state machine (the next attach re-probes the host).
505
+ dispatchQueue = [];
506
+ probeConsoleBuffer = [];
507
+ // A new simulator attach restarts the native sink: 'idle' until the next
508
+ // event re-probes the (possibly already-set) host.
509
+ sink = 'idle';
510
+ if (readyRetryTimer) {
511
+ clearTimeout(readyRetryTimer);
512
+ readyRetryTimer = null;
513
+ }
514
+ if (readyTimeoutTimer) {
515
+ clearTimeout(readyTimeoutTimer);
516
+ readyTimeoutTimer = null;
517
+ }
518
+ if (!wc || wc.isDestroyed())
519
+ return;
520
+ try {
521
+ if (wc.debugger.isAttached())
522
+ wc.debugger.detach();
523
+ }
524
+ catch { /* already detached / mid-destroy */ }
525
+ }
526
+ function attachSimulator(wc) {
527
+ if (!wc || wc.isDestroyed())
528
+ return;
529
+ if (simWc === wc && !wc.isDestroyed())
530
+ return;
531
+ detachSimulator();
532
+ try {
533
+ if (!wc.debugger.isAttached()) {
534
+ wc.debugger.attach('1.3');
535
+ }
536
+ }
537
+ catch (err) {
538
+ console.warn('[network-forward] debugger.attach failed; simulator network not captured:', err instanceof Error ? err.message : err);
539
+ return;
540
+ }
541
+ simWc = wc;
542
+ const attach = new DisposableRegistry();
543
+ attachDisposables = attach;
544
+ // Per-attach requestId namespace: a fresh epoch each attach guarantees ids
545
+ // from a previous simulator session can never collide with this one.
546
+ const ns = new RequestIdNamespace(String(Date.now()));
547
+ // Fallback bookkeeping: requestId → in-flight request, for the console line
548
+ // when the native dispatch path is unusable.
549
+ const pending = new Map();
550
+ const onMessage = (_event, method, params) => {
551
+ // ── Primary sink: forward the raw CDP event into the DevTools front-end ──
552
+ if (FORWARDED_METHODS.has(method)) {
553
+ const rewritten = rewriteRequestId(method, params, ns);
554
+ enqueueNative(rewritten.method, rewritten.params);
555
+ }
556
+ else if (REWRITE_REQUEST_ID_METHODS.has(method)) {
557
+ // Methods we namespace but don't forward (dataReceived, 二期): still
558
+ // resolve so the id mapping stays coherent if forwarding is added later.
559
+ rewriteRequestId(method, params, ns);
560
+ }
561
+ // ── Fallback bookkeeping (used only when native dispatch is unusable) ──
562
+ switch (method) {
563
+ case 'Network.requestWillBeSent': {
564
+ const p = params;
565
+ if (!p?.request)
566
+ return;
567
+ if (pending.size >= MAX_PENDING)
568
+ pending.clear();
569
+ pending.set(p.requestId, { url: p.request.url, method: p.request.method, status: 0 });
570
+ break;
571
+ }
572
+ case 'Network.responseReceived': {
573
+ const p = params;
574
+ const req = pending.get(p.requestId);
575
+ if (req)
576
+ req.status = p.response?.status ?? 0;
577
+ break;
578
+ }
579
+ case 'Network.loadingFinished': {
580
+ const p = params;
581
+ const req = pending.get(p.requestId);
582
+ if (!req)
583
+ return;
584
+ pending.delete(p.requestId);
585
+ maybeFallback({ source: 'service', url: req.url, method: req.method, status: req.status });
586
+ break;
587
+ }
588
+ case 'Network.loadingFailed': {
589
+ const p = params;
590
+ const req = pending.get(p.requestId);
591
+ if (!req)
592
+ return;
593
+ pending.delete(p.requestId);
594
+ maybeFallback({
595
+ source: 'service',
596
+ url: req.url,
597
+ method: req.method,
598
+ status: req.status,
599
+ errorText: p.canceled ? 'canceled' : (p.errorText || 'failed'),
600
+ });
601
+ break;
602
+ }
603
+ default:
604
+ break;
605
+ }
606
+ };
607
+ wc.debugger.on('message', onMessage);
608
+ attach.add(() => {
609
+ try {
610
+ wc.debugger.removeListener('message', onMessage);
611
+ }
612
+ catch { /* wc gone */ }
613
+ });
614
+ const onDetach = () => { if (simWc === wc) {
615
+ simWc = null;
616
+ } };
617
+ wc.debugger.on('detach', onDetach);
618
+ attach.add(() => {
619
+ try {
620
+ wc.debugger.removeListener('detach', onDetach);
621
+ }
622
+ catch { /* wc gone */ }
623
+ });
624
+ const onDestroyed = () => {
625
+ if (simWc === wc) {
626
+ simWc = null;
627
+ const ad = attachDisposables;
628
+ attachDisposables = null;
629
+ if (ad)
630
+ void ad.disposeAll().catch(() => { });
631
+ }
632
+ };
633
+ // Route the destroyed-teardown through the connection registry when one is
634
+ // available (deterministic disposal on wc destroy / connection reset);
635
+ // otherwise keep the bespoke `once('destroyed')` watcher. WHAT onDestroyed
636
+ // does is unchanged — only WHERE it's registered.
637
+ const reg = bridge.connections;
638
+ if (reg) {
639
+ const owned = reg.acquire(wc).own(onDestroyed);
640
+ attach.add(() => owned.dispose());
641
+ }
642
+ else {
643
+ wc.once('destroyed', onDestroyed);
644
+ attach.add(() => {
645
+ try {
646
+ wc.removeListener('destroyed', onDestroyed);
647
+ }
648
+ catch { /* wc gone */ }
649
+ });
650
+ }
651
+ void wc.debugger.sendCommand('Network.enable').catch((err) => {
652
+ console.warn('[network-forward] Network.enable failed:', err instanceof Error ? err.message : err);
653
+ });
654
+ }
655
+ /**
656
+ * Route a completed request to EXACTLY ONE sink, per the native-sink state
657
+ * machine (MAJOR 1 — no double-display):
658
+ * - 'ready' : native path already rendered it → suppress console.
659
+ * - 'degraded' : native abandoned → console.
660
+ * - 'idle' : no host configured → console (the native queue never flushes).
661
+ * - 'probing' : undecided → buffer the record; we'll either drop it (on ready)
662
+ * or flush it to console (on degrade). Bounded so it can't grow.
663
+ */
664
+ function maybeFallback(record) {
665
+ // 'idle' but a host is resolvable (set via the bridge, applyDevtoolsHost not
666
+ // run): the native path is in play, so promote to 'probing' instead of
667
+ // console — otherwise this completion would later double-render natively.
668
+ if (sink === 'idle' && resolveDevtoolsWc())
669
+ beginProbing();
670
+ switch (sink) {
671
+ case 'ready':
672
+ return;
673
+ case 'probing':
674
+ if (probeConsoleBuffer.length >= MAX_PENDING)
675
+ probeConsoleBuffer.shift();
676
+ probeConsoleBuffer.push(record);
677
+ return;
678
+ case 'degraded':
679
+ case 'idle':
680
+ default:
681
+ forwardToConsole(record);
682
+ }
683
+ }
684
+ /** Apply a new (or cleared) DevTools host: reset sink state and (re)probe. */
685
+ function applyDevtoolsHost(wc) {
686
+ devtoolsHostDisposable?.dispose();
687
+ devtoolsHostDisposable = null;
688
+ devtoolsWc = wc && !wc.isDestroyed() ? wc : null;
689
+ // Reset the native-sink state machine for the new host.
690
+ if (readyTimeoutTimer) {
691
+ clearTimeout(readyTimeoutTimer);
692
+ readyTimeoutTimer = null;
693
+ }
694
+ if (readyRetryTimer) {
695
+ clearTimeout(readyRetryTimer);
696
+ readyRetryTimer = null;
697
+ }
698
+ // Records buffered while probing the OLD host are stale — drop, don't flush
699
+ // (their native copies were already queued; on a host swap we restart clean).
700
+ probeConsoleBuffer = [];
701
+ if (!devtoolsWc) {
702
+ // No host → 'idle': completions go straight to console; native queue is
703
+ // moot, drop it so it can't double-render if a host later appears.
704
+ sink = 'idle';
705
+ dispatchQueue = [];
706
+ return;
707
+ }
708
+ // Host present: watch it so its destruction equals setDevtoolsHost(null)
709
+ // WITHOUT touching view-manager (host-destroyed cleanup lives here). Begin
710
+ // probing and flush anything already queued.
711
+ const host = devtoolsWc;
712
+ const onHostDestroyed = () => { applyDevtoolsHost(null); };
713
+ // Route host-destroyed teardown through the connection registry when present
714
+ // (the Connection fires onHostDestroyed on wc destroy / reset, and the
715
+ // returned Disposable releases the ownership early on host swap/clear);
716
+ // otherwise keep the bespoke `once('destroyed')` watcher. The
717
+ // `typeof host.once === 'function'` guard stays on the fallback so minimal
718
+ // test fakes / odd hosts don't throw.
719
+ const reg = bridge.connections;
720
+ // `acquire(host)` internally arms `host.once('destroyed')`, so it must be
721
+ // gated by the SAME `typeof host.once === 'function'` guard the fallback
722
+ // uses — otherwise a minimal/fake DevTools host (no emitter) throws on the
723
+ // connection path where the fallback would safely no-op.
724
+ if (reg && typeof host.once === 'function') {
725
+ const owned = reg.acquire(host).own(onHostDestroyed);
726
+ devtoolsHostDisposable = toDisposable(() => owned.dispose());
727
+ }
728
+ else {
729
+ if (typeof host.once === 'function')
730
+ host.once('destroyed', onHostDestroyed);
731
+ devtoolsHostDisposable = toDisposable(() => {
732
+ try {
733
+ host.removeListener?.('destroyed', onHostDestroyed);
734
+ }
735
+ catch { /* gone */ }
736
+ });
737
+ }
738
+ beginProbing();
739
+ if (dispatchQueue.length > 0)
740
+ scheduleFlush();
741
+ }
742
+ registry.add(() => {
743
+ devtoolsHostDisposable?.dispose();
744
+ devtoolsHostDisposable = null;
745
+ if (readyTimeoutTimer) {
746
+ clearTimeout(readyTimeoutTimer);
747
+ readyTimeoutTimer = null;
748
+ }
749
+ if (readyRetryTimer) {
750
+ clearTimeout(readyRetryTimer);
751
+ readyRetryTimer = null;
752
+ }
753
+ });
754
+ registry.add(() => detachSimulator());
755
+ return {
756
+ attachSimulator,
757
+ detachSimulator,
758
+ setDevtoolsHost: (wc) => applyDevtoolsHost(wc),
759
+ // report() has no observing debugger, so there's no live CDP event to push
760
+ // natively — surface it via the console fallback line.
761
+ report: (record) => forwardToConsole(record),
762
+ dispose: () => registry.disposeAll(),
763
+ };
764
+ }
765
+ //# sourceMappingURL=index.js.map