@dimina-kit/devtools 0.4.0-dev.20260828161241 → 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/render-host/pageFrame.html +1 -1
- package/dist/render-host/preload.cjs +16 -4
- 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
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared electron/fs/`@dimina-kit/devkit` mock core for
|
|
3
|
+
* `window-close-reveal.harness.ts` (app/) and
|
|
4
|
+
* `devtools-backend-before-quit.harness.ts` (runtime/) — both those harnesses
|
|
5
|
+
* drive a REAL runtime (`createDevtoolsRuntime` or `createDevtoolsBackend`)
|
|
6
|
+
* against the same mock set (lifted from `quit-flag-onclose.test.ts`) and
|
|
7
|
+
* previously duplicated it in full; this module holds the part that's
|
|
8
|
+
* actually identical between them.
|
|
9
|
+
*
|
|
10
|
+
* `BrowserWindow` here tracks visible/minimized state and implements
|
|
11
|
+
* `hide`/`isVisible`/`minimize`/`isMinimized`/`restore` on top of `show`/
|
|
12
|
+
* `showInactive` — the window-close-reveal guards need all of that, while the
|
|
13
|
+
* before-quit/shutdown-race tests only ever `destroy()` a window and never
|
|
14
|
+
* observe the extra state, so carrying it there is a no-op.
|
|
15
|
+
*
|
|
16
|
+
* `vi.mock(...)` calls here run once per importing test file's own isolated
|
|
17
|
+
* module graph (vitest scopes mocking per test file). A harness that imports
|
|
18
|
+
* this module chains into that same module graph, so importing THIS module
|
|
19
|
+
* (directly, or transitively through one of the two harnesses above) has the
|
|
20
|
+
* same effect as if each `vi.mock` call were written directly in the test
|
|
21
|
+
* file itself.
|
|
22
|
+
*/
|
|
23
|
+
import { vi } from 'vitest';
|
|
24
|
+
// ── Hoisted stub state ──────────────────────────────────────────────────
|
|
25
|
+
const stubs = vi.hoisted(() => {
|
|
26
|
+
const handlers = new Map();
|
|
27
|
+
const projectsJsonPath = '/tmp/dimina-test-userdata/dimina-projects.json';
|
|
28
|
+
let projectsJsonContent = null;
|
|
29
|
+
const projectsWithAppJson = new Set();
|
|
30
|
+
function makeEmitter() {
|
|
31
|
+
const listeners = {};
|
|
32
|
+
return {
|
|
33
|
+
listeners,
|
|
34
|
+
on(event, fn) {
|
|
35
|
+
;
|
|
36
|
+
(listeners[event] ??= new Set()).add(fn);
|
|
37
|
+
return this;
|
|
38
|
+
},
|
|
39
|
+
once(event, fn) {
|
|
40
|
+
const wrap = (...args) => {
|
|
41
|
+
listeners[event]?.delete(wrap);
|
|
42
|
+
return fn(...args);
|
|
43
|
+
};
|
|
44
|
+
(listeners[event] ??= new Set()).add(wrap);
|
|
45
|
+
return this;
|
|
46
|
+
},
|
|
47
|
+
off(event, fn) {
|
|
48
|
+
listeners[event]?.delete(fn);
|
|
49
|
+
return this;
|
|
50
|
+
},
|
|
51
|
+
removeListener(event, fn) {
|
|
52
|
+
listeners[event]?.delete(fn);
|
|
53
|
+
return this;
|
|
54
|
+
},
|
|
55
|
+
emit(event, ...args) {
|
|
56
|
+
for (const fn of [...(listeners[event] ?? [])])
|
|
57
|
+
fn(...args);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function reset() {
|
|
62
|
+
handlers.clear();
|
|
63
|
+
projectsJsonContent = null;
|
|
64
|
+
projectsWithAppJson.clear();
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
handlers,
|
|
68
|
+
projectsJsonPath,
|
|
69
|
+
getProjectsJson() {
|
|
70
|
+
return projectsJsonContent;
|
|
71
|
+
},
|
|
72
|
+
setProjectsJson(v) {
|
|
73
|
+
projectsJsonContent = v;
|
|
74
|
+
},
|
|
75
|
+
projectsWithAppJson,
|
|
76
|
+
makeEmitter,
|
|
77
|
+
reset,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
// ── electron stub ────────────────────────────────────────────────────────
|
|
81
|
+
vi.mock('electron', () => {
|
|
82
|
+
const ipcEmitter = stubs.makeEmitter();
|
|
83
|
+
const ipcMain = {
|
|
84
|
+
...ipcEmitter,
|
|
85
|
+
handle: vi.fn((channel, fn) => {
|
|
86
|
+
stubs.handlers.set(channel, fn);
|
|
87
|
+
}),
|
|
88
|
+
removeHandler: vi.fn((channel) => {
|
|
89
|
+
stubs.handlers.delete(channel);
|
|
90
|
+
}),
|
|
91
|
+
on: vi.fn((event, fn) => ipcEmitter.on(event, fn)),
|
|
92
|
+
removeListener: vi.fn((event, fn) => ipcEmitter.removeListener(event, fn)),
|
|
93
|
+
};
|
|
94
|
+
const appEmitter = stubs.makeEmitter();
|
|
95
|
+
const app = {
|
|
96
|
+
...appEmitter,
|
|
97
|
+
isPackaged: true,
|
|
98
|
+
whenReady: vi.fn(() => Promise.resolve()),
|
|
99
|
+
getPath: vi.fn(() => '/tmp/dimina-test-userdata'),
|
|
100
|
+
quit: vi.fn(),
|
|
101
|
+
setName: vi.fn(),
|
|
102
|
+
commandLine: {
|
|
103
|
+
getSwitchValue: vi.fn(() => ''),
|
|
104
|
+
appendSwitch: vi.fn(),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
// `WebContents` and `BrowserWindow` below both wrap a `stubs.makeEmitter()`
|
|
108
|
+
// and need the SAME on/once/off/removeListener/emit delegation (the
|
|
109
|
+
// emitter's own methods use `this` internally, so a plain property copy
|
|
110
|
+
// would call them with the wrong receiver — each needs `.bind(em)`). Shared
|
|
111
|
+
// base class instead of re-declaring the same 5 bound fields in both.
|
|
112
|
+
class EmittingStub {
|
|
113
|
+
em = stubs.makeEmitter();
|
|
114
|
+
on = this.em.on.bind(this.em);
|
|
115
|
+
once = this.em.once.bind(this.em);
|
|
116
|
+
off = this.em.off.bind(this.em);
|
|
117
|
+
removeListener = this.em.removeListener.bind(this.em);
|
|
118
|
+
emit = this.em.emit.bind(this.em);
|
|
119
|
+
}
|
|
120
|
+
class WebContents extends EmittingStub {
|
|
121
|
+
destroyed = false;
|
|
122
|
+
id = Math.floor(Math.random() * 1e6);
|
|
123
|
+
send = vi.fn();
|
|
124
|
+
isDestroyed = () => this.destroyed;
|
|
125
|
+
openDevTools = vi.fn();
|
|
126
|
+
closeDevTools = vi.fn();
|
|
127
|
+
setDevToolsWebContents = vi.fn();
|
|
128
|
+
setWindowOpenHandler = vi.fn();
|
|
129
|
+
loadFile = vi.fn(() => Promise.resolve());
|
|
130
|
+
loadURL = vi.fn(() => Promise.resolve());
|
|
131
|
+
executeJavaScript = vi.fn(() => Promise.resolve(undefined));
|
|
132
|
+
reload = vi.fn();
|
|
133
|
+
getType = () => 'window';
|
|
134
|
+
getURL = () => '';
|
|
135
|
+
debugger = {
|
|
136
|
+
attach: vi.fn(),
|
|
137
|
+
detach: vi.fn(),
|
|
138
|
+
isAttached: () => false,
|
|
139
|
+
on: vi.fn(),
|
|
140
|
+
removeListener: vi.fn(),
|
|
141
|
+
sendCommand: vi.fn(() => Promise.resolve({ entries: [] })),
|
|
142
|
+
};
|
|
143
|
+
close = vi.fn(() => {
|
|
144
|
+
this.destroyed = true;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
class WebContentsView {
|
|
148
|
+
webContents = new WebContents();
|
|
149
|
+
setBounds = vi.fn();
|
|
150
|
+
setBackgroundColor = vi.fn();
|
|
151
|
+
}
|
|
152
|
+
class View {
|
|
153
|
+
children = [];
|
|
154
|
+
addChildView(child) {
|
|
155
|
+
this.children.push(child);
|
|
156
|
+
}
|
|
157
|
+
removeChildView(child) {
|
|
158
|
+
const i = this.children.indexOf(child);
|
|
159
|
+
if (i >= 0)
|
|
160
|
+
this.children.splice(i, 1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
class BrowserWindow extends EmittingStub {
|
|
164
|
+
destroyed = false;
|
|
165
|
+
// The list window and every workbench window start shown/restored — the
|
|
166
|
+
// reveal/hide guards this state exists for only ever move a window AWAY
|
|
167
|
+
// from it, never establish it.
|
|
168
|
+
visible = true;
|
|
169
|
+
minimized = false;
|
|
170
|
+
webContents = new WebContents();
|
|
171
|
+
contentView = new WebContentsView();
|
|
172
|
+
isDestroyed = () => this.destroyed;
|
|
173
|
+
isVisible = () => this.visible && !this.destroyed;
|
|
174
|
+
isMinimized = () => this.minimized;
|
|
175
|
+
getContentSize = () => [1280, 980];
|
|
176
|
+
setIcon = vi.fn();
|
|
177
|
+
setTitle = vi.fn();
|
|
178
|
+
show = vi.fn(() => {
|
|
179
|
+
this.visible = true;
|
|
180
|
+
});
|
|
181
|
+
showInactive = vi.fn(() => {
|
|
182
|
+
this.visible = true;
|
|
183
|
+
});
|
|
184
|
+
hide = vi.fn(() => {
|
|
185
|
+
this.visible = false;
|
|
186
|
+
});
|
|
187
|
+
minimize = vi.fn(() => {
|
|
188
|
+
this.minimized = true;
|
|
189
|
+
});
|
|
190
|
+
restore = vi.fn(() => {
|
|
191
|
+
this.minimized = false;
|
|
192
|
+
this.visible = true;
|
|
193
|
+
});
|
|
194
|
+
focus = vi.fn();
|
|
195
|
+
close = vi.fn();
|
|
196
|
+
destroy = vi.fn(() => {
|
|
197
|
+
this.destroyed = true;
|
|
198
|
+
});
|
|
199
|
+
loadFile = vi.fn(() => Promise.resolve());
|
|
200
|
+
loadURL = vi.fn(() => Promise.resolve());
|
|
201
|
+
static getAllWindows = vi.fn(() => []);
|
|
202
|
+
}
|
|
203
|
+
const sessionStub = {
|
|
204
|
+
fromPartition: vi.fn(() => ({
|
|
205
|
+
webRequest: {
|
|
206
|
+
onBeforeSendHeaders: vi.fn(),
|
|
207
|
+
onHeadersReceived: vi.fn(),
|
|
208
|
+
},
|
|
209
|
+
registerPreloadScript: vi.fn(),
|
|
210
|
+
protocol: { handle: vi.fn(), unhandle: vi.fn() },
|
|
211
|
+
})),
|
|
212
|
+
defaultSession: {
|
|
213
|
+
protocol: { handle: vi.fn(), unhandle: vi.fn() },
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
const dialog = {
|
|
217
|
+
showOpenDialog: vi.fn(() => Promise.resolve({ canceled: true, filePaths: [] })),
|
|
218
|
+
showMessageBox: vi.fn(() => Promise.resolve({ response: 0 })),
|
|
219
|
+
};
|
|
220
|
+
const Menu = {
|
|
221
|
+
buildFromTemplate: vi.fn((tpl) => ({ template: tpl })),
|
|
222
|
+
setApplicationMenu: vi.fn(),
|
|
223
|
+
};
|
|
224
|
+
const shell = {
|
|
225
|
+
openExternal: vi.fn(() => Promise.resolve()),
|
|
226
|
+
openPath: vi.fn(() => Promise.resolve('')),
|
|
227
|
+
};
|
|
228
|
+
const nativeImage = {
|
|
229
|
+
createFromPath: vi.fn(() => ({ isEmpty: () => true })),
|
|
230
|
+
};
|
|
231
|
+
const nativeTheme = { ...stubs.makeEmitter(), themeSource: 'system' };
|
|
232
|
+
const globalShortcut = {
|
|
233
|
+
register: vi.fn(() => false),
|
|
234
|
+
unregister: vi.fn(),
|
|
235
|
+
unregisterAll: vi.fn(),
|
|
236
|
+
};
|
|
237
|
+
const webContentsStatic = {
|
|
238
|
+
fromId: vi.fn(() => null),
|
|
239
|
+
getAllWebContents: vi.fn(() => []),
|
|
240
|
+
};
|
|
241
|
+
const Tray = vi.fn();
|
|
242
|
+
return {
|
|
243
|
+
app,
|
|
244
|
+
ipcMain,
|
|
245
|
+
BrowserWindow,
|
|
246
|
+
WebContentsView,
|
|
247
|
+
BrowserView: WebContentsView,
|
|
248
|
+
View,
|
|
249
|
+
webContents: webContentsStatic,
|
|
250
|
+
session: sessionStub,
|
|
251
|
+
protocol: { registerSchemesAsPrivileged: vi.fn(), handle: vi.fn(), unhandle: vi.fn() },
|
|
252
|
+
dialog,
|
|
253
|
+
Menu,
|
|
254
|
+
shell,
|
|
255
|
+
nativeImage,
|
|
256
|
+
nativeTheme,
|
|
257
|
+
globalShortcut,
|
|
258
|
+
Tray,
|
|
259
|
+
default: {},
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
vi.mock('fs', async () => {
|
|
263
|
+
const real = await vi.importActual('fs');
|
|
264
|
+
function existsSync(p) {
|
|
265
|
+
const s = String(p);
|
|
266
|
+
if (s === stubs.projectsJsonPath)
|
|
267
|
+
return stubs.getProjectsJson() !== null;
|
|
268
|
+
if (s.endsWith('/app.json') || s.endsWith('\\app.json')) {
|
|
269
|
+
const dir = s.replace(/[\\/]app\.json$/, '');
|
|
270
|
+
return stubs.projectsWithAppJson.has(dir);
|
|
271
|
+
}
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
function readFileSync(p, opts) {
|
|
275
|
+
const s = String(p);
|
|
276
|
+
if (s === stubs.projectsJsonPath) {
|
|
277
|
+
const content = stubs.getProjectsJson();
|
|
278
|
+
if (content === null)
|
|
279
|
+
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
|
|
280
|
+
return content;
|
|
281
|
+
}
|
|
282
|
+
return real.readFileSync(p, opts);
|
|
283
|
+
}
|
|
284
|
+
function writeFileSync(p, data) {
|
|
285
|
+
const s = String(p);
|
|
286
|
+
if (s === stubs.projectsJsonPath) {
|
|
287
|
+
stubs.setProjectsJson(typeof data === 'string' ? data : Buffer.from(data).toString('utf8'));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const mocked = {
|
|
291
|
+
...real,
|
|
292
|
+
existsSync,
|
|
293
|
+
readFileSync,
|
|
294
|
+
writeFileSync,
|
|
295
|
+
mkdirSync: vi.fn(),
|
|
296
|
+
statSync: vi.fn(() => ({ isDirectory: () => true, isFile: () => false, size: 0, mtimeMs: 0 })),
|
|
297
|
+
watch: vi.fn(),
|
|
298
|
+
realpathSync: vi.fn((p) => p),
|
|
299
|
+
};
|
|
300
|
+
return { ...mocked, default: mocked };
|
|
301
|
+
});
|
|
302
|
+
const devkitStubs = vi.hoisted(() => ({
|
|
303
|
+
sessionClose: vi.fn(() => Promise.resolve()),
|
|
304
|
+
}));
|
|
305
|
+
vi.mock('@dimina-kit/devkit', () => ({
|
|
306
|
+
openProject: vi.fn(() => Promise.resolve({
|
|
307
|
+
port: 12345,
|
|
308
|
+
appInfo: { appId: 'fakeApp' },
|
|
309
|
+
close: devkitStubs.sessionClose,
|
|
310
|
+
})),
|
|
311
|
+
}));
|
|
312
|
+
export { stubs, devkitStubs };
|
|
313
|
+
//# sourceMappingURL=devtools-runtime-mock.harness.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { BrowserWindow } from 'electron';
|
|
2
|
+
import { type Disposable } from '@dimina-kit/electron-deck/main';
|
|
3
|
+
import type { ProjectsProvider, ProjectTemplate } from './projects/types.js';
|
|
4
|
+
import { type SimulatorApiRegistry } from './simulator/custom-apis.js';
|
|
5
|
+
/**
|
|
6
|
+
* The services that belong to the whole application rather than to one window.
|
|
7
|
+
*
|
|
8
|
+
* They are all registered or configured ONCE by the host (through
|
|
9
|
+
* `WorkbenchAppConfig` and the `WorkbenchAppInstance` registration methods),
|
|
10
|
+
* so every window context reads the same instance. Anything whose owner is a
|
|
11
|
+
* single window — views, connections, the CDP broker, the workspace — stays on
|
|
12
|
+
* `WorkbenchContext` instead.
|
|
13
|
+
*/
|
|
14
|
+
export interface AppServices {
|
|
15
|
+
/** Backing store for the project list. One list, shown by every window. */
|
|
16
|
+
projectsProvider: ProjectsProvider;
|
|
17
|
+
/** Built-in templates merged with the host's, per `builtinTemplates`. */
|
|
18
|
+
projectTemplates: ProjectTemplate[];
|
|
19
|
+
/**
|
|
20
|
+
* Reference-counted `webContents.id` → live registration count for
|
|
21
|
+
* host-owned BrowserWindows registered as trusted senders. A host window is
|
|
22
|
+
* registered against the app, not against whichever window happened to be
|
|
23
|
+
* open at the time, so the map is shared and every context's sender policy
|
|
24
|
+
* consults the same entries.
|
|
25
|
+
*/
|
|
26
|
+
trustedWindowSenderIds: Map<number, number>;
|
|
27
|
+
/**
|
|
28
|
+
* Simulator custom APIs registered by the host. The host registers each
|
|
29
|
+
* handler once (in `onSetup`), so the registry is app-wide: a window opened
|
|
30
|
+
* later must answer the same `wx.*` calls as the one that was open during
|
|
31
|
+
* registration.
|
|
32
|
+
*/
|
|
33
|
+
simulatorApis: SimulatorApiRegistry;
|
|
34
|
+
}
|
|
35
|
+
export interface CreateAppServicesOptions {
|
|
36
|
+
projectsProvider?: ProjectsProvider;
|
|
37
|
+
projectTemplates?: ProjectTemplate[];
|
|
38
|
+
builtinTemplates?: 'all' | 'none' | readonly string[];
|
|
39
|
+
}
|
|
40
|
+
export declare function createAppServices(opts?: CreateAppServicesOptions): AppServices;
|
|
41
|
+
/**
|
|
42
|
+
* Adds `win.webContents` to the trusted-sender set and returns a Disposable
|
|
43
|
+
* that removes it again.
|
|
44
|
+
*
|
|
45
|
+
* Trust is reference-counted: registering the SAME window N times keeps it
|
|
46
|
+
* trusted until every one of the N returned Disposables has been disposed.
|
|
47
|
+
* Each register bumps the count, each dispose decrements it, and the window is
|
|
48
|
+
* un-trusted only when the count reaches zero.
|
|
49
|
+
*
|
|
50
|
+
* The window's `closed` event short-circuits the ref-count: it deletes the
|
|
51
|
+
* map entry outright (the window is dead, so it must be un-trusted
|
|
52
|
+
* immediately regardless of how many Disposables are still outstanding).
|
|
53
|
+
* After `closed`, disposing any leftover Disposable is a safe no-op — the
|
|
54
|
+
* map entry is already gone, so the decrement finds `undefined` and bails
|
|
55
|
+
* without driving the count negative or resurrecting trust.
|
|
56
|
+
*
|
|
57
|
+
* Each returned Disposable is itself idempotent (`removed` flag), and its
|
|
58
|
+
* `closed` listener removes itself once fired so a long-lived app doesn't
|
|
59
|
+
* accumulate dead listeners on closed windows.
|
|
60
|
+
*/
|
|
61
|
+
export declare function registerTrustedWindow(services: Pick<AppServices, 'trustedWindowSenderIds'>, win: BrowserWindow): Disposable;
|
|
62
|
+
//# sourceMappingURL=app-services.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { toDisposable } from '@dimina-kit/electron-deck/main';
|
|
2
|
+
import { createLocalProjectsProvider } from './projects/local-provider.js';
|
|
3
|
+
import { resolveTemplates } from './projects/templates.js';
|
|
4
|
+
import { BUILTIN_TEMPLATES } from './projects/builtin-templates.js';
|
|
5
|
+
import { createSimulatorApiRegistry, } from './simulator/custom-apis.js';
|
|
6
|
+
export function createAppServices(opts = {}) {
|
|
7
|
+
return {
|
|
8
|
+
projectsProvider: opts.projectsProvider ?? createLocalProjectsProvider(),
|
|
9
|
+
projectTemplates: resolveTemplates(BUILTIN_TEMPLATES, opts.projectTemplates ?? [], opts.builtinTemplates ?? 'all'),
|
|
10
|
+
trustedWindowSenderIds: new Map(),
|
|
11
|
+
simulatorApis: createSimulatorApiRegistry(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Adds `win.webContents` to the trusted-sender set and returns a Disposable
|
|
16
|
+
* that removes it again.
|
|
17
|
+
*
|
|
18
|
+
* Trust is reference-counted: registering the SAME window N times keeps it
|
|
19
|
+
* trusted until every one of the N returned Disposables has been disposed.
|
|
20
|
+
* Each register bumps the count, each dispose decrements it, and the window is
|
|
21
|
+
* un-trusted only when the count reaches zero.
|
|
22
|
+
*
|
|
23
|
+
* The window's `closed` event short-circuits the ref-count: it deletes the
|
|
24
|
+
* map entry outright (the window is dead, so it must be un-trusted
|
|
25
|
+
* immediately regardless of how many Disposables are still outstanding).
|
|
26
|
+
* After `closed`, disposing any leftover Disposable is a safe no-op — the
|
|
27
|
+
* map entry is already gone, so the decrement finds `undefined` and bails
|
|
28
|
+
* without driving the count negative or resurrecting trust.
|
|
29
|
+
*
|
|
30
|
+
* Each returned Disposable is itself idempotent (`removed` flag), and its
|
|
31
|
+
* `closed` listener removes itself once fired so a long-lived app doesn't
|
|
32
|
+
* accumulate dead listeners on closed windows.
|
|
33
|
+
*/
|
|
34
|
+
export function registerTrustedWindow(services, win) {
|
|
35
|
+
const senderId = win.webContents.id;
|
|
36
|
+
const counts = services.trustedWindowSenderIds;
|
|
37
|
+
counts.set(senderId, (counts.get(senderId) ?? 0) + 1);
|
|
38
|
+
let removed = false;
|
|
39
|
+
const onClosed = () => {
|
|
40
|
+
// The window is gone: zero the ref-count for this sender id outright,
|
|
41
|
+
// regardless of any other outstanding registrations for the same window.
|
|
42
|
+
counts.delete(senderId);
|
|
43
|
+
win.removeListener('closed', onClosed);
|
|
44
|
+
};
|
|
45
|
+
function remove() {
|
|
46
|
+
if (removed)
|
|
47
|
+
return;
|
|
48
|
+
removed = true;
|
|
49
|
+
win.removeListener('closed', onClosed);
|
|
50
|
+
const count = counts.get(senderId);
|
|
51
|
+
// `undefined` → the entry was already cleared (e.g. by `closed` or by a
|
|
52
|
+
// prior sibling's decrement that hit zero). Nothing to do — never go
|
|
53
|
+
// negative, never resurrect trust.
|
|
54
|
+
if (count === undefined)
|
|
55
|
+
return;
|
|
56
|
+
if (count <= 1)
|
|
57
|
+
counts.delete(senderId);
|
|
58
|
+
else
|
|
59
|
+
counts.set(senderId, count - 1);
|
|
60
|
+
}
|
|
61
|
+
win.once('closed', onClosed);
|
|
62
|
+
return toDisposable(remove);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=app-services.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { WindowService } from '../window-service.js';
|
|
2
|
+
import type { WorkspaceService } from '../workspace/workspace-service.js';
|
|
3
|
+
/** The slice of a window context that decides where a connection's commands go. */
|
|
4
|
+
export interface TargetContext {
|
|
5
|
+
windows: Pick<WindowService, 'mainWindow'>;
|
|
6
|
+
workspace: Pick<WorkspaceService, 'hasActiveSession'>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The project window one automation connection acts on.
|
|
10
|
+
*
|
|
11
|
+
* The active context follows window focus, so resolving it afresh per message
|
|
12
|
+
* would let a click on another project's window redirect a script that is
|
|
13
|
+
* already driving one — the script does nothing wrong and its commands land
|
|
14
|
+
* somewhere else. A connection therefore PINS the first context it reaches
|
|
15
|
+
* that has a session and keeps it for the rest of its life. Which window a
|
|
16
|
+
* connection drives is decided by what it reached first, never by what the
|
|
17
|
+
* user is looking at.
|
|
18
|
+
*
|
|
19
|
+
* Nothing is pinned before a project window exists: a client may connect at
|
|
20
|
+
* boot and open a project afterwards, so until a context with a session shows
|
|
21
|
+
* up every use resolves the active one (the project list, which drives
|
|
22
|
+
* nothing). Pinning the list window would tie the connection to a window that
|
|
23
|
+
* can never run a command.
|
|
24
|
+
*/
|
|
25
|
+
export interface ConnectionTarget<T extends TargetContext> {
|
|
26
|
+
/**
|
|
27
|
+
* The context to run a command against. Throws once the pinned window is
|
|
28
|
+
* gone, so a connection whose project was closed fails loudly instead of
|
|
29
|
+
* sliding onto whichever project happens to be open now.
|
|
30
|
+
*/
|
|
31
|
+
resolve(): T;
|
|
32
|
+
/** The same target, but null instead of a throw once its window is gone. */
|
|
33
|
+
peek(): T | null;
|
|
34
|
+
}
|
|
35
|
+
export declare function createConnectionTarget<T extends TargetContext>(getCtx: () => T): ConnectionTarget<T>;
|
|
36
|
+
//# sourceMappingURL=connection-target.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function createConnectionTarget(getCtx) {
|
|
2
|
+
let pinned = null;
|
|
3
|
+
// The window, not the session, is what a connection is pinned to: closing
|
|
4
|
+
// the project inside a window (`Tool.close`) leaves that window able to open
|
|
5
|
+
// another, while destroying the window ends the connection's target for good.
|
|
6
|
+
const isGone = (ctx) => {
|
|
7
|
+
const win = ctx.windows.mainWindow;
|
|
8
|
+
return !win || win.isDestroyed();
|
|
9
|
+
};
|
|
10
|
+
const current = () => {
|
|
11
|
+
if (pinned)
|
|
12
|
+
return isGone(pinned) ? null : pinned;
|
|
13
|
+
const ctx = getCtx();
|
|
14
|
+
if (ctx.workspace.hasActiveSession())
|
|
15
|
+
pinned = ctx;
|
|
16
|
+
return ctx;
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
peek: current,
|
|
20
|
+
resolve() {
|
|
21
|
+
const ctx = current();
|
|
22
|
+
if (!ctx) {
|
|
23
|
+
throw new Error('The project window this connection was driving has been closed. Reconnect to drive another project.');
|
|
24
|
+
}
|
|
25
|
+
return ctx;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=connection-target.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { WebContents } from 'electron';
|
|
2
|
+
import type { ConnectionRegistry } from '@dimina-kit/electron-deck/main';
|
|
3
|
+
import type { ConsoleForwarder } from '../console-forward/index.js';
|
|
4
|
+
import type { RpcEvent } from './shared.js';
|
|
5
|
+
/** The slice of a window context the two console taps read. */
|
|
6
|
+
export interface ConsoleTargetContext {
|
|
7
|
+
consoleForwarder?: ConsoleForwarder;
|
|
8
|
+
connections: ConnectionRegistry;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The `App.logAdded` stream of ONE automation connection, taken from the same
|
|
12
|
+
* window that connection's commands reach.
|
|
13
|
+
*
|
|
14
|
+
* Two taps, because the two architectures deliver guest console differently:
|
|
15
|
+
* - native-host: the render/service preloads post to main, bridge-router
|
|
16
|
+
* routes into the window's always-on ConsoleForwarder, and we subscribe.
|
|
17
|
+
* - default dimina-fe: the page/service console flows through the simulator
|
|
18
|
+
* guest's `ipc-message-host` channel, so we listen on that webContents.
|
|
19
|
+
* Both are re-pointed by `sync()`, which is what keeps the log stream and the
|
|
20
|
+
* command stream on the same window as the target and its simulator change.
|
|
21
|
+
*/
|
|
22
|
+
export interface ConsoleBridge {
|
|
23
|
+
/** Re-point both taps at the connection's current target. */
|
|
24
|
+
sync(): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function createConsoleBridge<T extends ConsoleTargetContext>(target: () => T | null, simulatorOf: (ctx: T) => WebContents | null, emit: (event: RpcEvent) => void): ConsoleBridge;
|
|
28
|
+
//# sourceMappingURL=console-bridge.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { SimulatorChannel } from '../../../shared/ipc-channels.js';
|
|
2
|
+
/**
|
|
3
|
+
* How often an idle connection re-checks its target for a console to attach
|
|
4
|
+
* to. A connection that never sends a message (a script that only listens for
|
|
5
|
+
* logs) has no other moment to notice its window's simulator appearing or
|
|
6
|
+
* being replaced by a rebuild.
|
|
7
|
+
*/
|
|
8
|
+
const SYNC_INTERVAL_MS = 1000;
|
|
9
|
+
export function createConsoleBridge(target, simulatorOf, emit) {
|
|
10
|
+
let subscribedForwarder = null;
|
|
11
|
+
let forwarderSub;
|
|
12
|
+
let attachedSim = null;
|
|
13
|
+
let simHandler = null;
|
|
14
|
+
// The destroyed-teardown is owned by the sim's connection rather than a
|
|
15
|
+
// bespoke `sim.once('destroyed')`, so it cannot accumulate across re-attaches.
|
|
16
|
+
let simDestroyedDisposer = null;
|
|
17
|
+
function logAdded(entry) {
|
|
18
|
+
const e = entry ?? {};
|
|
19
|
+
emit({ method: 'App.logAdded', params: { type: e.level || 'log', args: e.args || [] } });
|
|
20
|
+
}
|
|
21
|
+
// Keyed on the forwarder itself, not the context: a window is wired with its
|
|
22
|
+
// forwarder after the context exists, so a context-identity check would miss
|
|
23
|
+
// the forwarder arriving on a context we already looked at.
|
|
24
|
+
function syncForwarder(ctx) {
|
|
25
|
+
const forwarder = ctx?.consoleForwarder ?? null;
|
|
26
|
+
if (forwarder === subscribedForwarder)
|
|
27
|
+
return;
|
|
28
|
+
forwarderSub?.dispose();
|
|
29
|
+
subscribedForwarder = forwarder;
|
|
30
|
+
forwarderSub = forwarder?.subscribe((entry) => logAdded(entry));
|
|
31
|
+
}
|
|
32
|
+
function detachSim() {
|
|
33
|
+
if (attachedSim && simHandler) {
|
|
34
|
+
try {
|
|
35
|
+
if (!attachedSim.isDestroyed()) {
|
|
36
|
+
;
|
|
37
|
+
attachedSim.removeListener('ipc-message-host', simHandler);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch { /* noop */ }
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
simDestroyedDisposer?.dispose();
|
|
44
|
+
}
|
|
45
|
+
catch { /* noop */ }
|
|
46
|
+
attachedSim = null;
|
|
47
|
+
simHandler = null;
|
|
48
|
+
simDestroyedDisposer = null;
|
|
49
|
+
}
|
|
50
|
+
function syncSim(ctx) {
|
|
51
|
+
const sim = ctx ? simulatorOf(ctx) : null;
|
|
52
|
+
const next = sim && !sim.isDestroyed() ? sim : null;
|
|
53
|
+
if (next === attachedSim)
|
|
54
|
+
return;
|
|
55
|
+
detachSim();
|
|
56
|
+
if (!next || !ctx)
|
|
57
|
+
return;
|
|
58
|
+
const onIpcMessageHost = (_event, channel, data) => {
|
|
59
|
+
if (channel !== SimulatorChannel.Console)
|
|
60
|
+
return;
|
|
61
|
+
logAdded(data);
|
|
62
|
+
};
|
|
63
|
+
attachedSim = next;
|
|
64
|
+
simHandler = onIpcMessageHost;
|
|
65
|
+
next.on('ipc-message-host', onIpcMessageHost);
|
|
66
|
+
simDestroyedDisposer = ctx.connections.acquire(next).own(() => {
|
|
67
|
+
// Dead webContents: drop the refs (removeListener on it would throw) and
|
|
68
|
+
// let the next sync attach to the replacement.
|
|
69
|
+
attachedSim = null;
|
|
70
|
+
simHandler = null;
|
|
71
|
+
simDestroyedDisposer = null;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function sync() {
|
|
75
|
+
const ctx = target();
|
|
76
|
+
syncForwarder(ctx);
|
|
77
|
+
syncSim(ctx);
|
|
78
|
+
}
|
|
79
|
+
const timer = setInterval(sync, SYNC_INTERVAL_MS);
|
|
80
|
+
return {
|
|
81
|
+
sync,
|
|
82
|
+
dispose() {
|
|
83
|
+
clearInterval(timer);
|
|
84
|
+
detachSim();
|
|
85
|
+
forwarderSub?.dispose();
|
|
86
|
+
forwarderSub = undefined;
|
|
87
|
+
subscribedForwarder = null;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=console-bridge.js.map
|
|
@@ -11,10 +11,10 @@ async function readRoute(ctx) {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Native-host: derive the visible page's `{ pagePath, query }` from the active
|
|
14
|
-
* render guest.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* render guest. This is the source for `App.getCurrentPage` and the fallback for
|
|
15
|
+
* `App.getPageStack` before DeviceShell reports its full ordered stack through
|
|
16
|
+
* the bridge. The render-host preload encodes `pagePath` (+ per-page query) in
|
|
17
|
+
* the guest's own `location.search`.
|
|
18
18
|
*/
|
|
19
19
|
async function readNativeActivePage(ctx) {
|
|
20
20
|
const renderWc = getActivePageWc(ctx);
|