@dimina-kit/devtools 0.4.0-dev.20260831134112 → 0.4.0-dev.20260907055341
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 +52 -729
- package/dist/main/app/app.d.ts +27 -7
- package/dist/main/app/app.js +285 -681
- 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/menu-setup.d.ts +13 -0
- package/dist/main/app/menu-setup.js +49 -0
- 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 +178 -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 +10669 -9096
- package/dist/main/ipc/app.d.ts +4 -1
- package/dist/main/ipc/app.js +4 -3
- 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 +1 -0
- package/dist/main/ipc/index.js +1 -0
- package/dist/main/ipc/internal-devtools.d.ts +2 -1
- package/dist/main/ipc/internal-devtools.js +4 -3
- package/dist/main/ipc/popover.d.ts +4 -1
- package/dist/main/ipc/popover.js +25 -9
- package/dist/main/ipc/project-create.d.ts +2 -1
- package/dist/main/ipc/project-create.js +6 -5
- package/dist/main/ipc/project-fs.d.ts +3 -1
- package/dist/main/ipc/project-fs.js +17 -17
- package/dist/main/ipc/projects.d.ts +3 -3
- package/dist/main/ipc/projects.js +13 -12
- package/dist/main/ipc/session.d.ts +4 -1
- package/dist/main/ipc/session.js +16 -11
- package/dist/main/ipc/settings.d.ts +4 -1
- package/dist/main/ipc/settings.js +12 -19
- package/dist/main/ipc/simulator-module.d.ts +5 -0
- package/dist/main/ipc/simulator-module.js +9 -5
- package/dist/main/ipc/simulator.d.ts +4 -1
- package/dist/main/ipc/simulator.js +12 -18
- package/dist/main/ipc/tooltip.d.ts +2 -1
- package/dist/main/ipc/tooltip.js +8 -7
- package/dist/main/ipc/views.d.ts +4 -1
- package/dist/main/ipc/views.js +20 -9
- package/dist/main/runtime/devtools-backend-before-quit.harness.d.ts +2 -20
- package/dist/main/runtime/devtools-backend-before-quit.harness.js +14 -283
- package/dist/main/runtime/devtools-backend.js +14 -3
- 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/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/default-adapter.js +1 -1
- 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 +8 -1
- package/dist/main/services/fs-watch-sse.js +19 -5
- 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/target-manager.d.ts +71 -23
- package/dist/main/services/mcp/target-manager.js +269 -142
- 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 +29 -6
- package/dist/main/services/mcp/tools/project-tools.js +37 -25
- package/dist/main/services/module.d.ts +13 -5
- package/dist/main/services/notifications/renderer-notifier.d.ts +28 -4
- package/dist/main/services/notifications/renderer-notifier.js +15 -3
- package/dist/main/services/projects/local-provider.js +2 -0
- package/dist/main/services/projects/project-repository.d.ts +31 -1
- package/dist/main/services/projects/project-repository.js +144 -20
- package/dist/main/services/projects/types.d.ts +31 -8
- package/dist/main/services/safe-area/index.d.ts +19 -5
- package/dist/main/services/safe-area/index.js +38 -13
- package/dist/main/services/simulator/ui-extensions.d.ts +8 -0
- package/dist/main/services/simulator/ui-extensions.js +16 -6
- package/dist/main/services/simulator-storage/index.d.ts +12 -1
- package/dist/main/services/simulator-storage/index.js +22 -7
- 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/views/host-slot-port-channel.d.ts +20 -3
- package/dist/main/services/views/host-slot-port-channel.js +33 -5
- package/dist/main/services/views/managed-web-contents-view.js +21 -1
- package/dist/main/services/views/native-simulator-view.js +13 -16
- package/dist/main/services/views/overlay-panels-view.d.ts +13 -0
- package/dist/main/services/views/overlay-panels-view.js +31 -0
- package/dist/main/services/views/view-manager.d.ts +1 -1
- package/dist/main/services/views/view-manager.js +3 -0
- 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.js +75 -133
- 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 +27 -3
- package/dist/main/services/workbench-context.js +10 -5
- 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/workspace-compile-modes.d.ts +59 -0
- package/dist/main/services/workspace/workspace-compile-modes.js +93 -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 +4 -0
- package/dist/main/services/workspace/workspace-service.js +51 -84
- 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 +42 -36
- package/dist/main/utils/ipc-registry.js +117 -50
- package/dist/main/utils/sender-policy.d.ts +9 -0
- package/dist/main/utils/sender-policy.js +26 -3
- package/dist/main/utils/theme.d.ts +3 -2
- package/dist/main/utils/theme.js +3 -2
- 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/native-host/common/common.js +94 -73
- package/dist/native-host/render/render.js +4079 -3171
- package/dist/native-host/service/service.js +2 -2
- package/dist/preload/runtime/native-host.d.ts +4 -0
- package/dist/preload/runtime/temp-files.js +9 -2
- package/dist/preload/runtime/wait-for-slot-root.d.ts +7 -4
- package/dist/preload/runtime/wait-for-slot-root.js +36 -8
- package/dist/preload/windows/host-dialog-runtime.cjs +10 -3
- package/dist/preload/windows/host-dialog-runtime.cjs.map +2 -2
- package/dist/preload/windows/host-sidebar-runtime.cjs +10 -3
- package/dist/preload/windows/host-sidebar-runtime.cjs.map +2 -2
- package/dist/preload/windows/host-toolbar-runtime.cjs +10 -3
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +181 -4
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/simulator.cjs +10 -0
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +10 -0
- 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-Bqi8hpdi.js → createLucideIcon-cZ9FlZZw.js} +2 -2
- 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-DKWdE1TK.js → dist-DIFlRMKe.js} +2 -2
- package/dist/renderer/assets/hostSidebarDefault-DRWh6OCt.js +2 -0
- package/dist/renderer/assets/index-CEL_XFge.js +2 -0
- package/dist/renderer/assets/{input-Ct3alvkq.js → input-CAObw95L.js} +2 -2
- 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-DGLHmmOq.js → jsx-runtime-DIw5wnYl.js} +1 -1
- 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-Bbjz75bP.js → projectCreateDialog-BmLqiYxn.js} +2 -2
- 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-B_-_YVbw.js → tooltip-D6AxXRk5.js} +2 -2
- package/dist/renderer/assets/types-Bh0hA7El.js +2 -0
- package/dist/renderer/assets/updateDialog-CwndJ0Bl.js +2 -0
- package/dist/renderer/assets/{utils-kfQov6Pa.js → utils-sEB5OZEQ.js} +2 -2
- 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-BLJ0wKsg.js +5 -0
- package/dist/renderer/assets/{workbenchSettings-CV4LVA9b.js → workbenchSettings-YD0Ctbm_.js} +3 -3
- package/dist/renderer/entries/device-picker/index.html +28 -0
- package/dist/renderer/entries/host-sidebar-default/index.html +6 -5
- package/dist/renderer/entries/main/index.html +16 -17
- package/dist/renderer/entries/popover/index.html +16 -10
- package/dist/renderer/entries/project-create-dialog/index.html +12 -12
- package/dist/renderer/entries/settings/index.html +10 -10
- package/dist/renderer/entries/tooltip/index.html +5 -5
- package/dist/renderer/entries/update-dialog/index.html +10 -10
- package/dist/renderer/entries/workbench/index.html +49 -0
- package/dist/renderer/entries/workbench-settings/index.html +12 -11
- package/dist/service-host/host-env-update.cjs +23 -0
- package/dist/service-host/preload.cjs +24 -49
- package/dist/service-host/service-message-queue.cjs +71 -0
- package/dist/service-host/sync-impls/system-info.js +31 -9
- 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/ipc-channels-overlays.d.ts +8 -2
- package/dist/shared/ipc-channels-overlays.js +19 -2
- package/dist/shared/ipc-channels.d.ts +5 -10
- package/dist/shared/ipc-channels.js +17 -14
- package/dist/shared/ipc-schemas.d.ts +76 -24
- package/dist/shared/ipc-schemas.js +55 -10
- package/dist/shared/project-window-types.d.ts +42 -0
- package/dist/shared/project-window-types.js +2 -0
- package/dist/shared/types.d.ts +43 -17
- package/dist/shared/view-ids.d.ts +1 -0
- package/dist/shared/view-ids.js +3 -1
- 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-pxnZZp10.js +431 -0
- package/dist/simulator/assets/simulator-Dwp7lnvu.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-DFo7fhi5.js +2 -0
- package/dist/simulator/assets/simulator-ui-B2ddJB1R.js +2 -0
- package/dist/simulator/simulator.html +3 -3
- package/dist/simulator/temp-files.d.ts +6 -0
- package/dist/simulator/temp-files.js +46 -4
- package/dist/simulator/types.d.ts +7 -0
- package/dist/vscode-workbench/assets/__vite-browser-external-JIkWewFf.js +1 -0
- package/dist/vscode-workbench/assets/{dist-CMfmHflW.js → dist-CXG-u1VB.js} +3 -3
- package/dist/vscode-workbench/assets/{iconv-lite-umd-Bx8CgaZq.js → iconv-lite-umd-Dxx9Qyb4.js} +1 -1
- package/dist/vscode-workbench/assets/{index-CdG6YWhW.js → index-6VJMsaWj.js} +460 -460
- package/dist/vscode-workbench/assets/{jschardet-M0qgd2lN.js → jschardet-Ba3D16vt.js} +1 -1
- package/dist/vscode-workbench/index.html +1 -1
- package/package.json +11 -8
- package/dist/renderer/assets/button-D5Q2wkXq.js +0 -2
- package/dist/renderer/assets/constants-D7DfVqMp.js +0 -2
- package/dist/renderer/assets/constants-D8zsejFq.js +0 -2
- package/dist/renderer/assets/dialog-e1TecTOY.js +0 -46
- package/dist/renderer/assets/hostSidebarDefault-B3yLfy8j.js +0 -2
- package/dist/renderer/assets/index-CsPD68N_.js +0 -5
- package/dist/renderer/assets/ipc-channels-overlays-LsDzGAtV.js +0 -2
- package/dist/renderer/assets/jsx-runtime-2vXHouwr.css +0 -1
- package/dist/renderer/assets/popover-Dix5cm_b.js +0 -2
- package/dist/renderer/assets/project-api-k7Oq9iaq.js +0 -2
- package/dist/renderer/assets/select-acGb0AmJ.js +0 -2
- package/dist/renderer/assets/settings-CQrTMr43.js +0 -2
- package/dist/renderer/assets/settings-api-CrUQ9rYV.js +0 -2
- package/dist/renderer/assets/settings-tab-bar-Cw1kVIf2.js +0 -2
- package/dist/renderer/assets/types-CsEKtFyr.js +0 -2
- package/dist/renderer/assets/updateDialog-NsF8223A.js +0 -2
- package/dist/renderer/assets/view-api-CLn3rcQE.js +0 -2
- package/dist/simulator/assets/bridge-channels-zhsumfky.js +0 -2
- package/dist/simulator/assets/device-shell-CmtM1QxP.js +0 -2
- package/dist/simulator/assets/device-shell-iFZo1igL.css +0 -1
- package/dist/simulator/assets/simulator-CIobn3Mh.js +0 -10
- package/dist/simulator/assets/simulator-mini-app-CRe6n9br.js +0 -2
- package/dist/simulator/assets/simulator-ui-C_cP602X.js +0 -2
- package/dist/vscode-workbench/assets/__vite-browser-external-Bc1vizTf.js +0 -1
|
@@ -20,27 +20,61 @@ elementHandlers['Element.triggerEvent'] = async (ctx, params) => {
|
|
|
20
20
|
`);
|
|
21
21
|
return {};
|
|
22
22
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Touch events must carry real touch points: the gesture layer reads coordinates
|
|
25
|
+
* off `touches` / `changedTouches` to decide tap vs. move-cancel, so an event with
|
|
26
|
+
* empty lists reaches it as a touch that happened nowhere. The protocol lets the
|
|
27
|
+
* caller omit points entirely (`touchstart()` with no args), in which case the
|
|
28
|
+
* element's centre — read from live layout, not a fixed offset — stands in for
|
|
29
|
+
* where a finger would land.
|
|
30
|
+
*/
|
|
31
|
+
function touchHandler(type) {
|
|
32
|
+
// A finished touch is no longer among the active `touches`, only in `changedTouches`.
|
|
33
|
+
const fallbackTouches = type === 'touchend' ? '[]' : '[{}]';
|
|
34
|
+
return async (ctx, params) => {
|
|
35
|
+
const ref = getElementRef(params);
|
|
36
|
+
const touches = params.touches ?? null;
|
|
37
|
+
// The protocol spells it `changeTouches`; the DOM event field is `changedTouches`.
|
|
38
|
+
const changed = params.changeTouches ?? null;
|
|
39
|
+
await evalInElement(ctx, ref, `
|
|
40
|
+
if (!el) throw new Error('Element not found')
|
|
41
|
+
const rect = el.getBoundingClientRect()
|
|
42
|
+
const centreX = rect.left + rect.width / 2
|
|
43
|
+
const centreY = rect.top + rect.height / 2
|
|
44
|
+
const make = (p, i) => {
|
|
45
|
+
const clientX = p.clientX != null ? p.clientX : (p.pageX != null ? p.pageX - scrollX : centreX)
|
|
46
|
+
const clientY = p.clientY != null ? p.clientY : (p.pageY != null ? p.pageY - scrollY : centreY)
|
|
47
|
+
return new Touch({
|
|
48
|
+
identifier: p.identifier != null ? p.identifier : i,
|
|
49
|
+
target: el,
|
|
50
|
+
clientX,
|
|
51
|
+
clientY,
|
|
52
|
+
pageX: p.pageX != null ? p.pageX : clientX + scrollX,
|
|
53
|
+
pageY: p.pageY != null ? p.pageY : clientY + scrollY,
|
|
54
|
+
screenX: clientX,
|
|
55
|
+
screenY: clientY,
|
|
56
|
+
force: 1,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
const raw = ${JSON.stringify(touches)}
|
|
60
|
+
const rawChanged = ${JSON.stringify(changed)}
|
|
61
|
+
const active = (raw || ${fallbackTouches}).map(make)
|
|
62
|
+
const changedList = (rawChanged || raw || [{}]).map(make)
|
|
63
|
+
el.dispatchEvent(new TouchEvent('${type}', {
|
|
64
|
+
bubbles: true,
|
|
65
|
+
cancelable: true,
|
|
66
|
+
composed: true,
|
|
67
|
+
touches: active,
|
|
68
|
+
targetTouches: active,
|
|
69
|
+
changedTouches: changedList,
|
|
70
|
+
}))
|
|
71
|
+
`);
|
|
72
|
+
return {};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
elementHandlers['Element.touchstart'] = touchHandler('touchstart');
|
|
76
|
+
elementHandlers['Element.touchmove'] = touchHandler('touchmove');
|
|
77
|
+
elementHandlers['Element.touchend'] = touchHandler('touchend');
|
|
44
78
|
elementHandlers['Element.getDOMProperties'] = async (ctx, params) => {
|
|
45
79
|
const ref = getElementRef(params);
|
|
46
80
|
const names = params.names;
|
|
@@ -9,11 +9,22 @@
|
|
|
9
9
|
* This server handles these messages by delegating to the workbench context
|
|
10
10
|
* (simulator webContents, IPC handlers, etc.) without modifying dimina upstream.
|
|
11
11
|
*/
|
|
12
|
+
import { type IpcInput } from '../../utils/ipc-context-source.js';
|
|
12
13
|
import type { WorkbenchContext } from '../workbench-context.js';
|
|
13
14
|
export interface AutomationServer {
|
|
14
15
|
close: () => void;
|
|
15
16
|
port: number;
|
|
16
17
|
}
|
|
17
18
|
export declare function getAutomationPort(): number | null;
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* `getCtx` rather than a context: the server binds its port at boot, before any
|
|
21
|
+
* project window exists, so there is nothing to capture yet. Each connection
|
|
22
|
+
* then pins the first project window it reaches and keeps driving that one —
|
|
23
|
+
* `getCtx` is how it finds that window, not a per-message lookup.
|
|
24
|
+
*
|
|
25
|
+
* `senders` covers every live window instead, because the port channel is asked
|
|
26
|
+
* by renderers `getCtx` never names: the project list and background project
|
|
27
|
+
* windows all start automation clients of their own.
|
|
28
|
+
*/
|
|
29
|
+
export declare function startAutomationServer(getCtx: () => WorkbenchContext, senders: IpcInput<WorkbenchContext>, port?: number): Promise<AutomationServer>;
|
|
19
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
* (simulator webContents, IPC handlers, etc.) without modifying dimina upstream.
|
|
11
11
|
*/
|
|
12
12
|
import { WebSocketServer } from 'ws';
|
|
13
|
-
import { AutomationChannel
|
|
13
|
+
import { AutomationChannel } from '../../../shared/ipc-channels.js';
|
|
14
14
|
import { IpcRegistry } from '../../utils/ipc-registry.js';
|
|
15
|
+
import { toIpcContextSource } from '../../utils/ipc-context-source.js';
|
|
16
|
+
import { createConnectionTarget } from './connection-target.js';
|
|
17
|
+
import { createConsoleBridge } from './console-bridge.js';
|
|
15
18
|
import { getSimulator } from './exec.js';
|
|
16
19
|
import { toolHandlers } from './handlers/tool.js';
|
|
17
20
|
import { appHandlers } from './handlers/app.js';
|
|
@@ -28,7 +31,17 @@ let currentPort = null;
|
|
|
28
31
|
export function getAutomationPort() {
|
|
29
32
|
return currentPort;
|
|
30
33
|
}
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* `getCtx` rather than a context: the server binds its port at boot, before any
|
|
36
|
+
* project window exists, so there is nothing to capture yet. Each connection
|
|
37
|
+
* then pins the first project window it reaches and keeps driving that one —
|
|
38
|
+
* `getCtx` is how it finds that window, not a per-message lookup.
|
|
39
|
+
*
|
|
40
|
+
* `senders` covers every live window instead, because the port channel is asked
|
|
41
|
+
* by renderers `getCtx` never names: the project list and background project
|
|
42
|
+
* windows all start automation clients of their own.
|
|
43
|
+
*/
|
|
44
|
+
export async function startAutomationServer(getCtx, senders, port = 0) {
|
|
32
45
|
const wss = new WebSocketServer({ port });
|
|
33
46
|
const clients = new Set();
|
|
34
47
|
await new Promise((resolve, reject) => {
|
|
@@ -39,125 +52,34 @@ export async function startAutomationServer(ctx, port = 0) {
|
|
|
39
52
|
// ws always binds via an http.Server, so address() is AddressInfo here.
|
|
40
53
|
const resolvedPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
41
54
|
currentPort = resolvedPort;
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
55
|
+
// Gated per OWNING window: each window trusts its own renderer plus its
|
|
56
|
+
// settings/popover overlays (see createWorkbenchSenderPolicy), and the
|
|
57
|
+
// simulator webview is trusted by none of them. Gating on the active
|
|
58
|
+
// window's policy instead would reject every window that does not currently
|
|
59
|
+
// have focus, starting with the project list.
|
|
60
|
+
const portIpc = new IpcRegistry(toIpcContextSource(senders));
|
|
47
61
|
portIpc.handle(AutomationChannel.GetPort, () => {
|
|
48
62
|
return currentPort;
|
|
49
63
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ws.send(msg);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Native-host console forwarding: under native-host the page/service console
|
|
58
|
-
// doesn't flow through the simulator guest's `ipc-message-host` channel (there
|
|
59
|
-
// is no Worker/MiniApp in the guest). Instead the render-host / service-host
|
|
60
|
-
// preloads post each console entry to main, bridge-router routes it to the
|
|
61
|
-
// always-on ConsoleForwarder, and we SUBSCRIBE to rebroadcast every entry
|
|
62
|
-
// (both layers) as an `App.logAdded` event — same shape the default-arch path
|
|
63
|
-
// emits below in `setupConsoleForwarding`. We no longer set `ctx.guestConsole`
|
|
64
|
-
// ourselves (the forwarder owns it); subscribing means render + service both
|
|
65
|
-
// reach automation while the forwarder also mirrors render into the service
|
|
66
|
-
// host's console for the embedded DevTools. Unsubscribed on close().
|
|
67
|
-
const consoleSub = ctx.consoleForwarder?.subscribe((entry) => {
|
|
68
|
-
const e = (entry ?? {});
|
|
69
|
-
broadcast({
|
|
70
|
-
method: 'App.logAdded',
|
|
71
|
-
params: { type: e.level || 'log', args: e.args || [] },
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
// Forward simulator console logs as App.logAdded events.
|
|
75
|
-
// Track the polling interval + stop timer + currently-attached sim and
|
|
76
|
-
// the named handler so we can fully detach on close() or sim destruction.
|
|
77
|
-
// Without this the listener accumulates across create/dispose cycles and
|
|
78
|
-
// across simulator rebuilds.
|
|
79
|
-
let consoleForwardingSetup = false;
|
|
80
|
-
let pollInterval = null;
|
|
81
|
-
let pollStopTimer = null;
|
|
82
|
-
let attachedSim = null;
|
|
83
|
-
let ipcMessageHostHandler = null;
|
|
84
|
-
// Handle for the destroyed-teardown owned by the sim's connection (replaces a
|
|
85
|
-
// bespoke `sim.once('destroyed')` + manual removeListener). Disposing it on
|
|
86
|
-
// graceful close()/detach releases the disposer from the connection segment so
|
|
87
|
-
// it can't accumulate across automation create/dispose cycles on the same sim.
|
|
88
|
-
let simDestroyedDisposer = null;
|
|
89
|
-
function detachConsoleForwarding() {
|
|
90
|
-
if (attachedSim) {
|
|
91
|
-
if (ipcMessageHostHandler) {
|
|
92
|
-
try {
|
|
93
|
-
if (!attachedSim.isDestroyed()) {
|
|
94
|
-
;
|
|
95
|
-
attachedSim.removeListener('ipc-message-host', ipcMessageHostHandler);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
catch { /* noop */ }
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Release the connection-owned destroyed disposer (idempotent; safe even if
|
|
102
|
-
// the sim is already destroyed — the disposer body only nulls local refs).
|
|
103
|
-
try {
|
|
104
|
-
simDestroyedDisposer?.dispose();
|
|
105
|
-
}
|
|
106
|
-
catch { /* noop */ }
|
|
107
|
-
attachedSim = null;
|
|
108
|
-
ipcMessageHostHandler = null;
|
|
109
|
-
simDestroyedDisposer = null;
|
|
110
|
-
}
|
|
111
|
-
function setupConsoleForwarding() {
|
|
112
|
-
if (consoleForwardingSetup)
|
|
113
|
-
return;
|
|
114
|
-
consoleForwardingSetup = true;
|
|
115
|
-
pollInterval = setInterval(() => {
|
|
116
|
-
const sim = getSimulator(ctx);
|
|
117
|
-
if (!sim)
|
|
118
|
-
return;
|
|
119
|
-
if (pollInterval) {
|
|
120
|
-
clearInterval(pollInterval);
|
|
121
|
-
pollInterval = null;
|
|
122
|
-
}
|
|
123
|
-
const onIpcMessageHost = (_event, channel, data) => {
|
|
124
|
-
if (channel === SimulatorChannel.Console) {
|
|
125
|
-
const logData = data;
|
|
126
|
-
broadcast({
|
|
127
|
-
method: 'App.logAdded',
|
|
128
|
-
params: { type: logData.level || 'log', args: logData.args || [] },
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
const onSimDestroyed = () => {
|
|
133
|
-
// Sim webContents is dead — drop refs (removeListener on a destroyed
|
|
134
|
-
// sender would throw) and allow re-attach to a new simulator.
|
|
135
|
-
attachedSim = null;
|
|
136
|
-
ipcMessageHostHandler = null;
|
|
137
|
-
simDestroyedDisposer = null;
|
|
138
|
-
consoleForwardingSetup = false;
|
|
139
|
-
};
|
|
140
|
-
attachedSim = sim;
|
|
141
|
-
ipcMessageHostHandler = onIpcMessageHost;
|
|
142
|
-
sim.on('ipc-message-host', onIpcMessageHost);
|
|
143
|
-
// Route the destroyed-teardown through the per-webContents connection
|
|
144
|
-
// registry: the connection arms a single `wc.once('destroyed')` and runs
|
|
145
|
-
// owned disposers on hard-destroy (or soft reset for pool reuse), so we no
|
|
146
|
-
// longer manage a bespoke 'destroyed' listener here.
|
|
147
|
-
simDestroyedDisposer = ctx.connections.acquire(sim).own(onSimDestroyed);
|
|
148
|
-
}, 1000);
|
|
149
|
-
// Stop polling after 30s
|
|
150
|
-
pollStopTimer = setTimeout(() => {
|
|
151
|
-
if (pollInterval) {
|
|
152
|
-
clearInterval(pollInterval);
|
|
153
|
-
pollInterval = null;
|
|
154
|
-
}
|
|
155
|
-
pollStopTimer = null;
|
|
156
|
-
}, 30000);
|
|
157
|
-
}
|
|
64
|
+
// Every connection owns its console bridge, because every connection has its
|
|
65
|
+
// own target window: a single server-wide broadcast would hand one client the
|
|
66
|
+
// logs of a project another client is driving.
|
|
67
|
+
const bridges = new Set();
|
|
158
68
|
wss.on('connection', (ws) => {
|
|
159
69
|
clients.add(ws);
|
|
160
|
-
|
|
70
|
+
const send = (msg) => {
|
|
71
|
+
if (ws.readyState === ws.OPEN)
|
|
72
|
+
ws.send(JSON.stringify(msg));
|
|
73
|
+
};
|
|
74
|
+
// Pinned, not resolved per message: the window a script drives is settled by
|
|
75
|
+
// the first project window this connection reaches, so a user clicking
|
|
76
|
+
// another project's window can't redirect it (see `createConnectionTarget`).
|
|
77
|
+
const target = createConnectionTarget(getCtx);
|
|
78
|
+
const consoleBridge = createConsoleBridge(() => target.peek(), getSimulator, send);
|
|
79
|
+
bridges.add(consoleBridge);
|
|
80
|
+
// Before any message: a client that only listens for `App.logAdded` never
|
|
81
|
+
// sends one, and still has to receive its target window's console.
|
|
82
|
+
consoleBridge.sync();
|
|
161
83
|
ws.on('message', async (raw) => {
|
|
162
84
|
let req;
|
|
163
85
|
try {
|
|
@@ -174,6 +96,9 @@ export async function startAutomationServer(ctx, port = 0) {
|
|
|
174
96
|
}
|
|
175
97
|
else {
|
|
176
98
|
try {
|
|
99
|
+
// Same target for the command and for the logs it produces.
|
|
100
|
+
const ctx = target.resolve();
|
|
101
|
+
consoleBridge.sync();
|
|
177
102
|
const result = await handler(ctx, params);
|
|
178
103
|
response = { id, result };
|
|
179
104
|
}
|
|
@@ -181,27 +106,23 @@ export async function startAutomationServer(ctx, port = 0) {
|
|
|
181
106
|
response = { id, error: { message: err.message || String(err) } };
|
|
182
107
|
}
|
|
183
108
|
}
|
|
184
|
-
|
|
185
|
-
|
|
109
|
+
send(response);
|
|
110
|
+
});
|
|
111
|
+
ws.on('close', () => {
|
|
112
|
+
clients.delete(ws);
|
|
113
|
+
bridges.delete(consoleBridge);
|
|
114
|
+
consoleBridge.dispose();
|
|
186
115
|
});
|
|
187
|
-
ws.on('close', () => clients.delete(ws));
|
|
188
116
|
});
|
|
189
117
|
return {
|
|
190
118
|
close: () => {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
detachConsoleForwarding();
|
|
200
|
-
// Unsubscribe from the always-on ConsoleForwarder so a late guest console
|
|
201
|
-
// entry can't broadcast against a torn-down server. The forwarder keeps
|
|
202
|
-
// owning `ctx.guestConsole` (render→service mirroring continues without an
|
|
203
|
-
// automation client). No-op if the forwarder was absent.
|
|
204
|
-
consoleSub?.dispose();
|
|
119
|
+
// Detaches every connection's simulator listener and unsubscribes it from
|
|
120
|
+
// its window's ConsoleForwarder, so a late guest console entry can't fire
|
|
121
|
+
// against a torn-down server. The forwarder keeps owning `ctx.guestConsole`
|
|
122
|
+
// (render→service mirroring continues without an automation client).
|
|
123
|
+
for (const bridge of bridges)
|
|
124
|
+
bridge.dispose();
|
|
125
|
+
bridges.clear();
|
|
205
126
|
for (const ws of clients) {
|
|
206
127
|
try {
|
|
207
128
|
ws.close();
|
|
@@ -5,7 +5,7 @@ export const defaultAdapter = {
|
|
|
5
5
|
async openProject(opts) {
|
|
6
6
|
const diminaKit = await import('@dimina-kit/devkit');
|
|
7
7
|
const openProjectOpts = {
|
|
8
|
-
|
|
8
|
+
outputRoot: path.join(app.getPath('userData'), 'dimina-fe-output'),
|
|
9
9
|
...opts,
|
|
10
10
|
simulatorDir,
|
|
11
11
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/__fs` bridge's wire shapes, declared once for the producing side.
|
|
3
|
+
*
|
|
4
|
+
* Every shape here is consumed by browser code in `packages/workbench`
|
|
5
|
+
* (`fs-bridge.ts`'s `FsEntry`, `wal-audit-transport.ts`'s SSE message parse,
|
|
6
|
+
* and through them `@dimina-kit/fs-core/sync/watch-expander`'s stat-diffing).
|
|
7
|
+
* That side cannot import from here — devtools is the application, the
|
|
8
|
+
* workbench is a library it embeds, and the dependency runs one way only. So
|
|
9
|
+
* this module does not give the two ends a shared declaration; it gives the
|
|
10
|
+
* PRODUCING end exactly one, instead of hand-building untyped payloads at each
|
|
11
|
+
* call site. The two ends stay pinned to each other by the round-trip
|
|
12
|
+
* assertions in workbench-coi-server.test.ts and by disk-sync.spec.ts.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The `type` field of every readdir/stat payload. These are wire values, not
|
|
16
|
+
* an internal enum: the browser side compares the raw numbers (watch-expander
|
|
17
|
+
* treats `2` as "recurse into it"), so renumbering them breaks the editor's
|
|
18
|
+
* disk mirror.
|
|
19
|
+
*/
|
|
20
|
+
export declare const FS_TYPE_FILE = 1;
|
|
21
|
+
export declare const FS_TYPE_DIR = 2;
|
|
22
|
+
/**
|
|
23
|
+
* One `/__fs/readdir` entry: `[name, type]` for a directory, `[name, type,
|
|
24
|
+
* size, mtimeMs]` for a file. The trailing stats let the sync engine tell a
|
|
25
|
+
* stat-unchanged survivor from an actually-modified file without re-reading
|
|
26
|
+
* its content. A file entry that raced a concurrent delete between readdir and
|
|
27
|
+
* stat has no stats to send and is TRUNCATED to `[name, 1]` rather than padded
|
|
28
|
+
* with the two values: a JSON array cannot hold a hole, so passing them
|
|
29
|
+
* through as `undefined` would put `null` on the wire, which neither the
|
|
30
|
+
* client's `size?: number` type nor any of the comments describing this format
|
|
31
|
+
* admit. Either way the client's `?? NaN` fallback treats the entry as
|
|
32
|
+
* "always changed"; truncating is what keeps the wire and the types honest.
|
|
33
|
+
*/
|
|
34
|
+
export type FsBridgeEntry = [name: string, type: number, size?: number, mtimeMs?: number];
|
|
35
|
+
/** The `/__fs/stat` payload. Its `mtime` carries the same value readdir sends
|
|
36
|
+
* as `mtimeMs` — the two endpoints named it differently and no client reads
|
|
37
|
+
* this one, so the names are left as shipped rather than changed blind. */
|
|
38
|
+
export interface FsBridgeStat {
|
|
39
|
+
type: number;
|
|
40
|
+
size: number;
|
|
41
|
+
ctime: number;
|
|
42
|
+
mtime: number;
|
|
43
|
+
}
|
|
44
|
+
/** One `/__fs/watch` SSE message: a batch of changed project-relative paths,
|
|
45
|
+
* or the terminal notice that the watcher is gone and the client must fall
|
|
46
|
+
* back to a full rescan. */
|
|
47
|
+
export type FsWatchMessage = {
|
|
48
|
+
paths: string[];
|
|
49
|
+
} | {
|
|
50
|
+
watcherDead: true;
|
|
51
|
+
};
|
|
52
|
+
/** Serialize one SSE message into an `event-stream` frame. */
|
|
53
|
+
export declare function fsWatchFrame(message: FsWatchMessage): string;
|
|
54
|
+
/** Project one `readdirWithin` entry onto the readdir wire shape. */
|
|
55
|
+
export declare function toFsBridgeEntry(entry: {
|
|
56
|
+
name: string;
|
|
57
|
+
isDirectory: boolean;
|
|
58
|
+
size?: number;
|
|
59
|
+
mtimeMs?: number;
|
|
60
|
+
}): FsBridgeEntry;
|
|
61
|
+
//# sourceMappingURL=fs-bridge-protocol.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/__fs` bridge's wire shapes, declared once for the producing side.
|
|
3
|
+
*
|
|
4
|
+
* Every shape here is consumed by browser code in `packages/workbench`
|
|
5
|
+
* (`fs-bridge.ts`'s `FsEntry`, `wal-audit-transport.ts`'s SSE message parse,
|
|
6
|
+
* and through them `@dimina-kit/fs-core/sync/watch-expander`'s stat-diffing).
|
|
7
|
+
* That side cannot import from here — devtools is the application, the
|
|
8
|
+
* workbench is a library it embeds, and the dependency runs one way only. So
|
|
9
|
+
* this module does not give the two ends a shared declaration; it gives the
|
|
10
|
+
* PRODUCING end exactly one, instead of hand-building untyped payloads at each
|
|
11
|
+
* call site. The two ends stay pinned to each other by the round-trip
|
|
12
|
+
* assertions in workbench-coi-server.test.ts and by disk-sync.spec.ts.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The `type` field of every readdir/stat payload. These are wire values, not
|
|
16
|
+
* an internal enum: the browser side compares the raw numbers (watch-expander
|
|
17
|
+
* treats `2` as "recurse into it"), so renumbering them breaks the editor's
|
|
18
|
+
* disk mirror.
|
|
19
|
+
*/
|
|
20
|
+
export const FS_TYPE_FILE = 1;
|
|
21
|
+
export const FS_TYPE_DIR = 2;
|
|
22
|
+
/** Serialize one SSE message into an `event-stream` frame. */
|
|
23
|
+
export function fsWatchFrame(message) {
|
|
24
|
+
return `data: ${JSON.stringify(message)}\n\n`;
|
|
25
|
+
}
|
|
26
|
+
/** Project one `readdirWithin` entry onto the readdir wire shape. */
|
|
27
|
+
export function toFsBridgeEntry(entry) {
|
|
28
|
+
if (entry.isDirectory)
|
|
29
|
+
return [entry.name, FS_TYPE_DIR];
|
|
30
|
+
if (entry.size === undefined || entry.mtimeMs === undefined)
|
|
31
|
+
return [entry.name, FS_TYPE_FILE];
|
|
32
|
+
return [entry.name, FS_TYPE_FILE, entry.size, entry.mtimeMs];
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=fs-bridge-protocol.js.map
|
|
@@ -28,6 +28,13 @@ declare function shouldReportWatchPath(rel: string): boolean;
|
|
|
28
28
|
export declare const __testing: {
|
|
29
29
|
shouldReportWatchPath: typeof shouldReportWatchPath;
|
|
30
30
|
};
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Returns the call that ends this stream from the server side, or `null` when
|
|
33
|
+
* the request was answered with an error instead of a stream. Nothing else can
|
|
34
|
+
* end it: the client is the only party that ever disconnects, so the server
|
|
35
|
+
* that outlives the request needs a handle to close the channel when IT is the
|
|
36
|
+
* one going away.
|
|
37
|
+
*/
|
|
38
|
+
export declare function handleFsWatchRequest(req: http.IncomingMessage, res: http.ServerResponse, getProjectRoot: () => string): (() => void) | null;
|
|
32
39
|
export {};
|
|
33
40
|
//# sourceMappingURL=fs-watch-sse.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import nodeFs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { SKIP_DIRS } from '../ipc/project-fs.js';
|
|
4
|
+
import { fsWatchFrame } from './fs-bridge-protocol.js';
|
|
4
5
|
import { jsonRes } from './http-json.js';
|
|
5
6
|
/** Debounce window for batching change notifications (ms). */
|
|
6
7
|
const WATCH_DEBOUNCE_MS = 80;
|
|
@@ -17,11 +18,18 @@ function shouldReportWatchPath(rel) {
|
|
|
17
18
|
return !rel.split('/').some((segment) => SKIP_DIRS.has(segment));
|
|
18
19
|
}
|
|
19
20
|
export const __testing = { shouldReportWatchPath };
|
|
21
|
+
/**
|
|
22
|
+
* Returns the call that ends this stream from the server side, or `null` when
|
|
23
|
+
* the request was answered with an error instead of a stream. Nothing else can
|
|
24
|
+
* end it: the client is the only party that ever disconnects, so the server
|
|
25
|
+
* that outlives the request needs a handle to close the channel when IT is the
|
|
26
|
+
* one going away.
|
|
27
|
+
*/
|
|
20
28
|
export function handleFsWatchRequest(req, res, getProjectRoot) {
|
|
21
29
|
const watchedRoot = getProjectRoot();
|
|
22
30
|
if (!watchedRoot) {
|
|
23
31
|
jsonRes(res, 409, { error: 'No active project', code: 'ENOACTIVE' });
|
|
24
|
-
return;
|
|
32
|
+
return null;
|
|
25
33
|
}
|
|
26
34
|
let watcher;
|
|
27
35
|
try {
|
|
@@ -29,7 +37,7 @@ export function handleFsWatchRequest(req, res, getProjectRoot) {
|
|
|
29
37
|
}
|
|
30
38
|
catch (e) {
|
|
31
39
|
jsonRes(res, 500, { error: String(e) });
|
|
32
|
-
return;
|
|
40
|
+
return null;
|
|
33
41
|
}
|
|
34
42
|
res.writeHead(200, {
|
|
35
43
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
@@ -55,7 +63,7 @@ export function handleFsWatchRequest(req, res, getProjectRoot) {
|
|
|
55
63
|
return;
|
|
56
64
|
const paths = [...pending];
|
|
57
65
|
pending = new Set();
|
|
58
|
-
res.write(
|
|
66
|
+
res.write(fsWatchFrame({ paths }));
|
|
59
67
|
}
|
|
60
68
|
function onChange(_event, filename) {
|
|
61
69
|
// FSEvents overflow delivers a null filename ("something changed,
|
|
@@ -75,7 +83,7 @@ export function handleFsWatchRequest(req, res, getProjectRoot) {
|
|
|
75
83
|
watcher.on('error', () => {
|
|
76
84
|
if (closed)
|
|
77
85
|
return;
|
|
78
|
-
res.write(
|
|
86
|
+
res.write(fsWatchFrame({ watcherDead: true }));
|
|
79
87
|
cleanup();
|
|
80
88
|
res.end();
|
|
81
89
|
});
|
|
@@ -89,11 +97,17 @@ export function handleFsWatchRequest(req, res, getProjectRoot) {
|
|
|
89
97
|
if (closed)
|
|
90
98
|
return;
|
|
91
99
|
if (getProjectRoot() !== watchedRoot) {
|
|
92
|
-
res.write(
|
|
100
|
+
res.write(fsWatchFrame({ watcherDead: true }));
|
|
93
101
|
cleanup();
|
|
94
102
|
res.end();
|
|
95
103
|
}
|
|
96
104
|
}, WATCH_DEBOUNCE_MS);
|
|
97
105
|
req.on('close', cleanup);
|
|
106
|
+
return function endStream() {
|
|
107
|
+
if (closed)
|
|
108
|
+
return;
|
|
109
|
+
cleanup();
|
|
110
|
+
res.end();
|
|
111
|
+
};
|
|
98
112
|
}
|
|
99
113
|
//# sourceMappingURL=fs-watch-sse.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which project window a CDP target belongs to.
|
|
3
|
+
*
|
|
4
|
+
* MCP keeps one `simulator` and one `workbench` connection for the whole app,
|
|
5
|
+
* so every connection has to record the window it ACTUALLY reached: one
|
|
6
|
+
* recorded as arrived is left alone, one recorded elsewhere keeps retrying
|
|
7
|
+
* until it lands on the window the user is in.
|
|
8
|
+
*/
|
|
9
|
+
/** The per-window facts that decide which window a target belongs to. */
|
|
10
|
+
export interface TargetIdentityFacts {
|
|
11
|
+
activeBridgeId: string | null;
|
|
12
|
+
/**
|
|
13
|
+
* The resolved project path this window opened with. Fixed for the life of
|
|
14
|
+
* the window: a window names its project from the moment it exists, so
|
|
15
|
+
* attribution never has to wait for a compile to record anything.
|
|
16
|
+
*/
|
|
17
|
+
projectPath: string;
|
|
18
|
+
getAppId: () => string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface TargetBinding {
|
|
21
|
+
owner: object | null;
|
|
22
|
+
onTarget: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** The value of query `key` in a target URL, or null when it carries none. */
|
|
25
|
+
export declare function targetQuery(url: string | undefined, key: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* The window a chosen target belongs to, and whether that is the window MCP
|
|
28
|
+
* means to drive right now.
|
|
29
|
+
*
|
|
30
|
+
* A simulator target that names no window it can be attributed to is treated as
|
|
31
|
+
* the wrong window on purpose: it may well be another project's, and keeping the
|
|
32
|
+
* retries running is what moves the connection onto the active window's render
|
|
33
|
+
* guest as soon as that guest exists. The one target that legitimately names no
|
|
34
|
+
* window is the project list, which is the right workbench surface exactly
|
|
35
|
+
* while no project window is open.
|
|
36
|
+
*/
|
|
37
|
+
export declare function connectionOwner(kind: 'simulator' | 'workbench', url: string | undefined, intended: object | null, windows: ReadonlyMap<object, TargetIdentityFacts>): TargetBinding;
|
|
38
|
+
//# sourceMappingURL=connection-owner.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which project window a CDP target belongs to.
|
|
3
|
+
*
|
|
4
|
+
* MCP keeps one `simulator` and one `workbench` connection for the whole app,
|
|
5
|
+
* so every connection has to record the window it ACTUALLY reached: one
|
|
6
|
+
* recorded as arrived is left alone, one recorded elsewhere keeps retrying
|
|
7
|
+
* until it lands on the window the user is in.
|
|
8
|
+
*/
|
|
9
|
+
/** The value of query `key` in a target URL, or null when it carries none. */
|
|
10
|
+
export function targetQuery(url, key) {
|
|
11
|
+
if (!url)
|
|
12
|
+
return null;
|
|
13
|
+
try {
|
|
14
|
+
return new URL(url).searchParams.get(key);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function bind(owner, intended) {
|
|
21
|
+
return { owner, onTarget: owner === intended };
|
|
22
|
+
}
|
|
23
|
+
/** The window whose workbench renderer has this target's project open. */
|
|
24
|
+
function workbenchOwner(url, windows) {
|
|
25
|
+
const path = targetQuery(url, 'path');
|
|
26
|
+
if (!path)
|
|
27
|
+
return null;
|
|
28
|
+
for (const [candidate, facts] of windows) {
|
|
29
|
+
if (facts.projectPath === path)
|
|
30
|
+
return candidate;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The window this simulator surface belongs to: a render guest names it through
|
|
36
|
+
* its `bridgeId`, the localhost:7788 shell through the `appId` its route
|
|
37
|
+
* carries. Two windows answering to one appId — what two projects declaring the
|
|
38
|
+
* same appId produce — name no single window, so neither is returned.
|
|
39
|
+
*/
|
|
40
|
+
function simulatorOwner(url, windows) {
|
|
41
|
+
const bridgeId = targetQuery(url, 'bridgeId');
|
|
42
|
+
if (bridgeId !== null) {
|
|
43
|
+
for (const [candidate, facts] of windows) {
|
|
44
|
+
if (facts.activeBridgeId === bridgeId)
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const appId = targetQuery(url, 'appId');
|
|
50
|
+
if (appId === null)
|
|
51
|
+
return null;
|
|
52
|
+
let shell = null;
|
|
53
|
+
for (const [candidate, facts] of windows) {
|
|
54
|
+
if (facts.getAppId() !== appId)
|
|
55
|
+
continue;
|
|
56
|
+
if (shell)
|
|
57
|
+
return null;
|
|
58
|
+
shell = candidate;
|
|
59
|
+
}
|
|
60
|
+
return shell;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The window a chosen target belongs to, and whether that is the window MCP
|
|
64
|
+
* means to drive right now.
|
|
65
|
+
*
|
|
66
|
+
* A simulator target that names no window it can be attributed to is treated as
|
|
67
|
+
* the wrong window on purpose: it may well be another project's, and keeping the
|
|
68
|
+
* retries running is what moves the connection onto the active window's render
|
|
69
|
+
* guest as soon as that guest exists. The one target that legitimately names no
|
|
70
|
+
* window is the project list, which is the right workbench surface exactly
|
|
71
|
+
* while no project window is open.
|
|
72
|
+
*/
|
|
73
|
+
export function connectionOwner(kind, url, intended, windows) {
|
|
74
|
+
const owner = kind === 'workbench' ? workbenchOwner(url, windows) : simulatorOwner(url, windows);
|
|
75
|
+
if (owner)
|
|
76
|
+
return bind(owner, intended);
|
|
77
|
+
return { owner: null, onTarget: kind === 'workbench' && intended === null };
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=connection-owner.js.map
|