@dimina-kit/devtools 0.4.0 → 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.
- package/README.md +51 -660
- package/dist/main/api.d.ts +2 -1
- package/dist/main/app/app.d.ts +41 -12
- package/dist/main/app/app.js +299 -514
- package/dist/main/app/bootstrap.d.ts +8 -5
- package/dist/main/app/bootstrap.js +15 -15
- package/dist/main/app/editor-view.d.ts +21 -0
- package/dist/main/app/editor-view.js +71 -0
- package/dist/main/app/global-mirrors.d.ts +23 -0
- package/dist/main/app/global-mirrors.js +37 -0
- package/dist/main/app/host-sidebar-default.d.ts +19 -0
- package/dist/main/app/host-sidebar-default.js +44 -0
- package/dist/main/app/lifecycle.d.ts +12 -1
- package/dist/main/app/lifecycle.js +27 -1
- package/dist/main/app/menu-setup.d.ts +13 -0
- package/dist/main/app/menu-setup.js +49 -0
- package/dist/main/app/native-overview.d.ts +1 -1
- package/dist/main/app/open-project-ipc.d.ts +13 -0
- package/dist/main/app/open-project-ipc.js +21 -0
- package/dist/main/app/project-list-surface.d.ts +31 -0
- package/dist/main/app/project-list-surface.js +20 -0
- package/dist/main/app/project-window.d.ts +36 -0
- package/dist/main/app/project-window.js +93 -0
- package/dist/main/app/servers.d.ts +17 -0
- package/dist/main/app/servers.js +60 -0
- package/dist/main/app/ui-extension-targets.d.ts +42 -0
- package/dist/main/app/ui-extension-targets.js +101 -0
- package/dist/main/app/window-close-reveal.harness.d.ts +34 -0
- package/dist/main/app/window-close-reveal.harness.js +65 -0
- package/dist/main/app/window-events.d.ts +15 -0
- package/dist/main/app/window-events.js +45 -0
- package/dist/main/app/window-runtime-services.d.ts +41 -0
- package/dist/main/app/window-runtime-services.js +197 -0
- package/dist/main/app/workbench-modules.d.ts +24 -0
- package/dist/main/app/workbench-modules.js +51 -0
- package/dist/main/app/workbench-window.d.ts +85 -0
- package/dist/main/app/workbench-window.js +310 -0
- package/dist/main/index.bundle.js +11712 -9986
- package/dist/main/index.js +49 -1
- package/dist/main/ipc/app-lifecycle.d.ts +1 -31
- package/dist/main/ipc/app-lifecycle.js +1 -53
- package/dist/main/ipc/app.d.ts +4 -1
- package/dist/main/ipc/app.js +4 -3
- package/dist/main/ipc/bridge-router.d.ts +19 -116
- package/dist/main/ipc/bridge-router.js +28 -2128
- package/dist/main/ipc/device-picker.d.ts +16 -0
- package/dist/main/ipc/device-picker.js +27 -0
- package/dist/main/ipc/index.d.ts +5 -0
- package/dist/main/ipc/index.js +5 -0
- package/dist/main/ipc/internal-devtools.d.ts +16 -0
- package/dist/main/ipc/internal-devtools.js +10 -0
- package/dist/main/ipc/page-scroll.d.ts +1 -13
- package/dist/main/ipc/page-scroll.js +1 -16
- package/dist/main/ipc/popover.d.ts +4 -1
- package/dist/main/ipc/popover.js +26 -19
- package/dist/main/ipc/project-create.d.ts +16 -0
- package/dist/main/ipc/project-create.js +27 -0
- package/dist/main/ipc/project-fs.d.ts +38 -3
- package/dist/main/ipc/project-fs.js +53 -22
- package/dist/main/ipc/projects.d.ts +3 -3
- package/dist/main/ipc/projects.js +47 -11
- package/dist/main/ipc/session-listener-bag.d.ts +1 -40
- package/dist/main/ipc/session-listener-bag.js +1 -39
- package/dist/main/ipc/session.d.ts +4 -1
- package/dist/main/ipc/session.js +26 -26
- package/dist/main/ipc/settings.d.ts +4 -1
- package/dist/main/ipc/settings.js +17 -19
- package/dist/main/ipc/simulator-module.d.ts +9 -3
- package/dist/main/ipc/simulator-module.js +13 -10
- package/dist/main/ipc/simulator.d.ts +4 -1
- package/dist/main/ipc/simulator.js +12 -18
- package/dist/main/ipc/tooltip.d.ts +14 -0
- package/dist/main/ipc/tooltip.js +29 -0
- package/dist/main/ipc/views.d.ts +19 -2
- package/dist/main/ipc/views.js +76 -8
- package/dist/main/runtime/devtools-backend-before-quit.harness.d.ts +43 -0
- package/dist/main/runtime/devtools-backend-before-quit.harness.js +87 -0
- package/dist/main/runtime/devtools-backend.js +60 -4
- package/dist/main/runtime/devtools-runtime-mock.harness.d.ts +21 -0
- package/dist/main/runtime/devtools-runtime-mock.harness.js +313 -0
- package/dist/main/runtime/miniapp-runtime.d.ts +91 -22
- package/dist/main/services/app-services.d.ts +62 -0
- package/dist/main/services/app-services.js +64 -0
- package/dist/main/services/automation/connection-target.d.ts +36 -0
- package/dist/main/services/automation/connection-target.js +29 -0
- package/dist/main/services/automation/console-bridge.d.ts +28 -0
- package/dist/main/services/automation/console-bridge.js +91 -0
- package/dist/main/services/automation/handlers/app.js +4 -4
- package/dist/main/services/automation/handlers/element.js +55 -21
- package/dist/main/services/automation/index.d.ts +12 -1
- package/dist/main/services/automation/index.js +54 -133
- package/dist/main/services/cdp-session/index.d.ts +87 -0
- package/dist/main/services/cdp-session/index.js +173 -0
- package/dist/main/services/compile-standby.js +4 -0
- package/dist/main/services/console-forward/cdp-inject.d.ts +18 -0
- package/dist/main/services/console-forward/cdp-inject.js +30 -0
- package/dist/main/services/console-forward/global-console-mirror.d.ts +33 -0
- package/dist/main/services/console-forward/global-console-mirror.js +71 -0
- package/dist/main/services/console-forward/global-diagnostics-mirror.d.ts +33 -0
- package/dist/main/services/console-forward/global-diagnostics-mirror.js +73 -0
- package/dist/main/services/console-forward/index.d.ts +1 -46
- package/dist/main/services/console-forward/index.js +1 -185
- package/dist/main/services/console-forward/internal-log.d.ts +2 -0
- package/dist/main/services/console-forward/internal-log.js +2 -0
- package/dist/main/services/console-forward/open-gated-relay.d.ts +67 -0
- package/dist/main/services/console-forward/open-gated-relay.js +55 -0
- package/dist/main/services/default-adapter.js +1 -1
- package/dist/main/services/diagnostics/index.d.ts +1 -48
- package/dist/main/services/diagnostics/index.js +1 -60
- package/dist/main/services/dimina-resource-server.d.ts +1 -6
- package/dist/main/services/dimina-resource-server.js +1 -98
- package/dist/main/services/elements-forward/index.d.ts +132 -15
- package/dist/main/services/elements-forward/index.js +155 -304
- package/dist/main/services/fs-bridge-protocol.d.ts +61 -0
- package/dist/main/services/fs-bridge-protocol.js +34 -0
- package/dist/main/services/fs-watch-sse.d.ts +40 -0
- package/dist/main/services/fs-watch-sse.js +113 -0
- package/dist/main/services/http-json.d.ts +9 -0
- package/dist/main/services/http-json.js +6 -0
- package/dist/main/services/layout/index.d.ts +11 -0
- package/dist/main/services/layout/index.js +23 -0
- package/dist/main/services/mcp/connection-owner.d.ts +38 -0
- package/dist/main/services/mcp/connection-owner.js +79 -0
- package/dist/main/services/mcp/index.d.ts +3 -1
- package/dist/main/services/mcp/index.js +4 -3
- package/dist/main/services/mcp/opened-project.d.ts +88 -0
- package/dist/main/services/mcp/opened-project.js +60 -0
- package/dist/main/services/mcp/server.d.ts +2 -1
- package/dist/main/services/mcp/server.js +29 -4
- package/dist/main/services/mcp/target-manager.d.ts +71 -23
- package/dist/main/services/mcp/target-manager.js +269 -138
- package/dist/main/services/mcp/target-selection.d.ts +47 -0
- package/dist/main/services/mcp/target-selection.js +71 -0
- package/dist/main/services/mcp/tools/context-tools.js +20 -5
- package/dist/main/services/mcp/tools/project-tools.d.ts +72 -0
- package/dist/main/services/mcp/tools/project-tools.js +133 -0
- package/dist/main/services/module.d.ts +13 -5
- package/dist/main/services/network-forward/body-cache.d.ts +72 -0
- package/dist/main/services/network-forward/body-cache.js +179 -0
- package/dist/main/services/network-forward/frontend-dispatch.d.ts +91 -0
- package/dist/main/services/network-forward/frontend-dispatch.js +148 -0
- package/dist/main/services/network-forward/global-body-gate.d.ts +46 -0
- package/dist/main/services/network-forward/global-body-gate.js +93 -0
- package/dist/main/services/network-forward/global-mirror-test-fixtures.d.ts +48 -0
- package/dist/main/services/network-forward/global-mirror-test-fixtures.js +96 -0
- package/dist/main/services/network-forward/http.d.ts +47 -0
- package/dist/main/services/network-forward/http.js +162 -0
- package/dist/main/services/network-forward/index.d.ts +135 -15
- package/dist/main/services/network-forward/index.js +854 -224
- package/dist/main/services/network-forward/request-ids.d.ts +5 -0
- package/dist/main/services/network-forward/request-ids.js +5 -0
- package/dist/main/services/network-forward/user-facing.d.ts +21 -0
- package/dist/main/services/network-forward/user-facing.js +50 -0
- package/dist/main/services/network-forward/websocket.d.ts +36 -0
- package/dist/main/services/network-forward/websocket.js +120 -0
- package/dist/main/services/notifications/renderer-notifier.d.ts +71 -9
- package/dist/main/services/notifications/renderer-notifier.js +45 -8
- package/dist/main/services/projects/create-project-service.js +19 -5
- package/dist/main/services/projects/local-provider.js +8 -4
- package/dist/main/services/projects/project-repository.d.ts +59 -7
- package/dist/main/services/projects/project-repository.js +285 -51
- package/dist/main/services/projects/types.d.ts +44 -34
- package/dist/main/services/render-inspect/index.d.ts +12 -2
- package/dist/main/services/render-inspect/index.js +21 -102
- package/dist/main/services/safe-area/index.d.ts +21 -6
- package/dist/main/services/safe-area/index.js +92 -46
- package/dist/main/services/service-host-pool/pool.d.ts +1 -196
- package/dist/main/services/service-host-pool/pool.js +1 -363
- package/dist/main/services/settings/index.d.ts +7 -1
- package/dist/main/services/settings/index.js +22 -2
- package/dist/main/services/simulator/custom-apis.d.ts +1 -42
- package/dist/main/services/simulator/custom-apis.js +1 -46
- package/dist/main/services/simulator/referer.d.ts +1 -9
- package/dist/main/services/simulator/referer.js +1 -19
- package/dist/main/services/simulator/ui-extensions.d.ts +21 -0
- package/dist/main/services/simulator/ui-extensions.js +176 -0
- package/dist/main/services/simulator-appdata/index.d.ts +6 -0
- package/dist/main/services/simulator-appdata/index.js +21 -2
- package/dist/main/services/simulator-current-page/index.d.ts +5 -3
- package/dist/main/services/simulator-current-page/index.js +8 -1
- package/dist/main/services/simulator-storage/index.d.ts +34 -5
- package/dist/main/services/simulator-storage/index.js +73 -71
- package/dist/main/services/simulator-storage/service-storage-ops.d.ts +1 -39
- package/dist/main/services/simulator-storage/service-storage-ops.js +1 -66
- package/dist/main/services/simulator-storage/window-scope.d.ts +20 -0
- package/dist/main/services/simulator-storage/window-scope.js +20 -0
- package/dist/main/services/simulator-temp-files/disk.d.ts +1 -54
- package/dist/main/services/simulator-temp-files/disk.js +1 -237
- package/dist/main/services/simulator-temp-files/fs-channels.d.ts +1 -68
- package/dist/main/services/simulator-temp-files/fs-channels.js +1 -107
- package/dist/main/services/simulator-temp-files/index.d.ts +1 -32
- package/dist/main/services/simulator-temp-files/index.js +1 -181
- package/dist/main/services/simulator-temp-files/request-handler.d.ts +1 -29
- package/dist/main/services/simulator-temp-files/request-handler.js +1 -177
- package/dist/main/services/simulator-temp-files/resolver.d.ts +1 -21
- package/dist/main/services/simulator-temp-files/resolver.js +1 -21
- package/dist/main/services/simulator-temp-files/store.d.ts +1 -13
- package/dist/main/services/simulator-temp-files/store.js +1 -23
- package/dist/main/services/update/github-release-checker.js +22 -3
- package/dist/main/services/update/update-manager.d.ts +21 -4
- package/dist/main/services/update/update-manager.js +55 -11
- package/dist/main/services/views/clear-console-filter.d.ts +44 -0
- package/dist/main/services/views/clear-console-filter.js +86 -0
- package/dist/main/services/views/console-filter-test-fixtures.d.ts +94 -0
- package/dist/main/services/views/console-filter-test-fixtures.js +82 -0
- package/dist/main/services/views/console-filter.d.ts +94 -0
- package/dist/main/services/views/console-filter.js +190 -0
- package/dist/main/services/views/devtools-tabs.d.ts +24 -4
- package/dist/main/services/views/devtools-tabs.js +40 -21
- package/dist/main/services/views/frontend-bootstrap-gate.d.ts +59 -0
- package/dist/main/services/views/frontend-bootstrap-gate.js +117 -0
- package/dist/main/services/views/host-dialog-port-channel.d.ts +18 -0
- package/dist/main/services/views/host-dialog-port-channel.js +15 -0
- package/dist/main/services/views/host-dialog-session-runtime.d.ts +10 -0
- package/dist/main/services/views/host-dialog-session-runtime.js +17 -0
- package/dist/main/services/views/host-dialog-view.d.ts +95 -0
- package/dist/main/services/views/host-dialog-view.js +147 -0
- package/dist/main/services/views/host-sidebar-port-channel.d.ts +18 -0
- package/dist/main/services/views/host-sidebar-port-channel.js +15 -0
- package/dist/main/services/views/host-sidebar-session-runtime.d.ts +10 -0
- package/dist/main/services/views/host-sidebar-session-runtime.js +17 -0
- package/dist/main/services/views/host-sidebar-view.d.ts +59 -0
- package/dist/main/services/views/host-sidebar-view.js +44 -0
- package/dist/main/services/views/host-slot-port-channel.d.ts +124 -0
- package/dist/main/services/views/host-slot-port-channel.js +289 -0
- package/dist/main/services/views/host-slot-session-runtime.d.ts +40 -0
- package/dist/main/services/views/host-slot-session-runtime.js +50 -0
- package/dist/main/services/views/host-slot-view.d.ts +109 -0
- package/dist/main/services/views/host-slot-view.js +147 -0
- package/dist/main/services/views/host-toolbar-port-channel.d.ts +6 -91
- package/dist/main/services/views/host-toolbar-port-channel.js +10 -243
- package/dist/main/services/views/host-toolbar-session-runtime.d.ts +5 -30
- package/dist/main/services/views/host-toolbar-session-runtime.js +9 -47
- package/dist/main/services/views/host-toolbar-view.d.ts +34 -7
- package/dist/main/services/views/host-toolbar-view.js +30 -237
- package/dist/main/services/views/managed-web-contents-view.d.ts +48 -0
- package/dist/main/services/views/managed-web-contents-view.js +143 -0
- package/dist/main/services/views/miniapp-partition.d.ts +1 -89
- package/dist/main/services/views/miniapp-partition.js +1 -158
- package/dist/main/services/views/native-simulator-devtools-host.js +206 -68
- package/dist/main/services/views/native-simulator-view.d.ts +6 -0
- package/dist/main/services/views/native-simulator-view.js +47 -40
- package/dist/main/services/views/native-view-tree.d.ts +25 -0
- package/dist/main/services/views/native-view-tree.js +69 -0
- package/dist/main/services/views/overlay-panels-view.d.ts +63 -4
- package/dist/main/services/views/overlay-panels-view.js +257 -108
- package/dist/main/services/views/placement-reconciler.d.ts +18 -18
- package/dist/main/services/views/placement-reconciler.js +91 -29
- package/dist/main/services/views/placement-test-driver.d.ts +27 -0
- package/dist/main/services/views/placement-test-driver.js +29 -0
- package/dist/main/services/views/refresh-styles.d.ts +10 -0
- package/dist/main/services/views/refresh-styles.js +57 -0
- package/dist/main/services/views/simulator-session-policy.d.ts +1 -27
- package/dist/main/services/views/simulator-session-policy.js +1 -98
- package/dist/main/services/views/view-manager-devtools-host-test-fixtures.d.ts +76 -0
- package/dist/main/services/views/view-manager-devtools-host-test-fixtures.js +184 -0
- package/dist/main/services/views/view-manager.d.ts +69 -88
- package/dist/main/services/views/view-manager.js +62 -7
- package/dist/main/services/views/workbench-view.d.ts +2 -0
- package/dist/main/services/views/workbench-view.js +104 -17
- package/dist/main/services/window-contexts/context-router.d.ts +36 -0
- package/dist/main/services/window-contexts/context-router.js +55 -0
- package/dist/main/services/workbench-coi-host.d.ts +52 -0
- package/dist/main/services/workbench-coi-host.js +227 -0
- package/dist/main/services/workbench-coi-server.d.ts +11 -0
- package/dist/main/services/workbench-coi-server.js +126 -126
- package/dist/main/services/workbench-coi-shutdown.d.ts +40 -0
- package/dist/main/services/workbench-coi-shutdown.js +69 -0
- package/dist/main/services/workbench-coi-static.d.ts +23 -0
- package/dist/main/services/workbench-coi-static.js +87 -0
- package/dist/main/services/workbench-context.d.ts +88 -8
- package/dist/main/services/workbench-context.js +54 -6
- package/dist/main/services/workspace/compile-log-buffer.d.ts +40 -0
- package/dist/main/services/workspace/compile-log-buffer.js +41 -0
- package/dist/main/services/workspace/compile-mode-adapter.d.ts +18 -0
- package/dist/main/services/workspace/compile-mode-adapter.js +57 -0
- package/dist/main/services/workspace/compile-mode-store.d.ts +25 -0
- package/dist/main/services/workspace/compile-mode-store.js +71 -0
- package/dist/main/services/workspace/open-project-guards.d.ts +16 -0
- package/dist/main/services/workspace/open-project-guards.js +42 -0
- package/dist/main/services/workspace/rebuild-status.d.ts +37 -0
- package/dist/main/services/workspace/rebuild-status.js +36 -0
- package/dist/main/services/workspace/session-status-store.d.ts +47 -0
- package/dist/main/services/workspace/session-status-store.js +73 -0
- package/dist/main/services/workspace/status-tap.d.ts +20 -0
- package/dist/main/services/workspace/status-tap.js +32 -0
- package/dist/main/services/workspace/workspace-compile-modes.d.ts +59 -0
- package/dist/main/services/workspace/workspace-compile-modes.js +93 -0
- package/dist/main/services/workspace/workspace-lifecycle-race.testutil.d.ts +22 -0
- package/dist/main/services/workspace/workspace-lifecycle-race.testutil.js +62 -0
- package/dist/main/services/workspace/workspace-referer.d.ts +17 -0
- package/dist/main/services/workspace/workspace-referer.js +26 -0
- package/dist/main/services/workspace/workspace-service.d.ts +7 -5
- package/dist/main/services/workspace/workspace-service.js +96 -135
- package/dist/main/services/workspace/workspace-thumbnails.d.ts +22 -0
- package/dist/main/services/workspace/workspace-thumbnails.js +67 -0
- package/dist/main/utils/ipc-context-source.d.ts +40 -0
- package/dist/main/utils/ipc-context-source.js +23 -0
- package/dist/main/utils/ipc-registry.d.ts +46 -21
- package/dist/main/utils/ipc-registry.js +166 -76
- package/dist/main/utils/paths.d.ts +18 -0
- package/dist/main/utils/paths.js +18 -0
- package/dist/main/utils/sender-policy.d.ts +10 -1
- package/dist/main/utils/sender-policy.js +42 -4
- package/dist/main/utils/theme.d.ts +18 -8
- package/dist/main/utils/theme.js +19 -9
- package/dist/main/windows/internal-devtools-window/index.d.ts +66 -0
- package/dist/main/windows/internal-devtools-window/index.js +160 -0
- package/dist/main/windows/main-window/create.d.ts +16 -0
- package/dist/main/windows/main-window/create.js +19 -11
- package/dist/main/windows/main-window/events.d.ts +1 -1
- package/dist/main/windows/main-window/events.js +26 -13
- package/dist/main/windows/main-window/index.d.ts +1 -1
- package/dist/main/windows/main-window/index.js +1 -1
- package/dist/main/windows/service-host-window/create.d.ts +1 -92
- package/dist/main/windows/service-host-window/create.js +1 -154
- package/dist/native-host/common/common.js +173 -53
- package/dist/native-host/container/pageFrame.css +1 -1
- package/dist/native-host/render/render.js +8278 -3659
- package/dist/native-host/service/service.js +2 -2
- package/dist/preload/index.d.ts +2 -0
- package/dist/preload/index.js +1 -0
- package/dist/preload/instrumentation/app-data.d.ts +2 -2
- package/dist/preload/instrumentation/app-data.js +1 -1
- package/dist/preload/instrumentation/wxml.js +1 -1
- package/dist/preload/runtime/bridge.d.ts +3 -3
- package/dist/preload/runtime/bridge.js +2 -2
- package/dist/preload/runtime/clipboard.d.ts +6 -0
- package/dist/preload/runtime/clipboard.js +12 -0
- package/dist/preload/runtime/host-dialog-advertiser.d.ts +22 -0
- package/dist/preload/runtime/host-dialog-advertiser.js +37 -0
- package/dist/preload/runtime/host-dialog-port.d.ts +44 -0
- package/dist/preload/runtime/host-dialog-port.js +53 -0
- package/dist/preload/runtime/host-dialog-runtime.d.ts +28 -0
- package/dist/preload/runtime/host-dialog-runtime.js +36 -0
- package/dist/preload/runtime/host-sidebar-advertiser.d.ts +19 -0
- package/dist/preload/runtime/host-sidebar-advertiser.js +35 -0
- package/dist/preload/runtime/host-sidebar-port.d.ts +44 -0
- package/dist/preload/runtime/host-sidebar-port.js +53 -0
- package/dist/preload/runtime/host-sidebar-runtime.d.ts +28 -0
- package/dist/preload/runtime/host-sidebar-runtime.js +36 -0
- package/dist/preload/runtime/host-slot-port-bridge.d.ts +18 -0
- package/dist/preload/runtime/host-slot-port-bridge.js +112 -0
- package/dist/preload/runtime/host-toolbar-advertiser.d.ts +5 -13
- package/dist/preload/runtime/host-toolbar-advertiser.js +18 -38
- package/dist/preload/runtime/host-toolbar-port.d.ts +9 -0
- package/dist/preload/runtime/host-toolbar-port.js +11 -95
- package/dist/preload/runtime/native-host.d.ts +14 -0
- package/dist/preload/runtime/native-host.js +5 -8
- package/dist/preload/runtime/node-bindings.d.ts +18 -0
- package/dist/preload/runtime/node-bindings.js +44 -0
- package/dist/preload/runtime/temp-files.js +9 -2
- package/dist/preload/runtime/wait-for-slot-root.d.ts +23 -0
- package/dist/preload/runtime/wait-for-slot-root.js +80 -0
- package/dist/preload/shared/api-compat.js +63 -41
- package/dist/preload/windows/host-dialog-runtime.cjs +360 -0
- package/dist/preload/windows/host-dialog-runtime.cjs.map +7 -0
- package/dist/preload/windows/host-dialog-runtime.d.ts +2 -0
- package/dist/preload/windows/host-dialog-runtime.js +13 -0
- package/dist/preload/windows/host-sidebar-runtime.cjs +361 -0
- package/dist/preload/windows/host-sidebar-runtime.cjs.map +7 -0
- package/dist/preload/windows/host-sidebar-runtime.d.ts +2 -0
- package/dist/preload/windows/host-sidebar-runtime.js +13 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs +142 -52
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +3 -3
- package/dist/preload/windows/main.cjs +4922 -1246
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/main.css +428 -0
- package/dist/preload/windows/main.css.map +7 -0
- package/dist/preload/windows/simulator.cjs +376 -322
- package/dist/preload/windows/simulator.cjs.map +4 -4
- package/dist/preload/windows/simulator.js +376 -322
- package/dist/render-host/pageFrame.html +4 -4
- package/dist/render-host/preload.cjs +30 -0
- package/dist/render-host/render-inspect.js +184 -115
- package/dist/renderer/assets/button-CB2MWjHY.js +2 -0
- package/dist/renderer/assets/constants-CkoGPQTS.js +2 -0
- package/dist/renderer/assets/constants-zQZ2-y21.js +2 -0
- package/dist/renderer/assets/createLucideIcon-cZ9FlZZw.js +2 -0
- package/dist/renderer/assets/devicePicker-H2eIO4e1.js +2 -0
- package/dist/renderer/assets/dialog-pHzEA8Vx.js +46 -0
- package/dist/renderer/assets/dist-B5HADirS.js +2 -0
- package/dist/renderer/assets/dist-DIFlRMKe.js +2 -0
- package/dist/renderer/assets/hostSidebarDefault-DRWh6OCt.js +2 -0
- package/dist/renderer/assets/index-CEL_XFge.js +2 -0
- package/dist/renderer/assets/input-CAObw95L.js +2 -0
- package/dist/renderer/assets/ipc-channels-overlays-gIPUFnqc.js +2 -0
- package/dist/renderer/assets/jsx-runtime-DICEQH_0.css +1 -0
- package/dist/renderer/assets/jsx-runtime-DIw5wnYl.js +9 -0
- package/dist/renderer/assets/plus-BQ9u5EbL.js +2 -0
- package/dist/renderer/assets/popover-Cw7G2Ctp.js +2 -0
- package/dist/renderer/assets/presets-DMZOb9ay.js +2 -0
- package/dist/renderer/assets/project-api-YTOr_Yvo.js +2 -0
- package/dist/renderer/assets/projectCreateDialog-BmLqiYxn.js +2 -0
- package/dist/renderer/assets/search-BMvuYF48.js +2 -0
- package/dist/renderer/assets/select-nFP95TxM.js +2 -0
- package/dist/renderer/assets/settings-CnpVfSWt.js +2 -0
- package/dist/renderer/assets/settings-api-Ccsf-Nsj.js +2 -0
- package/dist/renderer/assets/settings-tab-bar-CAzrv32m.js +2 -0
- package/dist/renderer/assets/smartphone-Bb2GdIKy.js +2 -0
- package/dist/renderer/assets/tabs-CPKN3Mmq.js +2 -0
- package/dist/renderer/assets/tooltip-D6AxXRk5.js +2 -0
- package/dist/renderer/assets/types-Bh0hA7El.js +2 -0
- package/dist/renderer/assets/updateDialog-CwndJ0Bl.js +2 -0
- package/dist/renderer/assets/utils-sEB5OZEQ.js +2 -0
- package/dist/renderer/assets/view-api-B-2zPBR5.js +2 -0
- package/dist/renderer/assets/view-ids-BfaDVV5P.js +2 -0
- package/dist/renderer/assets/workbench-C0Jkdlps.js +5 -0
- package/dist/renderer/assets/workbenchSettings-YD0Ctbm_.js +8 -0
- package/dist/renderer/entries/device-picker/index.html +28 -0
- package/dist/renderer/entries/host-sidebar-default/index.html +22 -0
- package/dist/renderer/entries/main/index.html +16 -7
- package/dist/renderer/entries/popover/index.html +16 -6
- package/dist/renderer/entries/project-create-dialog/index.html +27 -0
- package/dist/renderer/entries/settings/index.html +10 -5
- package/dist/renderer/entries/tooltip/index.html +21 -0
- package/dist/renderer/entries/update-dialog/index.html +25 -0
- package/dist/renderer/entries/workbench/index.html +49 -0
- package/dist/renderer/entries/workbench-settings/index.html +12 -5
- package/dist/service-host/appdata-set-data.cjs +39 -0
- package/dist/service-host/host-env-update.cjs +23 -0
- package/dist/service-host/preload.cjs +54 -48
- package/dist/service-host/service-message-queue.cjs +71 -0
- package/dist/service-host/sync-impls/menu-button-geometry.js +16 -5
- package/dist/service-host/sync-impls/menu-button.js +5 -3
- package/dist/service-host/sync-impls/system-info.js +31 -9
- package/dist/shared/bridge-channels.d.ts +1 -416
- package/dist/shared/bridge-channels.js +1 -89
- package/dist/shared/compile-mode-state.d.ts +73 -0
- package/dist/shared/compile-mode-state.js +100 -0
- package/dist/shared/compile-modes.d.ts +152 -0
- package/dist/shared/compile-modes.js +237 -0
- package/dist/shared/constants.d.ts +22 -0
- package/dist/shared/constants.js +22 -0
- package/dist/shared/dmb-resource-url.d.ts +2 -0
- package/dist/shared/dmb-resource-url.js +2 -0
- package/dist/shared/ipc-channels-overlays.d.ts +162 -0
- package/dist/shared/ipc-channels-overlays.js +203 -0
- package/dist/shared/ipc-channels.d.ts +24 -154
- package/dist/shared/ipc-channels.js +48 -96
- package/dist/shared/ipc-schemas.d.ts +143 -54
- package/dist/shared/ipc-schemas.js +137 -45
- package/dist/shared/node-bindings.d.ts +2 -0
- package/dist/shared/node-bindings.js +2 -0
- package/dist/shared/project-window-types.d.ts +42 -0
- package/dist/shared/project-window-types.js +2 -0
- package/dist/shared/simulator-route.d.ts +8 -0
- package/dist/shared/simulator-route.js +15 -0
- package/dist/shared/simulator-ui.d.ts +61 -0
- package/dist/shared/simulator-ui.js +12 -0
- package/dist/shared/types.d.ts +126 -41
- package/dist/shared/view-ids.d.ts +10 -0
- package/dist/shared/view-ids.js +23 -0
- package/dist/simulator/assets/bridge-channels-B3JAmRfl.js +2 -0
- package/dist/simulator/assets/device-shell-ATEhHV2_.css +1 -0
- package/dist/simulator/assets/device-shell-DucLepkm.js +431 -0
- package/dist/simulator/assets/simulator-B4rbcOVt.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-DFo7fhi5.js +2 -0
- package/dist/simulator/assets/simulator-ui-BupATgKo.css +1 -0
- package/dist/simulator/assets/simulator-ui-i3GCc7YJ.js +2 -0
- package/dist/simulator/simulator.html +4 -3
- package/dist/simulator/temp-files.d.ts +35 -0
- package/dist/simulator/temp-files.js +121 -0
- package/dist/simulator/types.d.ts +50 -0
- package/dist/simulator/types.js +2 -0
- package/dist/vscode-workbench/assets/__vite-browser-external-JIkWewFf.js +1 -0
- package/dist/vscode-workbench/assets/{dist-TpGpmMGi.js → dist-CXG-u1VB.js} +3 -3
- package/dist/vscode-workbench/assets/fs-core-worker-urls-DZ1nod84.js +1 -0
- package/dist/vscode-workbench/assets/fs-core.worker-Bnmpp6fo.js +1 -0
- package/dist/vscode-workbench/assets/fs-query.worker-cZ-7Fjch.js +2 -0
- package/dist/vscode-workbench/assets/{iconv-lite-umd-C9tiCAJa.js → iconv-lite-umd-Dxx9Qyb4.js} +1 -1
- package/dist/vscode-workbench/assets/{index-DJ1HyMZ7.js → index-6VJMsaWj.js} +475 -475
- package/dist/vscode-workbench/assets/{jschardet-DE1jV513.js → jschardet-Ba3D16vt.js} +1 -1
- package/dist/vscode-workbench/index.html +21 -1
- package/package.json +23 -13
- package/dist/main/services/views/destroy-child-view.d.ts +0 -9
- package/dist/main/services/views/destroy-child-view.js +0 -23
- package/dist/preload/instrumentation/wxml-extract.d.ts +0 -11
- package/dist/preload/instrumentation/wxml-extract.js +0 -382
- package/dist/preload/shared/sid-registry.d.ts +0 -11
- package/dist/preload/shared/sid-registry.js +0 -38
- package/dist/renderer/assets/constants-EAMiwL9_.js +0 -2
- package/dist/renderer/assets/index-BMqyDx71.js +0 -49
- package/dist/renderer/assets/input-CDAEwpBO.js +0 -2
- package/dist/renderer/assets/ipc-transport-B7eFOR68.css +0 -1
- package/dist/renderer/assets/ipc-transport-_G1k5V1p.js +0 -9
- package/dist/renderer/assets/popover-DEdjK14m.js +0 -2
- package/dist/renderer/assets/select-DpKPCh3y.js +0 -2
- package/dist/renderer/assets/settings-M5q2elUr.js +0 -2
- package/dist/renderer/assets/settings-api-CDlZoHiO.js +0 -2
- package/dist/renderer/assets/workbenchSettings-CT_H1WhV.js +0 -8
- package/dist/shared/appdata-accumulator.d.ts +0 -95
- package/dist/shared/appdata-accumulator.js +0 -218
- package/dist/shared/request-core.d.ts +0 -84
- package/dist/shared/request-core.js +0 -168
- package/dist/shared/simulator-api-metadata.d.ts +0 -26
- package/dist/shared/simulator-api-metadata.js +0 -59
- package/dist/shared/vpath.d.ts +0 -42
- package/dist/shared/vpath.js +0 -105
- package/dist/simulator/assets/bridge-channels-zhsumfky.js +0 -2
- package/dist/simulator/assets/device-shell-C-wcFq3Z.js +0 -2
- package/dist/simulator/assets/device-shell-CS1rqAQz.css +0 -1
- package/dist/simulator/assets/jsx-runtime-BDTY6fEq.js +0 -2
- package/dist/simulator/assets/simulator-DU3-fS3v.js +0 -10
- package/dist/simulator/assets/simulator-mini-app-DzfMfnVM.js +0 -2
- package/dist/simulator/device-shell/menu-button-geometry.js +0 -8
- package/dist/vscode-workbench/assets/__vite-browser-external-CXi6Kz9W.js +0 -1
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isFrontendSettled } from
|
|
3
|
-
import { packDispatchBatch } from
|
|
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";
|
|
4
13
|
// ── requestId namespacing (pure, testable) ──────────────────────────────────
|
|
5
14
|
/**
|
|
6
15
|
* CDP events whose `params.requestId` we rewrite into the virtual namespace.
|
|
@@ -10,32 +19,32 @@ import { packDispatchBatch } from './dispatch-batch.js';
|
|
|
10
19
|
* included for that reason (rewrite-only today, not forwarded).
|
|
11
20
|
*/
|
|
12
21
|
export const REWRITE_REQUEST_ID_METHODS = new Set([
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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",
|
|
22
31
|
// 二期 (when WebSocket/EventSource forwarding lands): Network.webSocket*,
|
|
23
32
|
// Network.eventSourceMessageReceived — keep ids namespaced once added here.
|
|
24
33
|
]);
|
|
25
34
|
/** The Network.* methods this one-shot pass forwards to the front-end. */
|
|
26
35
|
export const FORWARDED_METHODS = new Set([
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
"Network.requestWillBeSent",
|
|
37
|
+
"Network.requestWillBeSentExtraInfo",
|
|
38
|
+
"Network.responseReceived",
|
|
39
|
+
"Network.responseReceivedExtraInfo",
|
|
40
|
+
"Network.loadingFinished",
|
|
41
|
+
"Network.loadingFailed",
|
|
33
42
|
// `dataReceived` deliberately omitted (二期) — see header.
|
|
34
43
|
]);
|
|
35
44
|
/** Methods that mark a request finished, so its id mapping can age out. */
|
|
36
45
|
const TERMINAL_METHODS = new Set([
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
"Network.loadingFinished",
|
|
47
|
+
"Network.loadingFailed",
|
|
39
48
|
]);
|
|
40
49
|
/**
|
|
41
50
|
* Bounded, TTL'd raw→virtual requestId map with an active/retired split.
|
|
@@ -86,7 +95,7 @@ export class RequestIdNamespace {
|
|
|
86
95
|
this.map.set(rawId, existing);
|
|
87
96
|
return existing.virtual;
|
|
88
97
|
}
|
|
89
|
-
const virtual =
|
|
98
|
+
const virtual = `${VIRTUAL_REQUEST_ID_PREFIX}${this.epoch}:${this.seq++}:${rawId}`;
|
|
90
99
|
this.map.set(rawId, { virtual, expires: t + this.ttlMs, active: true });
|
|
91
100
|
this.evict(t);
|
|
92
101
|
return virtual;
|
|
@@ -143,7 +152,7 @@ export function rewriteRequestId(method, params, ns) {
|
|
|
143
152
|
if (!REWRITE_REQUEST_ID_METHODS.has(method))
|
|
144
153
|
return { method, params };
|
|
145
154
|
const p = params;
|
|
146
|
-
if (!p || typeof p.requestId !==
|
|
155
|
+
if (!p || typeof p.requestId !== "string")
|
|
147
156
|
return { method, params };
|
|
148
157
|
const virtual = ns.resolve(p.requestId);
|
|
149
158
|
if (TERMINAL_METHODS.has(method))
|
|
@@ -151,8 +160,6 @@ export function rewriteRequestId(method, params, ns) {
|
|
|
151
160
|
return { method, params: { ...p, requestId: virtual } };
|
|
152
161
|
}
|
|
153
162
|
// ── DevTools front-end injection (the primary sink) ─────────────────────────
|
|
154
|
-
/** Probe the front-end realm exposes `DevToolsAPI.dispatchMessage`. */
|
|
155
|
-
const PROBE_DEVTOOLS_API = `(window.DevToolsAPI && typeof window.DevToolsAPI.dispatchMessage === 'function')`;
|
|
156
163
|
/**
|
|
157
164
|
* Build the `executeJavaScript` source that dispatches a BATCH of raw CDP
|
|
158
165
|
* messages into the DevTools front-end. Each message is carried as a JSON
|
|
@@ -162,44 +169,13 @@ const PROBE_DEVTOOLS_API = `(window.DevToolsAPI && typeof window.DevToolsAPI.dis
|
|
|
162
169
|
*/
|
|
163
170
|
function buildDispatchScript(messages) {
|
|
164
171
|
const arr = JSON.stringify(messages);
|
|
165
|
-
return `(()=>{try{`
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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}})()`);
|
|
171
178
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Build the `executeJavaScript` source for a chunked dispatch of one large CDP
|
|
174
|
-
* message — DevTools' own transport caps a single `dispatchMessage` payload, so
|
|
175
|
-
* the front-end exposes `dispatchMessageChunk(messageChunk, messageSize)` where
|
|
176
|
-
* the FIRST chunk carries the total size and EVERY SUBSEQUENT chunk is called
|
|
177
|
-
* with the chunk ONLY (second arg omitted). This matches Chromium's
|
|
178
|
-
* `devtools_ui_bindings.cc` DispatchProtocolMessage, whose front-end treats a
|
|
179
|
-
* call with a second argument as the start of a new message and a call without
|
|
180
|
-
* one as a continuation. Passing 0 (the previous behaviour) risked the
|
|
181
|
-
* front-end mis-reading a continuation as a fresh 0-size message. Returns false
|
|
182
|
-
* when the API isn't available.
|
|
183
|
-
*/
|
|
184
|
-
function buildChunkedDispatchScript(chunks, totalSize) {
|
|
185
|
-
const arr = JSON.stringify(chunks);
|
|
186
|
-
return `(()=>{try{`
|
|
187
|
-
+ `if(!(window.DevToolsAPI&&typeof window.DevToolsAPI.dispatchMessageChunk==='function'))return false;`
|
|
188
|
-
+ `const cs=JSON.parse(${JSON.stringify(arr)});`
|
|
189
|
-
+ `for(let i=0;i<cs.length;i++){`
|
|
190
|
-
+ `try{`
|
|
191
|
-
// First chunk: (chunk, totalSize). Subsequent chunks: (chunk) — second arg
|
|
192
|
-
// omitted, NOT 0, per Chromium's continuation contract.
|
|
193
|
-
+ `if(i===0){window.DevToolsAPI.dispatchMessageChunk(cs[i], ${totalSize})}`
|
|
194
|
-
+ `else{window.DevToolsAPI.dispatchMessageChunk(cs[i])}`
|
|
195
|
-
+ `}catch(_){}`
|
|
196
|
-
+ `}`
|
|
197
|
-
+ `return true;`
|
|
198
|
-
+ `}catch(_){return false}})()`;
|
|
199
|
-
}
|
|
200
|
-
/** A single dispatch payload may not exceed this many UTF-16 chars; chunk above. */
|
|
201
|
-
const MAX_SINGLE_DISPATCH_CHARS = 1_000_000;
|
|
202
|
-
const CHUNK_CHARS = 256 * 1024;
|
|
203
179
|
/**
|
|
204
180
|
* Upper bound on the COMBINED size (in UTF-16 chars) of the messages packed into
|
|
205
181
|
* one `executeJavaScript` batch. 2000 small messages otherwise stitch into a
|
|
@@ -216,13 +192,13 @@ const MAX_BATCH_CHARS = 512 * 1024;
|
|
|
216
192
|
*/
|
|
217
193
|
function buildForwardScript(record) {
|
|
218
194
|
const json = JSON.stringify(record);
|
|
219
|
-
return `(()=>{try{const r=JSON.parse(${JSON.stringify(json)});`
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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(_){}})()`);
|
|
226
202
|
}
|
|
227
203
|
/** Cap so a long session can't grow the fallback pending map unboundedly. */
|
|
228
204
|
const MAX_PENDING = 1000;
|
|
@@ -238,17 +214,274 @@ const DEVTOOLS_READY_TIMEOUT_MS = 5_000;
|
|
|
238
214
|
/** Poll interval while probing for the front-end API to become ready. */
|
|
239
215
|
const READY_RETRY_MS = 100;
|
|
240
216
|
export function createNetworkForwarder(bridge) {
|
|
241
|
-
const registry = new
|
|
242
|
-
|
|
243
|
-
//
|
|
217
|
+
const registry = new SyncDisposableRegistry();
|
|
218
|
+
let disposed = false;
|
|
219
|
+
// ── Response body / post-data prefetch (serves the front-end's clicks) ─────
|
|
220
|
+
// Keyed by VIRTUAL requestId and owned by the forwarder (not the attach):
|
|
221
|
+
// epochs make ids non-colliding, so entries stay servable across a simulator
|
|
222
|
+
// detach/re-attach while the panel still shows the old rows. Bounded + TTL'd
|
|
223
|
+
// in the cache itself.
|
|
224
|
+
const bodyCache = new PrefetchCache((v) => v.body.length);
|
|
225
|
+
const postDataCache = new PrefetchCache((v) => v.postData.length);
|
|
226
|
+
// ── Prefetch admission control ──────────────────────────────────────────────
|
|
227
|
+
// Every completed request (simulator + all render guests combined — this
|
|
228
|
+
// counter is forwarder-wide, not per-attach) would otherwise trigger an
|
|
229
|
+
// unconditional `Network.getResponseBody` CDP round-trip that must
|
|
230
|
+
// materialize the FULL body into main-process memory before the cache's own
|
|
231
|
+
// per-entry/total-size limits can reject it (those limits apply only to
|
|
232
|
+
// SETTLED entries — a pending fetch counts 0 and is eviction-exempt). A page
|
|
233
|
+
// that finishes many large resources at once (e.g. many concurrent images —
|
|
234
|
+
// exactly the render-guest capture path) could otherwise pile up unboundedly
|
|
235
|
+
// many simultaneous full-body reads. This caps how many prefetches (body +
|
|
236
|
+
// post-data combined) may be in flight at once; once at the cap, further
|
|
237
|
+
// completions are simply skipped — the panel's Response tab 404s for those
|
|
238
|
+
// (same as any other not-found id) rather than the process risking a memory
|
|
239
|
+
// spike. `primeWithAdmission`'s bookkeeping only counts a slot when the cache
|
|
240
|
+
// actually started a new fetch (PrefetchCache.prime()'s idempotent no-op
|
|
241
|
+
// return doesn't consume one).
|
|
242
|
+
const MAX_CONCURRENT_PREFETCHES = 32;
|
|
243
|
+
let pendingPrefetchCount = 0;
|
|
244
|
+
/**
|
|
245
|
+
* `fetch` MUST be an `async` function (both current call sites in
|
|
246
|
+
* `prefetchBodies` are). An `async` function can never throw synchronously —
|
|
247
|
+
* any exception in its body is spec-guaranteed to surface as a rejected
|
|
248
|
+
* promise instead — which is what guarantees `release()` always eventually
|
|
249
|
+
* runs via the `.then()` below. A plain (non-async) function that throws
|
|
250
|
+
* synchronously when CALLED would bypass `.then()` entirely and leak this
|
|
251
|
+
* slot forever (`PrefetchCache.prime` catches that synchronous throw and
|
|
252
|
+
* still reports `started: true`), so never pass one here.
|
|
253
|
+
*/
|
|
254
|
+
function primeWithAdmission(cache, id, fetch) {
|
|
255
|
+
if (pendingPrefetchCount >= MAX_CONCURRENT_PREFETCHES)
|
|
256
|
+
return;
|
|
257
|
+
pendingPrefetchCount++;
|
|
258
|
+
let released = false;
|
|
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
|
+
}));
|
|
272
|
+
if (!started)
|
|
273
|
+
release();
|
|
274
|
+
}
|
|
275
|
+
// The simulator WCV we currently have a debugger session on, our broker
|
|
276
|
+
// lease for it, and the per-attach teardown (message listener). Null when
|
|
277
|
+
// not attached.
|
|
244
278
|
let simWc = null;
|
|
279
|
+
let simLease = null;
|
|
245
280
|
let attachDisposables = null;
|
|
281
|
+
// Render-host guests wired for capture: wc.id → SYNCHRONOUS per-guest
|
|
282
|
+
// teardown (message listener + broker lease). Attach/detach ownership for
|
|
283
|
+
// these sessions lives entirely in the shared broker now (see cdp-session/
|
|
284
|
+
// index.ts) — this map only tracks OUR OWN wiring (the 'message' listener
|
|
285
|
+
// wireNetworkCapture installs), not who may detach the underlying session.
|
|
286
|
+
const guestWired = new Map();
|
|
287
|
+
// wc.id → the generation token of the most recently scheduled render-guest
|
|
288
|
+
// retry (acquire refused, or an established session got detached).
|
|
289
|
+
// `guestWired` alone cannot de-duplicate repeat `attachRenderGuest` calls
|
|
290
|
+
// during this window — it is only populated on a SUCCESSFUL acquire — so
|
|
291
|
+
// without this, calling `attachRenderGuest(wc)` again while the exclusive
|
|
292
|
+
// holder keeps refusing spawns a second, independent 300ms retry chain.
|
|
293
|
+
// A bare presence flag is NOT enough: a stale timer from an EARLIER retry
|
|
294
|
+
// cycle (superseded by an intervening successful reattach, itself followed
|
|
295
|
+
// by a fresh detach that scheduled its own new retry) would, on firing,
|
|
296
|
+
// unconditionally clear whatever is recorded — including that newer retry's
|
|
297
|
+
// own marker — and then reschedule itself, forking back into two parallel
|
|
298
|
+
// chains. A monotonic generation token lets a fired timer recognize its own
|
|
299
|
+
// staleness (its captured token no longer matches the current one) and
|
|
300
|
+
// no-op instead of touching state it no longer owns.
|
|
301
|
+
const guestRetryGeneration = new Map();
|
|
302
|
+
let nextRenderGuestRetryGeneration = 0;
|
|
303
|
+
// Own (and dispose on this forwarder's own dispose()) a private broker only
|
|
304
|
+
// when the caller didn't supply a shared one.
|
|
305
|
+
const ownsBroker = !bridge.broker;
|
|
306
|
+
const broker = bridge.broker ??
|
|
307
|
+
createCdpSessionBroker({ connections: bridge.connections });
|
|
246
308
|
// The DevTools front-end host wc (primary sink), set by the ViewManager.
|
|
247
309
|
let devtoolsWc = null;
|
|
248
310
|
// Teardown for the wc 'destroyed' watcher on the current host (clears the host).
|
|
249
311
|
let devtoolsHostDisposable = null;
|
|
312
|
+
// ── Global mirror sink ──────────────────────────────────────────────────────
|
|
313
|
+
// The standalone internal (app-wide) DevTools window's front-end host wc.
|
|
314
|
+
// Deliberately NO probing/ready/degraded state machine and NO console
|
|
315
|
+
// fallback — see setGlobalDevtoolsHost's doc comment.
|
|
316
|
+
let globalDevtoolsWc = null;
|
|
317
|
+
let globalDevtoolsHostDisposable = null;
|
|
318
|
+
// Dedicated network-only outbound gate (body/post-data lookups) for the
|
|
319
|
+
// CURRENT global host — never elements-forward's gate, see
|
|
320
|
+
// global-body-gate.ts's header for why. Stopped whenever the host changes
|
|
321
|
+
// or clears.
|
|
322
|
+
let globalBodyGateStop = null;
|
|
323
|
+
// Bounded in-order hold for global-mirror messages arriving while the
|
|
324
|
+
// global host's front-end has not settled yet (its boot window) — without
|
|
325
|
+
// it those events are silently unrecoverable, breaking the window's
|
|
326
|
+
// full-stream promise. Scoped strictly to the CURRENT host: cleared when
|
|
327
|
+
// the host changes/clears, never replayed into a different host.
|
|
328
|
+
const MAX_GLOBAL_PENDING_QUEUE = 2000;
|
|
329
|
+
let globalPendingQueue = [];
|
|
330
|
+
let globalPendingOverflowWarned = false;
|
|
331
|
+
let globalFlushTimer = null;
|
|
332
|
+
// Wall-clock bound on the settle-poll (not on the queue itself): a
|
|
333
|
+
// front-end that never settles stops being polled after this, and the
|
|
334
|
+
// still-held queue flushes via the next incoming event instead.
|
|
335
|
+
const GLOBAL_FLUSH_POLL_MAX_MS = 60_000;
|
|
336
|
+
function clearGlobalPending() {
|
|
337
|
+
globalPendingQueue = [];
|
|
338
|
+
globalPendingOverflowWarned = false;
|
|
339
|
+
if (globalFlushTimer) {
|
|
340
|
+
clearTimeout(globalFlushTimer);
|
|
341
|
+
globalFlushTimer = null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function scheduleGlobalFlush(startedAt = Date.now()) {
|
|
345
|
+
if (globalFlushTimer)
|
|
346
|
+
return;
|
|
347
|
+
if (Date.now() - startedAt >= GLOBAL_FLUSH_POLL_MAX_MS)
|
|
348
|
+
return;
|
|
349
|
+
// unref'd like scheduleRenderGuestRetry's timer: a settle-poll must
|
|
350
|
+
// never be what keeps the process (or a test file) alive.
|
|
351
|
+
globalFlushTimer = setTimeout(() => {
|
|
352
|
+
globalFlushTimer = null;
|
|
353
|
+
if (globalPendingQueue.length === 0)
|
|
354
|
+
return;
|
|
355
|
+
if (!globalDevtoolsWc || globalDevtoolsWc.isDestroyed()) {
|
|
356
|
+
clearGlobalPending();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (!isFrontendSettled(globalDevtoolsWc)) {
|
|
360
|
+
scheduleGlobalFlush(startedAt);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
flushGlobalPending();
|
|
364
|
+
}, READY_RETRY_MS);
|
|
365
|
+
globalFlushTimer.unref?.();
|
|
366
|
+
}
|
|
367
|
+
/** Drain the pending queue into the (settled) global host, oldest first,
|
|
368
|
+
* packed greedily under the same per-script size bound as the native sink —
|
|
369
|
+
* and through the SAME chunked transport for messages past
|
|
370
|
+
* MAX_SINGLE_DISPATCH_CHARS: a single oversized message stitched into one
|
|
371
|
+
* giant executeJavaScript overflows the IPC/script limit and the whole
|
|
372
|
+
* call rejects into a silent drop. */
|
|
373
|
+
function flushGlobalPending() {
|
|
374
|
+
if (globalPendingQueue.length === 0)
|
|
375
|
+
return;
|
|
376
|
+
const target = globalDevtoolsWc;
|
|
377
|
+
if (!target || target.isDestroyed()) {
|
|
378
|
+
clearGlobalPending();
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
while (globalPendingQueue.length > 0) {
|
|
382
|
+
const { batch, chunked, remaining } = packDispatchBatch(globalPendingQueue, MAX_SINGLE_DISPATCH_CHARS, MAX_BATCH_CHARS);
|
|
383
|
+
globalPendingQueue = remaining;
|
|
384
|
+
for (const msg of chunked)
|
|
385
|
+
dispatchChunked(target, msg);
|
|
386
|
+
if (batch.length > 0) {
|
|
387
|
+
target.executeJavaScript(buildDispatchScript(batch), true).catch(() => {
|
|
388
|
+
/* best-effort */
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
globalPendingOverflowWarned = false;
|
|
393
|
+
}
|
|
394
|
+
function enqueueGlobalPending(json) {
|
|
395
|
+
if (globalPendingQueue.length >= MAX_GLOBAL_PENDING_QUEUE) {
|
|
396
|
+
globalPendingQueue.shift();
|
|
397
|
+
// Not silent: dropping past the cap means the window's full-stream
|
|
398
|
+
// promise is degraded — say so once per overflow episode, not per event.
|
|
399
|
+
if (!globalPendingOverflowWarned) {
|
|
400
|
+
globalPendingOverflowWarned = true;
|
|
401
|
+
console.warn(`[network-forward] global mirror queue overflow (cap ${MAX_GLOBAL_PENDING_QUEUE}): dropping oldest events while the debug window front-end is still loading`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
globalPendingQueue.push(json);
|
|
405
|
+
scheduleGlobalFlush();
|
|
406
|
+
}
|
|
407
|
+
function applyGlobalDevtoolsHost(host) {
|
|
408
|
+
globalDevtoolsHostDisposable?.dispose();
|
|
409
|
+
globalDevtoolsHostDisposable = null;
|
|
410
|
+
globalBodyGateStop?.();
|
|
411
|
+
globalBodyGateStop = null;
|
|
412
|
+
// Queued events belong to the PREVIOUS host's boot window — never carry
|
|
413
|
+
// them across a host change (or into "no host").
|
|
414
|
+
clearGlobalPending();
|
|
415
|
+
globalDevtoolsWc = host && !host.isDestroyed() ? host : null;
|
|
416
|
+
if (!globalDevtoolsWc)
|
|
417
|
+
return;
|
|
418
|
+
const target = globalDevtoolsWc;
|
|
419
|
+
const onHostDestroyed = () => {
|
|
420
|
+
if (globalDevtoolsWc === target)
|
|
421
|
+
globalDevtoolsWc = null;
|
|
422
|
+
globalBodyGateStop?.();
|
|
423
|
+
globalBodyGateStop = null;
|
|
424
|
+
clearGlobalPending();
|
|
425
|
+
};
|
|
426
|
+
const reg = bridge.connections;
|
|
427
|
+
if (reg && typeof target.once === "function") {
|
|
428
|
+
const owned = reg.acquire(target).own(onHostDestroyed);
|
|
429
|
+
globalDevtoolsHostDisposable = toDisposable(() => owned.dispose());
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
if (typeof target.once === "function")
|
|
433
|
+
target.once("destroyed", onHostDestroyed);
|
|
434
|
+
globalDevtoolsHostDisposable = toDisposable(() => {
|
|
435
|
+
try {
|
|
436
|
+
target.removeListener?.("destroyed", onHostDestroyed);
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
/* gone */
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
globalBodyGateStop = installGlobalNetworkBodyGate(target, {
|
|
444
|
+
getResponseBody: (requestId) => bodyCache.lookup(requestId),
|
|
445
|
+
getRequestPostData: (requestId) => postDataCache.lookup(requestId),
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
/** Best-effort mirror of one raw CDP message into the global host. */
|
|
449
|
+
function dispatchToGlobal(method, params) {
|
|
450
|
+
if (!globalDevtoolsWc || globalDevtoolsWc.isDestroyed())
|
|
451
|
+
return;
|
|
452
|
+
let json;
|
|
453
|
+
try {
|
|
454
|
+
json = JSON.stringify({ method, params });
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
// Same settled gate every other injection point in this file uses: an
|
|
460
|
+
// unsettled front-end wipes its state on load anyway, and executeJavaScript
|
|
461
|
+
// against it queues one did-stop-loading waiter per call — piling toward
|
|
462
|
+
// the MaxListeners ceiling during the global host's boot window. Unlike
|
|
463
|
+
// those injection points, the events here are NOT re-derivable later, so
|
|
464
|
+
// they queue for a post-settle flush instead of dropping.
|
|
465
|
+
if (!isFrontendSettled(globalDevtoolsWc)) {
|
|
466
|
+
enqueueGlobalPending(json);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Anything still queued flushes first so delivery order matches arrival.
|
|
470
|
+
flushGlobalPending();
|
|
471
|
+
// Oversized single messages take the chunked transport, mirroring the
|
|
472
|
+
// native sink's flushDispatch — see flushGlobalPending's doc.
|
|
473
|
+
if (json.length > MAX_SINGLE_DISPATCH_CHARS) {
|
|
474
|
+
dispatchChunked(globalDevtoolsWc, json);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
globalDevtoolsWc
|
|
478
|
+
.executeJavaScript(buildDispatchScript([json]), true)
|
|
479
|
+
.catch(() => {
|
|
480
|
+
/* best-effort */
|
|
481
|
+
});
|
|
482
|
+
}
|
|
250
483
|
// ── Native-sink state machine ─────────────────────────────────────────────
|
|
251
|
-
let sink =
|
|
484
|
+
let sink = "idle";
|
|
252
485
|
// Buffered completed-request records while 'probing' — flushed to console if we
|
|
253
486
|
// degrade, dropped if we go ready (so a request shows in exactly one sink).
|
|
254
487
|
let probeConsoleBuffer = [];
|
|
@@ -276,22 +509,22 @@ export function createNetworkForwarder(bridge) {
|
|
|
276
509
|
}
|
|
277
510
|
/** Enter the 'probing' state and arm the ready-timeout (idempotent). */
|
|
278
511
|
function beginProbing() {
|
|
279
|
-
if (sink ===
|
|
512
|
+
if (sink === "probing")
|
|
280
513
|
return;
|
|
281
|
-
sink =
|
|
514
|
+
sink = "probing";
|
|
282
515
|
probeDeadline = Date.now() + DEVTOOLS_READY_TIMEOUT_MS;
|
|
283
516
|
if (readyTimeoutTimer)
|
|
284
517
|
clearTimeout(readyTimeoutTimer);
|
|
285
518
|
readyTimeoutTimer = setTimeout(() => {
|
|
286
519
|
readyTimeoutTimer = null;
|
|
287
520
|
// Still not ready after the grace period → abandon native for this host.
|
|
288
|
-
if (sink ===
|
|
521
|
+
if (sink === "probing")
|
|
289
522
|
degradeToConsole();
|
|
290
523
|
}, DEVTOOLS_READY_TIMEOUT_MS);
|
|
291
524
|
}
|
|
292
525
|
/** Native path confirmed live: console buffer is moot, drop it. */
|
|
293
526
|
function markReady() {
|
|
294
|
-
sink =
|
|
527
|
+
sink = "ready";
|
|
295
528
|
probeDeadline = null;
|
|
296
529
|
if (readyTimeoutTimer) {
|
|
297
530
|
clearTimeout(readyTimeoutTimer);
|
|
@@ -310,7 +543,7 @@ export function createNetworkForwarder(bridge) {
|
|
|
310
543
|
* and flush the buffered completed-request records to the console sink.
|
|
311
544
|
*/
|
|
312
545
|
function degradeToConsole() {
|
|
313
|
-
sink =
|
|
546
|
+
sink = "degraded";
|
|
314
547
|
probeDeadline = null;
|
|
315
548
|
dispatchQueue = [];
|
|
316
549
|
if (readyTimeoutTimer) {
|
|
@@ -333,14 +566,17 @@ export function createNetworkForwarder(bridge) {
|
|
|
333
566
|
queueMicrotask(flushDispatch);
|
|
334
567
|
}
|
|
335
568
|
/** Trim the native queue to its cap, preferring to keep request-opening events.
|
|
336
|
-
* Active requests' first events (requestWillBeSent / ...ExtraInfo
|
|
337
|
-
* so later responseReceived/loadingFinished
|
|
338
|
-
*
|
|
569
|
+
* Active requests' first events (requestWillBeSent / ...ExtraInfo /
|
|
570
|
+
* webSocketCreated) are retained so later responseReceived/loadingFinished/
|
|
571
|
+
* webSocket* events never become orphans in the panel (the front-end
|
|
572
|
+
* silently drops events for an unknown requestId); we drop the oldest
|
|
573
|
+
* NON-opening (low-value / completion) events first. */
|
|
339
574
|
function trimQueue() {
|
|
340
575
|
if (dispatchQueue.length <= MAX_DISPATCH_QUEUE)
|
|
341
576
|
return;
|
|
342
|
-
const isOpener = (json) => json.includes('"Network.requestWillBeSent"')
|
|
343
|
-
|
|
577
|
+
const isOpener = (json) => json.includes('"Network.requestWillBeSent"') ||
|
|
578
|
+
json.includes('"Network.requestWillBeSentExtraInfo"') ||
|
|
579
|
+
json.includes('"Network.webSocketCreated"');
|
|
344
580
|
// First pass: drop oldest non-opener events.
|
|
345
581
|
const kept = [];
|
|
346
582
|
let over = dispatchQueue.length - MAX_DISPATCH_QUEUE;
|
|
@@ -363,7 +599,7 @@ export function createNetworkForwarder(bridge) {
|
|
|
363
599
|
flushScheduled = false;
|
|
364
600
|
if (dispatchQueue.length === 0)
|
|
365
601
|
return;
|
|
366
|
-
if (sink ===
|
|
602
|
+
if (sink === "degraded") {
|
|
367
603
|
dispatchQueue = [];
|
|
368
604
|
return;
|
|
369
605
|
}
|
|
@@ -386,7 +622,7 @@ export function createNetworkForwarder(bridge) {
|
|
|
386
622
|
scheduleReadyRetry();
|
|
387
623
|
return;
|
|
388
624
|
}
|
|
389
|
-
if (sink ===
|
|
625
|
+
if (sink === "idle")
|
|
390
626
|
beginProbing();
|
|
391
627
|
// Pack greedily up to MAX_BATCH_CHARS so one executeJavaScript stays sized;
|
|
392
628
|
// oversized single messages go via the chunked transport (see packDispatchBatch).
|
|
@@ -415,24 +651,26 @@ export function createNetworkForwarder(bridge) {
|
|
|
415
651
|
dispatchQueue = remaining;
|
|
416
652
|
if (remaining.length > 0)
|
|
417
653
|
scheduleFlush();
|
|
418
|
-
wc.executeJavaScript(script, true)
|
|
654
|
+
wc.executeJavaScript(script, true)
|
|
655
|
+
.then((ok) => {
|
|
419
656
|
if (ok === true) {
|
|
420
|
-
if (sink !==
|
|
657
|
+
if (sink !== "ready")
|
|
421
658
|
markReady();
|
|
422
659
|
return;
|
|
423
660
|
}
|
|
424
661
|
// API not present yet — front-end still booting. Re-queue this batch ahead
|
|
425
662
|
// of any newer events and poll until ready (or the timeout degrades us).
|
|
426
|
-
if (sink ===
|
|
663
|
+
if (sink === "degraded")
|
|
427
664
|
return;
|
|
428
665
|
dispatchQueue = batch.concat(dispatchQueue);
|
|
429
666
|
trimQueue();
|
|
430
667
|
scheduleReadyRetry();
|
|
431
|
-
})
|
|
668
|
+
})
|
|
669
|
+
.catch(() => {
|
|
432
670
|
// wc navigated / torn down mid-call, OR the script overflowed IPC. Re-queue
|
|
433
671
|
// (bounded) and let the next flush re-resolve the host. Best-effort; the
|
|
434
672
|
// ready-timeout still governs giving up. Backoff is via the retry timer.
|
|
435
|
-
if (sink ===
|
|
673
|
+
if (sink === "degraded")
|
|
436
674
|
return;
|
|
437
675
|
dispatchQueue = batch.concat(dispatchQueue);
|
|
438
676
|
trimQueue();
|
|
@@ -452,10 +690,12 @@ export function createNetworkForwarder(bridge) {
|
|
|
452
690
|
catch {
|
|
453
691
|
return;
|
|
454
692
|
}
|
|
455
|
-
wc.executeJavaScript(script, true).catch(() => {
|
|
693
|
+
wc.executeJavaScript(script, true).catch(() => {
|
|
694
|
+
/* best-effort */
|
|
695
|
+
});
|
|
456
696
|
}
|
|
457
697
|
function scheduleReadyRetry() {
|
|
458
|
-
if (readyRetryTimer || sink ===
|
|
698
|
+
if (readyRetryTimer || sink === "ready" || sink === "degraded")
|
|
459
699
|
return;
|
|
460
700
|
// Self-terminate on the same deadline readyTimeoutTimer enforces, instead of
|
|
461
701
|
// trusting that timer to win a same-instant race against this one (see the
|
|
@@ -466,7 +706,7 @@ export function createNetworkForwarder(bridge) {
|
|
|
466
706
|
}
|
|
467
707
|
readyRetryTimer = setTimeout(() => {
|
|
468
708
|
readyRetryTimer = null;
|
|
469
|
-
if (sink ===
|
|
709
|
+
if (sink === "degraded")
|
|
470
710
|
return;
|
|
471
711
|
if (probeDeadline !== null && Date.now() >= probeDeadline) {
|
|
472
712
|
degradeToConsole();
|
|
@@ -483,9 +723,9 @@ export function createNetworkForwarder(bridge) {
|
|
|
483
723
|
// requests — queueing them would let them double-render if a host later
|
|
484
724
|
// arrives / swaps in. resolveDevtoolsWc() guards the idle case where a host
|
|
485
725
|
// is set on the bridge but applyDevtoolsHost hasn't run yet.
|
|
486
|
-
if (sink ===
|
|
726
|
+
if (sink === "degraded")
|
|
487
727
|
return;
|
|
488
|
-
if (sink ===
|
|
728
|
+
if (sink === "idle" && !resolveDevtoolsWc())
|
|
489
729
|
return;
|
|
490
730
|
let json;
|
|
491
731
|
try {
|
|
@@ -514,20 +754,39 @@ export function createNetworkForwarder(bridge) {
|
|
|
514
754
|
}
|
|
515
755
|
wc.executeJavaScript(script, true).catch(() => { });
|
|
516
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* Stop USING the simulator wc's session. Releases our own lease (message
|
|
759
|
+
* listener + Network capture wiring) but never forces a physical
|
|
760
|
+
* `debugger.detach()` — this same simulator wc's debugger session is also
|
|
761
|
+
* independently used by simulator-storage (DOMStorage capture), and an
|
|
762
|
+
* unconditional detach here would kill ITS capture too, exactly like an
|
|
763
|
+
* unconditional detach on ITS side would kill ours. The broker (see
|
|
764
|
+
* cdp-session/index.ts) is the only thing that decides when an actual
|
|
765
|
+
* detach happens (wc destroy, or its own top-level dispose() for a
|
|
766
|
+
* self-attached session) — never a single consumer switching away.
|
|
767
|
+
*/
|
|
517
768
|
function detachSimulator() {
|
|
518
|
-
const wc = simWc;
|
|
519
769
|
simWc = null;
|
|
770
|
+
const lease = simLease;
|
|
771
|
+
simLease = null;
|
|
520
772
|
const ad = attachDisposables;
|
|
521
773
|
attachDisposables = null;
|
|
522
|
-
if (ad)
|
|
523
|
-
|
|
774
|
+
if (ad) {
|
|
775
|
+
try {
|
|
776
|
+
ad.disposeAll();
|
|
777
|
+
}
|
|
778
|
+
catch {
|
|
779
|
+
/* best-effort teardown */
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
lease?.dispose();
|
|
524
783
|
// Drop any queued-but-unflushed messages so a new attach starts clean, and
|
|
525
784
|
// reset the native-sink state machine (the next attach re-probes the host).
|
|
526
785
|
dispatchQueue = [];
|
|
527
786
|
probeConsoleBuffer = [];
|
|
528
787
|
// A new simulator attach restarts the native sink: 'idle' until the next
|
|
529
788
|
// event re-probes the (possibly already-set) host.
|
|
530
|
-
sink =
|
|
789
|
+
sink = "idle";
|
|
531
790
|
probeDeadline = null;
|
|
532
791
|
if (readyRetryTimer) {
|
|
533
792
|
clearTimeout(readyRetryTimer);
|
|
@@ -537,141 +796,403 @@ export function createNetworkForwarder(bridge) {
|
|
|
537
796
|
clearTimeout(readyTimeoutTimer);
|
|
538
797
|
readyTimeoutTimer = null;
|
|
539
798
|
}
|
|
540
|
-
if (!wc || wc.isDestroyed())
|
|
541
|
-
return;
|
|
542
|
-
try {
|
|
543
|
-
if (wc.debugger.isAttached())
|
|
544
|
-
wc.debugger.detach();
|
|
545
|
-
}
|
|
546
|
-
catch { /* already detached / mid-destroy */ }
|
|
547
799
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
console.warn('[network-forward] debugger.attach failed; simulator network not captured:', err instanceof Error ? err.message : err);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
simWc = wc;
|
|
564
|
-
const attach = new DisposableRegistry();
|
|
565
|
-
attachDisposables = attach;
|
|
566
|
-
// Per-attach requestId namespace: a fresh epoch each attach guarantees ids
|
|
567
|
-
// from a previous simulator session can never collide with this one.
|
|
568
|
-
const ns = new RequestIdNamespace(String(Date.now()));
|
|
800
|
+
/**
|
|
801
|
+
* Wire one wc's already-usable debugger session for Network capture: a fresh
|
|
802
|
+
* per-attach requestId namespace (epochs keep ids from ever colliding across
|
|
803
|
+
* sources/attaches), the raw-event forward into the DevTools front-end, the
|
|
804
|
+
* loadingFinished-time body/post-data prefetch, and the console-fallback
|
|
805
|
+
* bookkeeping (tagged with `source`). Shared verbatim by the simulator
|
|
806
|
+
* attach (owner semantics) and every render-guest attach (shared-session
|
|
807
|
+
* semantics) — the capture pipeline is identical, only session ownership
|
|
808
|
+
* differs. The 'message' listener teardown is registered on `attach`.
|
|
809
|
+
*/
|
|
810
|
+
function wireNetworkCapture(wc, source, attach, epoch) {
|
|
811
|
+
const ns = new RequestIdNamespace(epoch);
|
|
569
812
|
// Fallback bookkeeping: requestId → in-flight request, for the console line
|
|
570
813
|
// when the native dispatch path is unusable.
|
|
571
814
|
const pending = new Map();
|
|
815
|
+
// User-facing classification, decided ONCE at
|
|
816
|
+
// requestWillBeSent (the only event carrying a url) and consulted by every
|
|
817
|
+
// later event on the same rawId — later events (responseReceived/
|
|
818
|
+
// loadingFinished/loadingFailed) carry no url, so they cannot re-derive
|
|
819
|
+
// the verdict; they must remember it.
|
|
820
|
+
const userFacingByRawId = new Map();
|
|
821
|
+
// Raw ids whose requestWillBeSent announced a post body that was NOT
|
|
822
|
+
// inlined (`hasPostData` without `postData`) — the only case the front-end
|
|
823
|
+
// round-trips `Network.getRequestPostData`. Consumed at loadingFinished.
|
|
824
|
+
const postDataWanted = new Set();
|
|
825
|
+
/**
|
|
826
|
+
* Prefetch the response body (and, when flagged, the post data) from this
|
|
827
|
+
* wc's CDP session the moment the request finishes — the renderer may
|
|
828
|
+
* evict the resource soon after, so a click-time fetch would race it.
|
|
829
|
+
* Gated exactly like event forwarding: with no devtools host to serve
|
|
830
|
+
* (idle-without-host / degraded) there is no panel to click, so buffering
|
|
831
|
+
* bodies would only burn memory.
|
|
832
|
+
*/
|
|
833
|
+
const prefetchBodies = (rawId, encodedDataLength) => {
|
|
834
|
+
// Skip only when NEITHER consumer can use the body: the user-facing
|
|
835
|
+
// sink is unusable (degraded, or idle with no host) AND the global
|
|
836
|
+
// mirror has no host either. Either alone is enough reason to prefetch
|
|
837
|
+
// — global-body-gate answers Network.getResponseBody/getRequestPostData
|
|
838
|
+
// from this same cache, and it must not 404 just because the
|
|
839
|
+
// user-facing sink happens to be closed/degraded.
|
|
840
|
+
const userSinkUsable = sink !== "degraded" && !(sink === "idle" && !resolveDevtoolsWc());
|
|
841
|
+
const globalUsable = globalDevtoolsWc !== null && !globalDevtoolsWc.isDestroyed();
|
|
842
|
+
if (!userSinkUsable && !globalUsable)
|
|
843
|
+
return;
|
|
844
|
+
const virtualId = ns.resolve(rawId);
|
|
845
|
+
// Skip the CDP round-trip entirely for a response already known (from
|
|
846
|
+
// the wire size CDP reports at completion) to exceed the cache's own
|
|
847
|
+
// per-entry ceiling — no point materializing a full body into main-
|
|
848
|
+
// process memory just to have the cache reject it after the fact. This
|
|
849
|
+
// is a best-effort heuristic (encodedDataLength is the ON-THE-WIRE size;
|
|
850
|
+
// a decompressed body can be larger), not a hard guarantee — the
|
|
851
|
+
// concurrency cap below is what actually bounds worst-case memory.
|
|
852
|
+
const knownOversized = typeof encodedDataLength === "number" &&
|
|
853
|
+
encodedDataLength > DEFAULT_PER_ENTRY_MAX_CHARS;
|
|
854
|
+
if (!knownOversized) {
|
|
855
|
+
primeWithAdmission(bodyCache, virtualId, async () => {
|
|
856
|
+
const raw = await wc.debugger.sendCommand("Network.getResponseBody", { requestId: rawId });
|
|
857
|
+
const r = raw;
|
|
858
|
+
if (!r || typeof r.body !== "string")
|
|
859
|
+
throw new Error("response body unavailable");
|
|
860
|
+
return { body: r.body, base64Encoded: r.base64Encoded === true };
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
if (!postDataWanted.delete(rawId))
|
|
864
|
+
return;
|
|
865
|
+
primeWithAdmission(postDataCache, virtualId, async () => {
|
|
866
|
+
const raw = await wc.debugger.sendCommand("Network.getRequestPostData", { requestId: rawId });
|
|
867
|
+
const r = raw;
|
|
868
|
+
if (!r || typeof r.postData !== "string")
|
|
869
|
+
throw new Error("post data unavailable");
|
|
870
|
+
return { postData: r.postData };
|
|
871
|
+
});
|
|
872
|
+
};
|
|
873
|
+
// ── Fallback bookkeeping handlers — one per Network.* method, each kept
|
|
874
|
+
// simple so `onMessage` itself stays a flat dispatch (not a branchy switch). ──
|
|
875
|
+
function onRequestWillBeSent(params) {
|
|
876
|
+
const p = params;
|
|
877
|
+
if (!p?.request)
|
|
878
|
+
return;
|
|
879
|
+
if (pending.size >= MAX_PENDING)
|
|
880
|
+
pending.clear();
|
|
881
|
+
pending.set(p.requestId, {
|
|
882
|
+
url: p.request.url,
|
|
883
|
+
method: p.request.method,
|
|
884
|
+
status: 0,
|
|
885
|
+
});
|
|
886
|
+
// A body announced but not inlined is the one case the panel will
|
|
887
|
+
// round-trip `Network.getRequestPostData` — flag it for prefetch.
|
|
888
|
+
if (p.request.hasPostData === true &&
|
|
889
|
+
typeof p.request.postData !== "string") {
|
|
890
|
+
if (postDataWanted.size >= MAX_PENDING)
|
|
891
|
+
postDataWanted.clear();
|
|
892
|
+
postDataWanted.add(p.requestId);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
function onResponseReceived(params) {
|
|
896
|
+
const p = params;
|
|
897
|
+
const req = pending.get(p.requestId);
|
|
898
|
+
if (req)
|
|
899
|
+
req.status = p.response?.status ?? 0;
|
|
900
|
+
}
|
|
901
|
+
/** Shared "request terminated" bookkeeping for both loadingFinished and
|
|
902
|
+
* loadingFailed: drop the classification + pending record, returning the
|
|
903
|
+
* pending entry (or undefined if it was already gone/never seen). */
|
|
904
|
+
function retirePending(rawId) {
|
|
905
|
+
userFacingByRawId.delete(rawId);
|
|
906
|
+
const req = pending.get(rawId);
|
|
907
|
+
if (!req)
|
|
908
|
+
return undefined;
|
|
909
|
+
pending.delete(rawId);
|
|
910
|
+
return req;
|
|
911
|
+
}
|
|
912
|
+
function onLoadingFinished(params) {
|
|
913
|
+
const p = params;
|
|
914
|
+
if (typeof p?.requestId === "string")
|
|
915
|
+
prefetchBodies(p.requestId, p.encodedDataLength);
|
|
916
|
+
const req = retirePending(p?.requestId);
|
|
917
|
+
if (!req)
|
|
918
|
+
return;
|
|
919
|
+
maybeFallback({
|
|
920
|
+
source,
|
|
921
|
+
url: req.url,
|
|
922
|
+
method: req.method,
|
|
923
|
+
status: req.status,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
function onLoadingFailed(params) {
|
|
927
|
+
const p = params;
|
|
928
|
+
postDataWanted.delete(p?.requestId);
|
|
929
|
+
const req = retirePending(p?.requestId);
|
|
930
|
+
if (!req)
|
|
931
|
+
return;
|
|
932
|
+
maybeFallback({
|
|
933
|
+
source,
|
|
934
|
+
url: req.url,
|
|
935
|
+
method: req.method,
|
|
936
|
+
status: req.status,
|
|
937
|
+
errorText: p.canceled ? "canceled" : p.errorText || "failed",
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
const FALLBACK_HANDLERS = {
|
|
941
|
+
"Network.requestWillBeSent": onRequestWillBeSent,
|
|
942
|
+
"Network.responseReceived": onResponseReceived,
|
|
943
|
+
"Network.loadingFinished": onLoadingFinished,
|
|
944
|
+
"Network.loadingFailed": onLoadingFailed,
|
|
945
|
+
};
|
|
946
|
+
/**
|
|
947
|
+
* Resolve (and, on `requestWillBeSent`, record) whether `rawId` is
|
|
948
|
+
* user-facing. MUST be computed eagerly here — not deferred to
|
|
949
|
+
* `onRequestWillBeSent` below, which runs AFTER this in `onMessage` — a
|
|
950
|
+
* `requestWillBeSent` is itself a FORWARDED_METHODS event whose own
|
|
951
|
+
* routing decision depends on the very verdict it establishes. Every
|
|
952
|
+
* later event for the same rawId (no url of its own) just looks up the
|
|
953
|
+
* recorded verdict; an unknown rawId fails open (user-facing) rather
|
|
954
|
+
* than silently hiding it.
|
|
955
|
+
*/
|
|
956
|
+
function resolveUserFacing(method, params, rawId) {
|
|
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
|
+
])
|
|
965
|
+
: true;
|
|
966
|
+
if (rawId) {
|
|
967
|
+
if (userFacingByRawId.size >= MAX_PENDING)
|
|
968
|
+
userFacingByRawId.clear();
|
|
969
|
+
userFacingByRawId.set(rawId, verdict);
|
|
970
|
+
}
|
|
971
|
+
return verdict;
|
|
972
|
+
}
|
|
973
|
+
if (rawId && userFacingByRawId.has(rawId))
|
|
974
|
+
return userFacingByRawId.get(rawId);
|
|
975
|
+
// `Network.requestWillBeSentExtraInfo` can arrive BEFORE its own
|
|
976
|
+
// `requestWillBeSent` (real CDP ordering) — it carries no url, so an
|
|
977
|
+
// unrecorded rawId here means "classification not yet known", not
|
|
978
|
+
// "capture attached mid-flight". Fail CLOSED for this one case: the
|
|
979
|
+
// very next event for the same rawId is (almost always) the real
|
|
980
|
+
// requestWillBeSent, which establishes the true verdict immediately —
|
|
981
|
+
// dropping this one early line is a far smaller cost than leaking an
|
|
982
|
+
// internal resource's ExtraInfo into the user-facing panel before its
|
|
983
|
+
// classification is even known. Every OTHER "rawId never seen" case
|
|
984
|
+
// (e.g. capture attached mid-flight, no requestWillBeSent ever coming)
|
|
985
|
+
// keeps failing open — there is no better signal available there.
|
|
986
|
+
if (method === "Network.requestWillBeSentExtraInfo")
|
|
987
|
+
return false;
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
572
990
|
const onMessage = (_event, method, params) => {
|
|
573
991
|
// ── Primary sink: forward the raw CDP event into the DevTools front-end ──
|
|
574
992
|
if (FORWARDED_METHODS.has(method)) {
|
|
575
993
|
const rewritten = rewriteRequestId(method, params, ns);
|
|
576
|
-
|
|
994
|
+
// Global mirror: full, unfiltered — independent of the
|
|
995
|
+
// user-facing sink's classification and state machine.
|
|
996
|
+
dispatchToGlobal(rewritten.method, rewritten.params);
|
|
997
|
+
// User-facing sink: only requests isUserFacingRequest() judged as the
|
|
998
|
+
// developer's own business traffic — internal/framework resource
|
|
999
|
+
// loads are mirrored to the global window ONLY.
|
|
1000
|
+
const rawId = params
|
|
1001
|
+
?.requestId;
|
|
1002
|
+
const userFacing = resolveUserFacing(method, params, typeof rawId === "string" ? rawId : undefined);
|
|
1003
|
+
if (userFacing) {
|
|
1004
|
+
enqueueNative(rewritten.method, rewritten.params);
|
|
1005
|
+
}
|
|
577
1006
|
}
|
|
578
1007
|
else if (REWRITE_REQUEST_ID_METHODS.has(method)) {
|
|
579
|
-
// Methods we namespace but don't forward (dataReceived
|
|
580
|
-
//
|
|
1008
|
+
// Methods we namespace but don't forward (dataReceived): still resolve
|
|
1009
|
+
// so the id mapping stays coherent if forwarding is added later.
|
|
581
1010
|
rewriteRequestId(method, params, ns);
|
|
582
1011
|
}
|
|
583
1012
|
// ── Fallback bookkeeping (used only when native dispatch is unusable) ──
|
|
584
|
-
|
|
585
|
-
case 'Network.requestWillBeSent': {
|
|
586
|
-
const p = params;
|
|
587
|
-
if (!p?.request)
|
|
588
|
-
return;
|
|
589
|
-
if (pending.size >= MAX_PENDING)
|
|
590
|
-
pending.clear();
|
|
591
|
-
pending.set(p.requestId, { url: p.request.url, method: p.request.method, status: 0 });
|
|
592
|
-
break;
|
|
593
|
-
}
|
|
594
|
-
case 'Network.responseReceived': {
|
|
595
|
-
const p = params;
|
|
596
|
-
const req = pending.get(p.requestId);
|
|
597
|
-
if (req)
|
|
598
|
-
req.status = p.response?.status ?? 0;
|
|
599
|
-
break;
|
|
600
|
-
}
|
|
601
|
-
case 'Network.loadingFinished': {
|
|
602
|
-
const p = params;
|
|
603
|
-
const req = pending.get(p.requestId);
|
|
604
|
-
if (!req)
|
|
605
|
-
return;
|
|
606
|
-
pending.delete(p.requestId);
|
|
607
|
-
maybeFallback({ source: 'service', url: req.url, method: req.method, status: req.status });
|
|
608
|
-
break;
|
|
609
|
-
}
|
|
610
|
-
case 'Network.loadingFailed': {
|
|
611
|
-
const p = params;
|
|
612
|
-
const req = pending.get(p.requestId);
|
|
613
|
-
if (!req)
|
|
614
|
-
return;
|
|
615
|
-
pending.delete(p.requestId);
|
|
616
|
-
maybeFallback({
|
|
617
|
-
source: 'service',
|
|
618
|
-
url: req.url,
|
|
619
|
-
method: req.method,
|
|
620
|
-
status: req.status,
|
|
621
|
-
errorText: p.canceled ? 'canceled' : (p.errorText || 'failed'),
|
|
622
|
-
});
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
default:
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
1013
|
+
FALLBACK_HANDLERS[method]?.(params);
|
|
628
1014
|
};
|
|
629
|
-
wc.debugger.on(
|
|
1015
|
+
wc.debugger.on("message", onMessage);
|
|
630
1016
|
attach.add(() => {
|
|
631
1017
|
try {
|
|
632
|
-
wc.debugger.removeListener(
|
|
1018
|
+
wc.debugger.removeListener("message", onMessage);
|
|
633
1019
|
}
|
|
634
|
-
catch {
|
|
635
|
-
|
|
636
|
-
const onDetach = () => { if (simWc === wc) {
|
|
637
|
-
simWc = null;
|
|
638
|
-
} };
|
|
639
|
-
wc.debugger.on('detach', onDetach);
|
|
640
|
-
attach.add(() => {
|
|
641
|
-
try {
|
|
642
|
-
wc.debugger.removeListener('detach', onDetach);
|
|
1020
|
+
catch {
|
|
1021
|
+
/* wc gone */
|
|
643
1022
|
}
|
|
644
|
-
catch { /* wc gone */ }
|
|
645
1023
|
});
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1024
|
+
}
|
|
1025
|
+
/** Drop one guest's wiring (message listener + broker lease). Attach/detach
|
|
1026
|
+
* ownership of the underlying session is entirely the broker's concern. */
|
|
1027
|
+
function cleanupGuest(wcId) {
|
|
1028
|
+
const teardown = guestWired.get(wcId);
|
|
1029
|
+
guestWired.delete(wcId);
|
|
1030
|
+
teardown?.disposeAll();
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* A hot-reload respawn replaces the render-host `<webview>` guest with a
|
|
1034
|
+
* fresh one (see simulator-app.tsx's ready-then-swap session commit); the
|
|
1035
|
+
* OLD guest's wc is closed asynchronously as part of the old session's
|
|
1036
|
+
* teardown (bridge-router's `closeSessionPages`), so there is a window
|
|
1037
|
+
* where `wc.isDestroyed()` is still `false` but the debugger session is
|
|
1038
|
+
* already gone (Electron reports `detach` with reason `"target closed"` —
|
|
1039
|
+
* confirmed via instrumentation against a real respawn: every detach in
|
|
1040
|
+
* that run carried this exact reason, one per closing guest, never a
|
|
1041
|
+
* repeated storm on one guest). Retrying the re-attach INLINE on every
|
|
1042
|
+
* `detach` (no delay) raced that async teardown: each re-attach attempt
|
|
1043
|
+
* landed inside the still-closing window and immediately produced another
|
|
1044
|
+
* `detach`, self-sustaining a tight synchronous loop (confirmed by
|
|
1045
|
+
* instrumentation: thousands of attach/detach cycles within a couple
|
|
1046
|
+
* seconds) until the wc finally finished closing — there is no public
|
|
1047
|
+
* Electron event for "this wc's close is about to complete", so the only
|
|
1048
|
+
* available signal to end the loop is `wc.isDestroyed()` itself, which the
|
|
1049
|
+
* retry already checks. This delay does not wait for any timing to
|
|
1050
|
+
* "settle" — it only paces the retry slower than the async close, so the
|
|
1051
|
+
* loop's real termination check (`wc.isDestroyed()` / `disposed`) has a
|
|
1052
|
+
* chance to observe the close having finished before the next attempt.
|
|
1053
|
+
*/
|
|
1054
|
+
const RENDER_GUEST_REATTACH_DELAY_MS = 300;
|
|
1055
|
+
/**
|
|
1056
|
+
* Schedule one more `wireRenderGuest(wc)` attempt after
|
|
1057
|
+
* `RENDER_GUEST_REATTACH_DELAY_MS`, guarded so a guest that has since been
|
|
1058
|
+
* destroyed (or a forwarder that has since been disposed) never gets a
|
|
1059
|
+
* stray retry. Shared by both places capture can fail to be wired: an
|
|
1060
|
+
* established session later getting detached, AND `broker.acquire()`
|
|
1061
|
+
* itself refusing on the very first attempt (the session is exclusively
|
|
1062
|
+
* held elsewhere, e.g. a real Chrome DevTools window) — that second case
|
|
1063
|
+
* used to give up permanently instead of retrying, so capture never
|
|
1064
|
+
* recovered even once the holder released it.
|
|
1065
|
+
*
|
|
1066
|
+
* De-duplicated via `guestRetryGeneration`: a repeat `attachRenderGuest(wc)`
|
|
1067
|
+
* call while one guest's retry is already pending must join the SAME
|
|
1068
|
+
* chain, not start an independent second one — `guestWired` alone cannot
|
|
1069
|
+
* catch this because it is only populated once acquire actually succeeds.
|
|
1070
|
+
* Each schedule mints a fresh generation token; the fired timer only acts
|
|
1071
|
+
* if its captured token is still the current one, so a stale timer whose
|
|
1072
|
+
* generation has since been superseded silently no-ops instead of clearing
|
|
1073
|
+
* (and forking) a newer retry's bookkeeping.
|
|
1074
|
+
*/
|
|
1075
|
+
function scheduleRenderGuestRetry(wc) {
|
|
1076
|
+
if (wc.isDestroyed())
|
|
1077
|
+
return;
|
|
1078
|
+
if (guestRetryGeneration.has(wc.id))
|
|
1079
|
+
return;
|
|
1080
|
+
const generation = ++nextRenderGuestRetryGeneration;
|
|
1081
|
+
guestRetryGeneration.set(wc.id, generation);
|
|
1082
|
+
const timer = setTimeout(() => {
|
|
1083
|
+
if (guestRetryGeneration.get(wc.id) !== generation)
|
|
1084
|
+
return;
|
|
1085
|
+
guestRetryGeneration.delete(wc.id);
|
|
1086
|
+
if (!disposed && !wc.isDestroyed())
|
|
1087
|
+
wireRenderGuest(wc);
|
|
1088
|
+
}, RENDER_GUEST_REATTACH_DELAY_MS);
|
|
1089
|
+
// Best-effort: if the whole forwarder tears down before this fires, there
|
|
1090
|
+
// is nothing to clear it from — the disposed check above is the guard.
|
|
1091
|
+
timer.unref?.();
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Wire one render guest for Network capture via the shared broker.
|
|
1095
|
+
*
|
|
1096
|
+
* The lease's `onDetach` fires on EITHER an external detach (another owner
|
|
1097
|
+
* releasing the shared session, or a real Chrome DevTools window stealing
|
|
1098
|
+
* it) OR the guest being destroyed (see cdp-session/index.ts) — either way
|
|
1099
|
+
* our wiring is torn down and, if the guest is still alive, re-wired (after
|
|
1100
|
+
* `RENDER_GUEST_REATTACH_DELAY_MS`) so capture self-heals. Previously
|
|
1101
|
+
* `attachRenderGuest` only ever ran ONCE per guest (at webview creation,
|
|
1102
|
+
* from `did-attach-webview`), so any detach permanently killed capture for
|
|
1103
|
+
* that guest's remaining lifetime — nothing else ever called it again.
|
|
1104
|
+
*
|
|
1105
|
+
* `guestWired` alone guards re-entry once successfully wired. A repeat call
|
|
1106
|
+
* while acquire keeps failing is deliberately NOT blocked here (only the
|
|
1107
|
+
* TIMER that call would schedule is de-duplicated, in
|
|
1108
|
+
* `scheduleRenderGuestRetry`) — an explicit re-attach right after a detach
|
|
1109
|
+
* must still get its own immediate `acquire()` attempt (the exclusive
|
|
1110
|
+
* holder may have already released it by then), not be forced to wait out
|
|
1111
|
+
* a stale pending window.
|
|
1112
|
+
*/
|
|
1113
|
+
function wireRenderGuest(wc) {
|
|
1114
|
+
if (guestWired.has(wc.id))
|
|
1115
|
+
return;
|
|
1116
|
+
const lease = broker.acquire(wc);
|
|
1117
|
+
if (!lease) {
|
|
1118
|
+
// Not terminal — the exclusive holder may release the session later.
|
|
1119
|
+
// Retry on the same cadence as the onDetach self-heal below, or this
|
|
1120
|
+
// guest's capture would die for its whole remaining lifetime the very
|
|
1121
|
+
// first time acquire() lost the race.
|
|
1122
|
+
scheduleRenderGuestRetry(wc);
|
|
1123
|
+
return;
|
|
663
1124
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1125
|
+
// This wc is now genuinely wired — drop any stale pending-retry
|
|
1126
|
+
// generation (e.g. from an earlier detach cycle superseded by THIS
|
|
1127
|
+
// successful acquire) so a LATER detach can freely mint its own fresh
|
|
1128
|
+
// generation instead of being silently swallowed by a leftover entry.
|
|
1129
|
+
guestRetryGeneration.delete(wc.id);
|
|
1130
|
+
const teardown = new SyncDisposableRegistry();
|
|
1131
|
+
guestWired.set(wc.id, teardown);
|
|
1132
|
+
wireNetworkCapture(wc, "render", teardown, `g${wc.id}-${Date.now()}`);
|
|
1133
|
+
const detachSub = lease.onDetach(() => {
|
|
1134
|
+
cleanupGuest(wc.id);
|
|
1135
|
+
scheduleRenderGuestRetry(wc);
|
|
1136
|
+
});
|
|
1137
|
+
teardown.add(() => {
|
|
1138
|
+
detachSub.dispose();
|
|
1139
|
+
lease.dispose();
|
|
1140
|
+
});
|
|
1141
|
+
void lease.send("Network.enable").catch((err) => {
|
|
1142
|
+
console.warn("[network-forward] guest Network.enable failed:", err instanceof Error ? err.message : err);
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
function attachRenderGuest(wc) {
|
|
1146
|
+
if (!wc || wc.isDestroyed())
|
|
1147
|
+
return;
|
|
1148
|
+
wireRenderGuest(wc);
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Attach (or reuse) the simulator wc's shared session via the broker. The
|
|
1152
|
+
* SAME wc is independently used by simulator-storage (DOMStorage capture) —
|
|
1153
|
+
* `broker.acquire` reuses its session rather than fighting it for exclusive
|
|
1154
|
+
* ownership, and our own switch-away/dispose only ever releases OUR lease
|
|
1155
|
+
* (see detachSimulator), never forces a physical detach that would kill
|
|
1156
|
+
* simulator-storage's capture too.
|
|
1157
|
+
*/
|
|
1158
|
+
function attachSimulator(wc) {
|
|
1159
|
+
if (!wc || wc.isDestroyed())
|
|
1160
|
+
return;
|
|
1161
|
+
if (simWc === wc && !wc.isDestroyed())
|
|
1162
|
+
return;
|
|
1163
|
+
detachSimulator();
|
|
1164
|
+
const lease = broker.acquire(wc);
|
|
1165
|
+
if (!lease) {
|
|
1166
|
+
console.warn("[network-forward] debugger session unavailable; simulator network not captured");
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
simWc = wc;
|
|
1170
|
+
simLease = lease;
|
|
1171
|
+
const attach = new SyncDisposableRegistry();
|
|
1172
|
+
attachDisposables = attach;
|
|
1173
|
+
wireNetworkCapture(wc, "service", attach, String(Date.now()));
|
|
1174
|
+
const detachSub = lease.onDetach(() => {
|
|
1175
|
+
if (simWc !== wc)
|
|
1176
|
+
return;
|
|
1177
|
+
simWc = null;
|
|
1178
|
+
simLease = null;
|
|
1179
|
+
// Tear down OUR OWN wiring (wireNetworkCapture's 'message' listener) —
|
|
1180
|
+
// the broker already removed its own; without this, our listener would
|
|
1181
|
+
// keep receiving events from a session we no longer track as "current".
|
|
1182
|
+
const ad = attachDisposables;
|
|
1183
|
+
attachDisposables = null;
|
|
1184
|
+
if (ad) {
|
|
667
1185
|
try {
|
|
668
|
-
|
|
1186
|
+
ad.disposeAll();
|
|
669
1187
|
}
|
|
670
|
-
catch {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1188
|
+
catch {
|
|
1189
|
+
/* best-effort teardown */
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
attach.add(() => detachSub.dispose());
|
|
1194
|
+
void lease.send("Network.enable").catch((err) => {
|
|
1195
|
+
console.warn("[network-forward] Network.enable failed:", err instanceof Error ? err.message : err);
|
|
675
1196
|
});
|
|
676
1197
|
}
|
|
677
1198
|
/**
|
|
@@ -687,18 +1208,18 @@ export function createNetworkForwarder(bridge) {
|
|
|
687
1208
|
// 'idle' but a host is resolvable (set via the bridge, applyDevtoolsHost not
|
|
688
1209
|
// run): the native path is in play, so promote to 'probing' instead of
|
|
689
1210
|
// console — otherwise this completion would later double-render natively.
|
|
690
|
-
if (sink ===
|
|
1211
|
+
if (sink === "idle" && resolveDevtoolsWc())
|
|
691
1212
|
beginProbing();
|
|
692
1213
|
switch (sink) {
|
|
693
|
-
case
|
|
1214
|
+
case "ready":
|
|
694
1215
|
return;
|
|
695
|
-
case
|
|
1216
|
+
case "probing":
|
|
696
1217
|
if (probeConsoleBuffer.length >= MAX_PENDING)
|
|
697
1218
|
probeConsoleBuffer.shift();
|
|
698
1219
|
probeConsoleBuffer.push(record);
|
|
699
1220
|
return;
|
|
700
|
-
case
|
|
701
|
-
case
|
|
1221
|
+
case "degraded":
|
|
1222
|
+
case "idle":
|
|
702
1223
|
default:
|
|
703
1224
|
forwardToConsole(record);
|
|
704
1225
|
}
|
|
@@ -724,7 +1245,7 @@ export function createNetworkForwarder(bridge) {
|
|
|
724
1245
|
if (!devtoolsWc) {
|
|
725
1246
|
// No host → 'idle': completions go straight to console; native queue is
|
|
726
1247
|
// moot, drop it so it can't double-render if a host later appears.
|
|
727
|
-
sink =
|
|
1248
|
+
sink = "idle";
|
|
728
1249
|
dispatchQueue = [];
|
|
729
1250
|
return;
|
|
730
1251
|
}
|
|
@@ -732,7 +1253,9 @@ export function createNetworkForwarder(bridge) {
|
|
|
732
1253
|
// WITHOUT touching view-manager (host-destroyed cleanup lives here). Begin
|
|
733
1254
|
// probing and flush anything already queued.
|
|
734
1255
|
const host = devtoolsWc;
|
|
735
|
-
const onHostDestroyed = () => {
|
|
1256
|
+
const onHostDestroyed = () => {
|
|
1257
|
+
applyDevtoolsHost(null);
|
|
1258
|
+
};
|
|
736
1259
|
// Route host-destroyed teardown through the connection registry when present
|
|
737
1260
|
// (the Connection fires onHostDestroyed on wc destroy / reset, and the
|
|
738
1261
|
// returned Disposable releases the ownership early on host swap/clear);
|
|
@@ -744,18 +1267,20 @@ export function createNetworkForwarder(bridge) {
|
|
|
744
1267
|
// gated by the SAME `typeof host.once === 'function'` guard the fallback
|
|
745
1268
|
// uses — otherwise a minimal/fake DevTools host (no emitter) throws on the
|
|
746
1269
|
// connection path where the fallback would safely no-op.
|
|
747
|
-
if (reg && typeof host.once ===
|
|
1270
|
+
if (reg && typeof host.once === "function") {
|
|
748
1271
|
const owned = reg.acquire(host).own(onHostDestroyed);
|
|
749
1272
|
devtoolsHostDisposable = toDisposable(() => owned.dispose());
|
|
750
1273
|
}
|
|
751
1274
|
else {
|
|
752
|
-
if (typeof host.once ===
|
|
753
|
-
host.once(
|
|
1275
|
+
if (typeof host.once === "function")
|
|
1276
|
+
host.once("destroyed", onHostDestroyed);
|
|
754
1277
|
devtoolsHostDisposable = toDisposable(() => {
|
|
755
1278
|
try {
|
|
756
|
-
host.removeListener?.(
|
|
1279
|
+
host.removeListener?.("destroyed", onHostDestroyed);
|
|
1280
|
+
}
|
|
1281
|
+
catch {
|
|
1282
|
+
/* gone */
|
|
757
1283
|
}
|
|
758
|
-
catch { /* gone */ }
|
|
759
1284
|
});
|
|
760
1285
|
}
|
|
761
1286
|
// A host swap while still 'probing' the OLD host must not inherit its
|
|
@@ -763,11 +1288,14 @@ export function createNetworkForwarder(bridge) {
|
|
|
763
1288
|
// (re-)arming readyTimeoutTimer/probeDeadline for the NEW host, leaving it
|
|
764
1289
|
// probing forever with no timeout. Force through 'idle' so beginProbing()
|
|
765
1290
|
// always arms a fresh window for whichever host is now current.
|
|
766
|
-
sink =
|
|
1291
|
+
sink = "idle";
|
|
767
1292
|
beginProbing();
|
|
768
1293
|
if (dispatchQueue.length > 0)
|
|
769
1294
|
scheduleFlush();
|
|
770
1295
|
}
|
|
1296
|
+
registry.add(() => {
|
|
1297
|
+
disposed = true;
|
|
1298
|
+
});
|
|
771
1299
|
registry.add(() => {
|
|
772
1300
|
devtoolsHostDisposable?.dispose();
|
|
773
1301
|
devtoolsHostDisposable = null;
|
|
@@ -780,14 +1308,116 @@ export function createNetworkForwarder(bridge) {
|
|
|
780
1308
|
readyRetryTimer = null;
|
|
781
1309
|
}
|
|
782
1310
|
});
|
|
1311
|
+
registry.add(() => {
|
|
1312
|
+
globalDevtoolsHostDisposable?.dispose();
|
|
1313
|
+
globalDevtoolsHostDisposable = null;
|
|
1314
|
+
globalBodyGateStop?.();
|
|
1315
|
+
globalBodyGateStop = null;
|
|
1316
|
+
globalDevtoolsWc = null;
|
|
1317
|
+
// A live settle-poll timer at dispose time is a real-timer leak (this
|
|
1318
|
+
// suite's flaky-test history: undisposed timers adopted by later tests'
|
|
1319
|
+
// fake clocks).
|
|
1320
|
+
clearGlobalPending();
|
|
1321
|
+
});
|
|
1322
|
+
registry.add(() => {
|
|
1323
|
+
bodyCache.clear();
|
|
1324
|
+
postDataCache.clear();
|
|
1325
|
+
});
|
|
1326
|
+
// Every debugger session (simulator + all render guests) must already be
|
|
1327
|
+
// detached and every 'message' listener already removed before `dispose()`
|
|
1328
|
+
// returns control to an un-awaited caller (every real call site is
|
|
1329
|
+
// `fwd.dispose()` with no `await`) — a guest event arriving in the SAME
|
|
1330
|
+
// tick as dispose() must never be forwarded. `registry` is a
|
|
1331
|
+
// SyncDisposableRegistry, so every entry below runs to completion before
|
|
1332
|
+
// disposeAll() returns — no ordering dependency between them.
|
|
783
1333
|
registry.add(() => detachSimulator());
|
|
1334
|
+
registry.add(() => {
|
|
1335
|
+
for (const wcId of [...guestWired.keys()])
|
|
1336
|
+
cleanupGuest(wcId);
|
|
1337
|
+
});
|
|
1338
|
+
// Only detach sessions we self-attached if we own the broker's lifecycle —
|
|
1339
|
+
// a shared/injected broker keeps serving other consumers past our dispose().
|
|
1340
|
+
registry.add(() => {
|
|
1341
|
+
if (ownsBroker)
|
|
1342
|
+
broker.dispose();
|
|
1343
|
+
});
|
|
1344
|
+
// ── Main-process WebSocket trace → synthesized Network.webSocket* CDP ─────
|
|
1345
|
+
// One synthesizer for the forwarder's lifetime: its virtual-id epoch keeps
|
|
1346
|
+
// `dimina:ws:` ids collision-free across simulator re-attaches, and its
|
|
1347
|
+
// per-socket verdict cache mirrors resolveUserFacing's decide-once rule.
|
|
1348
|
+
const wsSynthesizer = new WebSocketTraceSynthesizer({
|
|
1349
|
+
epoch: String(Date.now()),
|
|
1350
|
+
internalOrigins: () => [
|
|
1351
|
+
bridge.getResourceServerBaseUrl?.(),
|
|
1352
|
+
bridge.getSimulatorServerBaseUrl?.(),
|
|
1353
|
+
],
|
|
1354
|
+
});
|
|
1355
|
+
function reportWebSocketTrace(sessionId, event) {
|
|
1356
|
+
if (disposed)
|
|
1357
|
+
return;
|
|
1358
|
+
const message = wsSynthesizer.synthesize(sessionId, event);
|
|
1359
|
+
if (!message)
|
|
1360
|
+
return;
|
|
1361
|
+
// Global mirror first (full, unfiltered), then the user-facing native
|
|
1362
|
+
// sink gated on the created-time verdict — the same routing
|
|
1363
|
+
// wireNetworkCapture's onMessage uses for simulator traffic.
|
|
1364
|
+
dispatchToGlobal(message.method, message.params);
|
|
1365
|
+
if (message.userFacing)
|
|
1366
|
+
enqueueNative(message.method, message.params);
|
|
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
|
+
}
|
|
784
1406
|
return {
|
|
785
1407
|
attachSimulator,
|
|
786
1408
|
detachSimulator,
|
|
1409
|
+
attachRenderGuest,
|
|
787
1410
|
setDevtoolsHost: (wc) => applyDevtoolsHost(wc),
|
|
1411
|
+
setGlobalDevtoolsHost: (wc) => applyGlobalDevtoolsHost(wc),
|
|
788
1412
|
// report() has no observing debugger, so there's no live CDP event to push
|
|
789
1413
|
// natively — surface it via the console fallback line.
|
|
790
1414
|
report: (record) => forwardToConsole(record),
|
|
1415
|
+
reportWebSocketTrace,
|
|
1416
|
+
reportNativeRequestTrace,
|
|
1417
|
+
bodies: {
|
|
1418
|
+
getResponseBody: (requestId) => bodyCache.lookup(requestId),
|
|
1419
|
+
getRequestPostData: (requestId) => postDataCache.lookup(requestId),
|
|
1420
|
+
},
|
|
791
1421
|
dispose: () => registry.disposeAll(),
|
|
792
1422
|
};
|
|
793
1423
|
}
|