@dimina-kit/devtools 0.3.2-dev.20260525152421 → 0.3.2-dev.20260610082053
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -40
- package/dist/main/api.d.ts +0 -1
- package/dist/main/api.js +5 -1
- package/dist/main/app/app.d.ts +15 -5
- package/dist/main/app/app.js +247 -110
- package/dist/main/app/bootstrap.d.ts +16 -0
- package/dist/main/app/bootstrap.js +20 -0
- package/dist/main/app/launch.d.ts +8 -4
- package/dist/main/app/launch.js +9 -4
- package/dist/main/index.bundle.js +6607 -2102
- package/dist/main/index.js +9 -1
- package/dist/main/ipc/app.d.ts +1 -1
- package/dist/main/ipc/bridge-router.d.ts +61 -0
- package/dist/main/ipc/bridge-router.js +1300 -0
- package/dist/main/ipc/panels.d.ts +1 -1
- package/dist/main/ipc/panels.js +6 -2
- package/dist/main/ipc/popover.d.ts +1 -1
- package/dist/main/ipc/project-fs.d.ts +57 -0
- package/dist/main/ipc/project-fs.js +448 -0
- package/dist/main/ipc/projects.d.ts +1 -1
- package/dist/main/ipc/session.d.ts +1 -1
- package/dist/main/ipc/settings.d.ts +1 -1
- package/dist/main/ipc/simulator-module.d.ts +6 -0
- package/dist/main/ipc/simulator-module.js +11 -1
- package/dist/main/ipc/simulator.d.ts +2 -2
- package/dist/main/ipc/simulator.js +49 -5
- package/dist/main/ipc/toolbar.d.ts +1 -1
- package/dist/main/ipc/views.d.ts +19 -0
- package/dist/main/ipc/views.js +65 -0
- package/dist/main/runtime/devtools-backend.d.ts +17 -0
- package/dist/main/runtime/devtools-backend.js +49 -0
- package/dist/main/runtime/miniapp-runtime.d.ts +39 -0
- package/dist/main/runtime/miniapp-runtime.js +17 -0
- package/dist/main/services/automation/exec.d.ts +27 -2
- package/dist/main/services/automation/exec.js +37 -3
- package/dist/main/services/automation/handlers/app.js +127 -11
- package/dist/main/services/automation/handlers/page.js +36 -19
- package/dist/main/services/automation/index.js +40 -13
- package/dist/main/services/automation/wait-active-page.d.ts +56 -0
- package/dist/main/services/automation/wait-active-page.js +71 -0
- package/dist/main/services/console-forward/index.d.ts +36 -0
- package/dist/main/services/console-forward/index.js +81 -0
- package/dist/main/services/dimina-resource-server.d.ts +7 -0
- package/dist/main/services/dimina-resource-server.js +99 -0
- package/dist/main/services/elements-forward/index.d.ts +66 -0
- package/dist/main/services/elements-forward/index.js +583 -0
- package/dist/main/services/layout/index.d.ts +23 -1
- package/dist/main/services/layout/index.js +28 -3
- package/dist/main/services/mcp/index.d.ts +1 -0
- package/dist/main/services/mcp/index.js +3 -0
- package/dist/main/services/mcp/server.d.ts +1 -1
- package/dist/main/services/mcp/server.js +1 -1
- package/dist/main/services/mcp/target-manager.d.ts +35 -0
- package/dist/main/services/mcp/target-manager.js +63 -1
- package/dist/main/services/mcp/tools/context-tools.js +15 -1
- package/dist/main/services/module.d.ts +1 -1
- package/dist/main/services/network-forward/index.d.ts +190 -0
- package/dist/main/services/network-forward/index.js +765 -0
- package/dist/main/services/notifications/renderer-notifier.d.ts +11 -0
- package/dist/main/services/notifications/renderer-notifier.js +7 -1
- package/dist/main/services/render-inspect/index.d.ts +41 -0
- package/dist/main/services/render-inspect/index.js +88 -0
- package/dist/main/services/safe-area/index.d.ts +17 -0
- package/dist/main/services/safe-area/index.js +59 -0
- package/dist/main/services/service-host-pool/pool.d.ts +197 -0
- package/dist/main/services/service-host-pool/pool.js +364 -0
- package/dist/main/services/simulator/custom-apis.d.ts +34 -0
- package/dist/main/services/simulator/custom-apis.js +22 -0
- package/dist/main/services/simulator-appdata/index.d.ts +35 -0
- package/dist/main/services/simulator-appdata/index.js +71 -0
- package/dist/main/services/simulator-current-page/index.d.ts +24 -0
- package/dist/main/services/simulator-current-page/index.js +19 -0
- package/dist/main/services/simulator-storage/index.d.ts +37 -2
- package/dist/main/services/simulator-storage/index.js +209 -32
- package/dist/main/services/simulator-storage/service-storage-ops.d.ts +40 -0
- package/dist/main/services/simulator-storage/service-storage-ops.js +67 -0
- package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
- package/dist/main/services/simulator-temp-files/index.d.ts +1 -1
- package/dist/main/services/simulator-temp-files/index.js +1 -1
- package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
- package/dist/main/services/simulator-wxml/index.d.ts +29 -0
- package/dist/main/services/simulator-wxml/index.js +28 -0
- package/dist/main/services/update/update-manager.d.ts +10 -0
- package/dist/main/services/update/update-manager.js +16 -3
- package/dist/main/services/views/devtools-tabs.d.ts +47 -0
- package/dist/main/services/views/devtools-tabs.js +95 -0
- package/dist/main/services/views/view-manager.d.ts +142 -4
- package/dist/main/services/views/view-manager.js +909 -17
- package/dist/main/services/workbench-context.d.ts +66 -1
- package/dist/main/services/workbench-context.js +6 -1
- package/dist/main/services/workspace/workspace-service.d.ts +8 -0
- package/dist/main/services/workspace/workspace-service.js +12 -0
- package/dist/main/utils/ipc-registry.d.ts +11 -2
- package/dist/main/utils/ipc-registry.js +63 -3
- package/dist/main/utils/paths.d.ts +15 -0
- package/dist/main/utils/paths.js +19 -0
- package/dist/main/utils/sender-policy.d.ts +9 -3
- package/dist/main/utils/sender-policy.js +18 -4
- package/dist/main/utils/theme.d.ts +1 -1
- package/dist/main/utils/theme.js +1 -1
- package/dist/main/windows/main-window/create.d.ts +0 -6
- package/dist/main/windows/main-window/create.js +11 -59
- package/dist/main/windows/main-window/events.d.ts +1 -1
- package/dist/main/windows/main-window/events.js +1 -1
- package/dist/main/windows/service-host-window/create.d.ts +52 -0
- package/dist/main/windows/service-host-window/create.js +112 -0
- package/dist/native-host/common/common.js +368 -0
- package/dist/native-host/container/pageFrame.css +1 -0
- package/dist/native-host/render/render.js +9334 -0
- package/dist/native-host/service/service.js +6 -0
- package/dist/preload/index.d.ts +2 -0
- package/dist/preload/index.js +6 -0
- package/dist/preload/instrumentation/app-data.d.ts +18 -20
- package/dist/preload/instrumentation/app-data.js +23 -160
- package/dist/preload/instrumentation/wxml-extract.d.ts +1 -1
- package/dist/preload/instrumentation/wxml-extract.js +1 -1
- package/dist/preload/runtime/bridge.d.ts +3 -8
- package/dist/preload/runtime/bridge.js +2 -32
- package/dist/preload/runtime/custom-apis.js +20 -17
- package/dist/preload/runtime/host-toolbar-advertiser.d.ts +27 -0
- package/dist/preload/runtime/host-toolbar-advertiser.js +55 -0
- package/dist/preload/runtime/main-api-runner.d.ts +9 -0
- package/dist/preload/runtime/main-api-runner.js +85 -0
- package/dist/preload/runtime/native-host.d.ts +43 -0
- package/dist/preload/runtime/native-host.js +83 -0
- package/dist/preload/shared/expose.d.ts +10 -5
- package/dist/preload/shared/expose.js +29 -11
- package/dist/preload/shared/sid-registry.d.ts +11 -0
- package/dist/preload/shared/sid-registry.js +38 -0
- package/dist/preload/windows/host-toolbar.cjs +161 -0
- package/dist/preload/windows/host-toolbar.cjs.map +7 -0
- package/dist/preload/windows/host-toolbar.d.ts +2 -0
- package/dist/preload/windows/host-toolbar.js +12 -0
- package/dist/preload/windows/main.cjs +1939 -0
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/simulator.cjs +925 -0
- package/dist/preload/windows/simulator.cjs.map +7 -0
- package/dist/preload/windows/simulator.js +243 -573
- package/dist/render-host/pageFrame.html +29 -0
- package/dist/render-host/preload.cjs +154 -0
- package/dist/render-host/render-inspect.js +295 -0
- package/dist/renderer/assets/abap-DiwvWnMr.js +2 -0
- package/dist/renderer/assets/apex-CmtZjKlf.js +2 -0
- package/dist/renderer/assets/azcli-DL2My_i-.js +2 -0
- package/dist/renderer/assets/bat-B-nC98wG.js +2 -0
- package/dist/renderer/assets/bicep-Ju5MwOgh.js +3 -0
- package/dist/renderer/assets/cameligo-8Eu1TyBr.js +2 -0
- package/dist/renderer/assets/chunk-DPI0Y_ll.js +1 -0
- package/dist/renderer/assets/clojure-u-RpMkH3.js +2 -0
- package/dist/renderer/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/renderer/assets/coffee-CdA7bbTe.js +2 -0
- package/dist/renderer/assets/constants-CLAQX6VZ.js +2 -0
- package/dist/renderer/assets/cpp-CzNFP8ks.js +2 -0
- package/dist/renderer/assets/csharp-j1LThmcE.js +2 -0
- package/dist/renderer/assets/csp-CLRC61y6.js +2 -0
- package/dist/renderer/assets/css-r6rC_7P2.js +4 -0
- package/dist/renderer/assets/css.worker-DyDu5ynT.js +90 -0
- package/dist/renderer/assets/cssMode-BCBgxrnA.js +2 -0
- package/dist/renderer/assets/cypher-CW08XVUh.js +2 -0
- package/dist/renderer/assets/dart-Cs9aL5T_.js +2 -0
- package/dist/renderer/assets/dockerfile-BWM0M184.js +2 -0
- package/dist/renderer/assets/ecl-MJJuer5P.js +2 -0
- package/dist/renderer/assets/editor-CWI9TnMM.css +1 -0
- package/dist/renderer/assets/editor.api2-BTaYmRq7.js +873 -0
- package/dist/renderer/assets/editor.worker-Dpt_xPrs.js +27 -0
- package/dist/renderer/assets/elixir-D2AIuXqn.js +2 -0
- package/dist/renderer/assets/flow9-B2H24giC.js +2 -0
- package/dist/renderer/assets/freemarker2-DGDGzimW.js +4 -0
- package/dist/renderer/assets/fsharp-CMk2OIJN.js +2 -0
- package/dist/renderer/assets/go-BrMkuJg0.js +2 -0
- package/dist/renderer/assets/graphql-PSR1UKGv.js +2 -0
- package/dist/renderer/assets/handlebars-bOHhw3EG.js +2 -0
- package/dist/renderer/assets/hcl-DAQrbDOW.js +2 -0
- package/dist/renderer/assets/html-BMyuX0_c.js +2 -0
- package/dist/renderer/assets/html.worker-IWNXhnIC.js +503 -0
- package/dist/renderer/assets/htmlMode-DR4kXJ7l.js +2 -0
- package/dist/renderer/assets/index-CcJKzatF.js +50 -0
- package/dist/renderer/assets/ini-0TG5BxW0.js +2 -0
- package/dist/renderer/assets/input-QGx6FFX1.js +2 -0
- package/dist/renderer/assets/ipc-transport-BT4QYQqu.css +1 -0
- package/dist/renderer/assets/{ipc-transport-BzPzxPwf.js → ipc-transport-DvZvwK-Z.js} +5 -5
- package/dist/renderer/assets/java-rgorz17v.js +2 -0
- package/dist/renderer/assets/javascript-Bu-tl1P7.js +2 -0
- package/dist/renderer/assets/json.worker-c4h5s80L.js +59 -0
- package/dist/renderer/assets/jsonMode-MQV_ffLR.js +8 -0
- package/dist/renderer/assets/julia-C8VMdHm8.js +2 -0
- package/dist/renderer/assets/kotlin-AhhC4Qhm.js +2 -0
- package/dist/renderer/assets/less-Cgca25AP.js +3 -0
- package/dist/renderer/assets/lexon-D0GHdBaw.js +2 -0
- package/dist/renderer/assets/liquid-ClkfOvhB.js +2 -0
- package/dist/renderer/assets/lspLanguageFeatures-Bke_4Ie9.js +5 -0
- package/dist/renderer/assets/lua-DmRsNG-P.js +2 -0
- package/dist/renderer/assets/m3-BgL5dNKT.js +2 -0
- package/dist/renderer/assets/markdown-BuJfycGS.js +2 -0
- package/dist/renderer/assets/mdx-RZdUskPu.js +2 -0
- package/dist/renderer/assets/mips-C9m_93PR.js +2 -0
- package/dist/renderer/assets/monaco.contribution-B7rUMaEv.js +3 -0
- package/dist/renderer/assets/msdax-CpFHC9OI.js +2 -0
- package/dist/renderer/assets/mysql-qFvltsqN.js +2 -0
- package/dist/renderer/assets/objective-c-Bnmr858J.js +2 -0
- package/dist/renderer/assets/pascal-WP0_D5AO.js +2 -0
- package/dist/renderer/assets/pascaligo-Blom4Rij.js +2 -0
- package/dist/renderer/assets/perl-B-vk8g64.js +2 -0
- package/dist/renderer/assets/pgsql-Cgvz6v67.js +2 -0
- package/dist/renderer/assets/php-8a3Lrw9m.js +2 -0
- package/dist/renderer/assets/pla-DuFqEZ8V.js +2 -0
- package/dist/renderer/assets/popover-BOH1If1J.js +2 -0
- package/dist/renderer/assets/postiats-DkLtSgkp.js +2 -0
- package/dist/renderer/assets/powerquery-BJ1aNepW.js +2 -0
- package/dist/renderer/assets/powershell-rE98k687.js +2 -0
- package/dist/renderer/assets/protobuf-CUheFacr.js +3 -0
- package/dist/renderer/assets/pug-LDcAMD8w.js +2 -0
- package/dist/renderer/assets/python-Cobrm_-8.js +2 -0
- package/dist/renderer/assets/qsharp-DUKSQoR1.js +2 -0
- package/dist/renderer/assets/r-D-QApv87.js +2 -0
- package/dist/renderer/assets/razor-BEUIQFNJ.js +2 -0
- package/dist/renderer/assets/redis-SXdDyWR9.js +2 -0
- package/dist/renderer/assets/redshift-Y6lsCryn.js +2 -0
- package/dist/renderer/assets/restructuredtext-edObr9a8.js +2 -0
- package/dist/renderer/assets/ruby-CNnUfF-8.js +2 -0
- package/dist/renderer/assets/rust-IHUZWzBr.js +2 -0
- package/dist/renderer/assets/sb-DrUvY44N.js +2 -0
- package/dist/renderer/assets/scala-B4hbXGLM.js +2 -0
- package/dist/renderer/assets/scheme-BGrd12j3.js +2 -0
- package/dist/renderer/assets/scss-x5G1ES4U.js +4 -0
- package/dist/renderer/assets/select-C7JQ8G5N.js +2 -0
- package/dist/renderer/assets/settings-DpAbTJyF.js +2 -0
- package/dist/renderer/assets/{settings-api-BXCw9vOZ.js → settings-api-RD2IZF53.js} +2 -2
- package/dist/renderer/assets/shell-DOehe2Y8.js +2 -0
- package/dist/renderer/assets/solidity-BeRvcwWV.js +2 -0
- package/dist/renderer/assets/sophia-DZbkUNjy.js +2 -0
- package/dist/renderer/assets/sparql-B7_oi5-h.js +2 -0
- package/dist/renderer/assets/sql-CTlsFWVE.js +2 -0
- package/dist/renderer/assets/st-DJVEJdPE.js +2 -0
- package/dist/renderer/assets/swift-CwhT3fYa.js +2 -0
- package/dist/renderer/assets/systemverilog-BQN63pkN.js +2 -0
- package/dist/renderer/assets/tcl-DqwfpskA.js +2 -0
- package/dist/renderer/assets/ts.worker-BwM5Ha3L.js +67720 -0
- package/dist/renderer/assets/tsMode-BHa7dl9v.js +12 -0
- package/dist/renderer/assets/twig-BiyenUgc.js +2 -0
- package/dist/renderer/assets/typescript-15aT0Yj8.js +2 -0
- package/dist/renderer/assets/typespec-CWOJribt.js +2 -0
- package/dist/renderer/assets/vb-Cq5F87m3.js +2 -0
- package/dist/renderer/assets/wgsl-BAvW2lVr.js +299 -0
- package/dist/renderer/assets/workbenchSettings-CNJX4Qna.js +8 -0
- package/dist/renderer/assets/workers-CRBS0W-v.js +2 -0
- package/dist/renderer/assets/xml-bR2Zh-HV.js +2 -0
- package/dist/renderer/assets/yaml-Bdk54Qrb.js +2 -0
- package/dist/renderer/entries/main/index.html +29 -6
- package/dist/renderer/entries/popover/index.html +7 -6
- package/dist/renderer/entries/settings/index.html +6 -5
- package/dist/renderer/entries/workbench-settings/index.html +6 -5
- package/dist/service-host/preload.cjs +254 -0
- package/dist/service-host/service.html +10 -0
- package/dist/service-host/sourcemap-rewrite.cjs +58 -0
- package/dist/service-host/sync-api-patch.js +20 -0
- package/dist/service-host/sync-impls/menu-button.js +16 -0
- package/dist/service-host/sync-impls/storage.js +50 -0
- package/dist/service-host/sync-impls/system-info.js +54 -0
- package/dist/shared/appdata-accumulator.d.ts +95 -0
- package/dist/shared/appdata-accumulator.js +213 -0
- package/dist/shared/bridge-channels.d.ts +364 -0
- package/dist/shared/bridge-channels.js +54 -0
- package/dist/shared/constants.d.ts +0 -4
- package/dist/shared/constants.js +0 -4
- package/dist/shared/ipc-channels.d.ts +108 -1
- package/dist/shared/ipc-channels.js +127 -10
- package/dist/shared/ipc-schemas.d.ts +65 -2
- package/dist/shared/ipc-schemas.js +79 -3
- package/dist/shared/open-in-editor.d.ts +71 -0
- package/dist/shared/open-in-editor.js +138 -0
- package/dist/shared/simulator-api-metadata.d.ts +21 -0
- package/dist/shared/simulator-api-metadata.js +23 -0
- package/dist/shared/types.d.ts +2 -2
- package/dist/shared/vpath.d.ts +42 -0
- package/dist/shared/vpath.js +105 -0
- package/dist/simulator/assets/device-shell-DsdcNEkt.css +1 -0
- package/dist/simulator/assets/device-shell-vwYSOVrh.js +2 -0
- package/dist/simulator/assets/jsx-runtime-P6ZegaOf.js +2 -0
- package/dist/simulator/assets/simulator-DddliUL5.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +2 -0
- package/dist/simulator/device-shell/menu-button-geometry.js +8 -0
- package/dist/simulator/simulator.html +2 -112
- package/package.json +9 -9
- package/dist/main/utils/disposable.d.ts +0 -18
- package/dist/main/utils/disposable.js +0 -67
- package/dist/renderer/assets/constants-DI382mTP.js +0 -2
- package/dist/renderer/assets/index-4lkyF644.js +0 -46
- package/dist/renderer/assets/input-D4byFOLS.js +0 -2
- package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +0 -1
- package/dist/renderer/assets/popover-cxKrg_os.js +0 -2
- package/dist/renderer/assets/select-BgSkkdHW.js +0 -2
- package/dist/renderer/assets/settings-oD2K1jdT.js +0 -2
- package/dist/renderer/assets/workbenchSettings-CTNBpTft.js +0 -8
- package/dist/simulator/assets/simulator-CVOFTpSj.js +0 -9
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { BrowserWindow } from 'electron';
|
|
2
|
+
/** Blank placeholder URL loaded while warming / after reset. */
|
|
3
|
+
const BLANK_URL = 'about:blank';
|
|
4
|
+
/** Hard ceiling on pool size regardless of requested size (doc §3.3). */
|
|
5
|
+
const HARD_MAX_POOL_SIZE = 4;
|
|
6
|
+
/** Storage buckets cleared on every release (doc §3.4 "必须" rows). */
|
|
7
|
+
const RESET_STORAGES = [
|
|
8
|
+
'cookies',
|
|
9
|
+
'localstorage',
|
|
10
|
+
'indexdb',
|
|
11
|
+
'serviceworkers',
|
|
12
|
+
'cachestorage',
|
|
13
|
+
];
|
|
14
|
+
export class ServiceHostPool {
|
|
15
|
+
entries = new Map();
|
|
16
|
+
currentSpec = null;
|
|
17
|
+
targetSize = 0;
|
|
18
|
+
maxPoolSize = 3;
|
|
19
|
+
nextId = 0;
|
|
20
|
+
disposed = false;
|
|
21
|
+
/**
|
|
22
|
+
* Warm `defaultPoolSize` entries with `defaultSpec`. Normally resolves once the
|
|
23
|
+
* pool has reached `defaultPoolSize` ready entries — even if a warming window
|
|
24
|
+
* crashes mid-init and is refilled. NOTE: `warmUpToTarget` caps its refill loop
|
|
25
|
+
* with a bounded guard, so under SUSTAINED warm crashes `init` resolves
|
|
26
|
+
* best-effort (possibly under-filled) rather than spinning forever — treat
|
|
27
|
+
* `getStats().ready` as advisory after init, not a hard invariant.
|
|
28
|
+
*/
|
|
29
|
+
async init(opts) {
|
|
30
|
+
if (this.disposed)
|
|
31
|
+
return;
|
|
32
|
+
this.currentSpec = opts.defaultSpec;
|
|
33
|
+
this.maxPoolSize = Math.min(opts.maxPoolSize ?? 3, HARD_MAX_POOL_SIZE);
|
|
34
|
+
this.targetSize = Math.min(Math.max(opts.defaultPoolSize, 0), this.maxPoolSize);
|
|
35
|
+
await this.warmUpToTarget();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Hand a ready window matching `spec` to the caller. If none is ready, fall
|
|
39
|
+
* back to synchronously constructing a fresh window — never blocking on warm
|
|
40
|
+
* (doc §3.1 "绝不阻塞 acquire"). Fallback windows carry `entryId === null` and
|
|
41
|
+
* are destroyed (not pooled) on release.
|
|
42
|
+
*
|
|
43
|
+
* A spec change (different `preloadPath`) tears down all *pooled* entries and
|
|
44
|
+
* re-targets the pool to the new spec; in-use entries are left to their owners
|
|
45
|
+
* and disposed when returned (see `release`).
|
|
46
|
+
*/
|
|
47
|
+
async acquire(spec) {
|
|
48
|
+
if (this.currentSpec && this.currentSpec.preloadPath !== spec.preloadPath) {
|
|
49
|
+
this.onSpecChange(spec);
|
|
50
|
+
}
|
|
51
|
+
else if (!this.currentSpec) {
|
|
52
|
+
this.currentSpec = spec;
|
|
53
|
+
}
|
|
54
|
+
for (const entry of this.entries.values()) {
|
|
55
|
+
if (entry.state === 'ready' && this.matches(entry.spec, spec)) {
|
|
56
|
+
// A ready entry whose window died (closed/crashed) while idle must never
|
|
57
|
+
// be handed out — navigating a destroyed WebContents silently no-ops the
|
|
58
|
+
// spawn. Drop it (and refill) and keep scanning.
|
|
59
|
+
if (entry.win.isDestroyed()) {
|
|
60
|
+
this.reclaim(entry, { destroyIfPooled: false, refill: false });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
entry.state = 'in-use';
|
|
64
|
+
return { win: entry.win, entryId: entry.id };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Pool miss → fallback create. Not pooled; entryId null.
|
|
68
|
+
const win = this.createWindow(spec);
|
|
69
|
+
// Kick the load but do not await — acquire must not block.
|
|
70
|
+
void this.loadBlank(win);
|
|
71
|
+
return { win, entryId: null };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Return a window. Fallback windows (`entryId === null`) and entries whose
|
|
75
|
+
* spec no longer matches the current pool spec are torn down. Otherwise the
|
|
76
|
+
* window is reset (navigated to blank, then storage cleared) and re-pooled as
|
|
77
|
+
* `ready`, unless that would exceed `maxPoolSize`.
|
|
78
|
+
*/
|
|
79
|
+
async release(entryId, win) {
|
|
80
|
+
if (entryId === null) {
|
|
81
|
+
this.destroyWindow(win);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const entry = this.entries.get(entryId);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
this.destroyWindow(win);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (this.disposed || !this.currentSpec || !this.matches(entry.spec, this.currentSpec)) {
|
|
90
|
+
this.disposeEntry(entry);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
entry.state = 'resetting';
|
|
94
|
+
await this.reset(entry);
|
|
95
|
+
// The entry may have been disposed/crashed/spec-changed/destroyed underneath
|
|
96
|
+
// the reset await; only re-pool if it is still the live, resetting entry with
|
|
97
|
+
// a live window. A destroyed window must never be re-marked `ready` (a later
|
|
98
|
+
// acquire would hand it out and the spawn would silently never boot).
|
|
99
|
+
if (this.disposed
|
|
100
|
+
|| this.entries.get(entryId) !== entry
|
|
101
|
+
|| entry.state !== 'resetting'
|
|
102
|
+
|| entry.win.isDestroyed()) {
|
|
103
|
+
this.destroyWindow(entry.win);
|
|
104
|
+
this.entries.delete(entryId);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Honor the hard cap: if re-pooling would exceed maxPoolSize, drop it.
|
|
108
|
+
const otherPooled = this.warmCount() - (this.isWarm(entry) ? 1 : 0);
|
|
109
|
+
if (otherPooled >= this.maxPoolSize) {
|
|
110
|
+
this.disposeEntry(entry);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
entry.state = 'ready';
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The owner of an acquired window reports it died externally (graceful close /
|
|
117
|
+
* crash) — reclaim its in-use slot so it isn't leaked in `entries` forever
|
|
118
|
+
* (bridge-router calls this on the `serviceAlreadyClosed` teardown path, where
|
|
119
|
+
* `release` is intentionally skipped). No-op for an unknown id; idempotent;
|
|
120
|
+
* never touches the (already-gone) window.
|
|
121
|
+
*/
|
|
122
|
+
releaseDestroyed(entryId) {
|
|
123
|
+
if (entryId === null)
|
|
124
|
+
return;
|
|
125
|
+
const entry = this.entries.get(entryId);
|
|
126
|
+
if (!entry)
|
|
127
|
+
return;
|
|
128
|
+
this.reclaim(entry, { destroyIfPooled: false, refill: false });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Re-target the pool. `target` is clamped to `[0, maxPoolSize]`. Pooled
|
|
132
|
+
* entries beyond the target are disposed OLDEST-FIRST (doc §3.3/§3.6); in-use
|
|
133
|
+
* entries are untouched. Does not warm new entries up (use `init` / refill).
|
|
134
|
+
*/
|
|
135
|
+
resize(target) {
|
|
136
|
+
this.targetSize = Math.min(Math.max(target, 0), this.maxPoolSize);
|
|
137
|
+
// Map iteration is insertion order, so `pooled` is oldest-first. Dispose the
|
|
138
|
+
// oldest prefix and keep the `targetSize` most-recently-created entries.
|
|
139
|
+
const pooled = [...this.entries.values()].filter((e) => this.isWarm(e));
|
|
140
|
+
const surplus = pooled.length - this.targetSize;
|
|
141
|
+
if (surplus <= 0)
|
|
142
|
+
return;
|
|
143
|
+
for (const entry of pooled.slice(0, surplus)) {
|
|
144
|
+
this.disposeEntry(entry);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Occupancy snapshot. */
|
|
148
|
+
getStats() {
|
|
149
|
+
let ready = 0;
|
|
150
|
+
let inUse = 0;
|
|
151
|
+
let warming = 0;
|
|
152
|
+
let resetting = 0;
|
|
153
|
+
for (const entry of this.entries.values()) {
|
|
154
|
+
switch (entry.state) {
|
|
155
|
+
case 'ready':
|
|
156
|
+
ready++;
|
|
157
|
+
break;
|
|
158
|
+
case 'in-use':
|
|
159
|
+
inUse++;
|
|
160
|
+
break;
|
|
161
|
+
case 'warming':
|
|
162
|
+
warming++;
|
|
163
|
+
break;
|
|
164
|
+
case 'resetting':
|
|
165
|
+
resetting++;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { total: this.entries.size, ready, inUse, warming, resetting, spec: this.currentSpec };
|
|
170
|
+
}
|
|
171
|
+
/** Tear down every entry and stop accepting work. */
|
|
172
|
+
async dispose() {
|
|
173
|
+
this.disposed = true;
|
|
174
|
+
this.targetSize = 0;
|
|
175
|
+
for (const entry of [...this.entries.values()]) {
|
|
176
|
+
this.disposeEntry(entry);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// ── internals ──────────────────────────────────────────────────────────
|
|
180
|
+
matches(a, b) {
|
|
181
|
+
return a.preloadPath === b.preloadPath;
|
|
182
|
+
}
|
|
183
|
+
/** ready + warming + resetting entries are "pooled" (owned by the pool). */
|
|
184
|
+
isWarm(entry) {
|
|
185
|
+
return entry.state === 'ready' || entry.state === 'warming' || entry.state === 'resetting';
|
|
186
|
+
}
|
|
187
|
+
warmCount() {
|
|
188
|
+
let n = 0;
|
|
189
|
+
for (const entry of this.entries.values()) {
|
|
190
|
+
if (this.isWarm(entry))
|
|
191
|
+
n++;
|
|
192
|
+
}
|
|
193
|
+
return n;
|
|
194
|
+
}
|
|
195
|
+
readyCount() {
|
|
196
|
+
let n = 0;
|
|
197
|
+
for (const entry of this.entries.values()) {
|
|
198
|
+
if (entry.state === 'ready')
|
|
199
|
+
n++;
|
|
200
|
+
}
|
|
201
|
+
return n;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Drive the pool to `targetSize` ready entries and resolve only then. Loops so
|
|
205
|
+
* a crash that drops a warming entry mid-init (which refills asynchronously
|
|
206
|
+
* via `onGone`) is awaited too, instead of resolving on a stale snapshot. A
|
|
207
|
+
* generous iteration guard prevents an unbounded spin if windows keep dying.
|
|
208
|
+
*/
|
|
209
|
+
async warmUpToTarget() {
|
|
210
|
+
if (!this.currentSpec)
|
|
211
|
+
return;
|
|
212
|
+
let guard = 0;
|
|
213
|
+
const maxIterations = this.targetSize * 8 + 8;
|
|
214
|
+
while (!this.disposed && this.readyCount() < this.targetSize && guard++ < maxIterations) {
|
|
215
|
+
while (this.warmCount() < this.targetSize) {
|
|
216
|
+
void this.warmOne(this.currentSpec);
|
|
217
|
+
}
|
|
218
|
+
const warming = [...this.entries.values()]
|
|
219
|
+
.filter((e) => e.state === 'warming')
|
|
220
|
+
.map((e) => e.ready);
|
|
221
|
+
if (warming.length === 0)
|
|
222
|
+
break;
|
|
223
|
+
await Promise.all(warming);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
createWindow(spec) {
|
|
227
|
+
return new BrowserWindow({
|
|
228
|
+
show: false,
|
|
229
|
+
width: spec.size?.width,
|
|
230
|
+
height: spec.size?.height,
|
|
231
|
+
webPreferences: {
|
|
232
|
+
preload: spec.preloadPath,
|
|
233
|
+
partition: spec.partition,
|
|
234
|
+
// Mirror createServiceHostWindow's process model (create.ts:24-30): the
|
|
235
|
+
// service-host preload does `require('electron')` and writes globals
|
|
236
|
+
// onto the page realm, so it requires nodeIntegration:false,
|
|
237
|
+
// contextIsolation:false, sandbox:false. Default to that contract; let a
|
|
238
|
+
// caller override via the spec for a different runtime.
|
|
239
|
+
nodeIntegration: spec.nodeIntegration ?? false,
|
|
240
|
+
contextIsolation: spec.contextIsolation ?? false,
|
|
241
|
+
sandbox: spec.sandbox ?? false,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
loadBlank(win) {
|
|
246
|
+
// `loadURL` resolves on did-finish-load in Electron; treat any failure as a
|
|
247
|
+
// settled load so a single bad warm never wedges the pool. The synchronous
|
|
248
|
+
// call ITSELF throws on an already-destroyed webContents (it isn't a
|
|
249
|
+
// rejection) — swallow that too, so reset/release of a window that died
|
|
250
|
+
// mid-flight resolves instead of rejecting (the "never wedges" contract).
|
|
251
|
+
try {
|
|
252
|
+
return Promise.resolve(win.webContents.loadURL(BLANK_URL)).then(() => undefined, () => undefined);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return Promise.resolve();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/** Construct + load a fresh pooled entry, registering crash recovery. */
|
|
259
|
+
warmOne(spec) {
|
|
260
|
+
const win = this.createWindow(spec);
|
|
261
|
+
const id = `pool-${++this.nextId}`;
|
|
262
|
+
const entry = { id, spec, state: 'warming', win, ready: Promise.resolve() };
|
|
263
|
+
this.entries.set(id, entry);
|
|
264
|
+
win.webContents.on('render-process-gone', () => this.onGone(entry));
|
|
265
|
+
// A window can also die by graceful close (app quit / manual close), which
|
|
266
|
+
// never fires render-process-gone. Observe it too so a pooled entry isn't
|
|
267
|
+
// leaked. reclaim is a no-op if WE disposed it (disposeEntry deletes the
|
|
268
|
+
// entry before its own destroy fires 'closed').
|
|
269
|
+
win.once('closed', () => this.reclaim(entry, { destroyIfPooled: false, refill: false }));
|
|
270
|
+
entry.ready = this.loadBlank(win).then(() => {
|
|
271
|
+
if (entry.state === 'warming')
|
|
272
|
+
entry.state = 'ready';
|
|
273
|
+
});
|
|
274
|
+
return entry.ready;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Reset a released window back to a clean, blank state. Order matters
|
|
278
|
+
* (doc §3.5.3 / §3.4「等待时序」): navigate to blank FIRST so the old document
|
|
279
|
+
* stops running and in-flight requests are aborted, THEN clear storage — so a
|
|
280
|
+
* still-live old page cannot re-populate storage after the clear.
|
|
281
|
+
*/
|
|
282
|
+
async reset(entry) {
|
|
283
|
+
await this.loadBlank(entry.win);
|
|
284
|
+
// Consumers sharing a session opt out of the storage clear (see
|
|
285
|
+
// ServiceHostSpec.clearStorageOnReset): clearing a shared partition would wipe
|
|
286
|
+
// other live projects. Navigation-to-blank above still resets the JS realm.
|
|
287
|
+
if (entry.spec.clearStorageOnReset === false)
|
|
288
|
+
return;
|
|
289
|
+
// The window may have been destroyed during the load await; touching a dead
|
|
290
|
+
// webContents.session throws SYNCHRONOUSLY, which would reject release().
|
|
291
|
+
if (entry.win.isDestroyed())
|
|
292
|
+
return;
|
|
293
|
+
try {
|
|
294
|
+
const ses = entry.win.webContents.session;
|
|
295
|
+
await ses.clearStorageData({ storages: [...RESET_STORAGES] });
|
|
296
|
+
await ses.clearCache();
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Window/session torn down mid-reset — release() re-checks isDestroyed and
|
|
300
|
+
// disposes the entry, so swallowing here keeps release from rejecting.
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/** A spec change tears down pooled entries and re-targets the pool. */
|
|
304
|
+
onSpecChange(spec) {
|
|
305
|
+
for (const entry of [...this.entries.values()]) {
|
|
306
|
+
if (this.isWarm(entry))
|
|
307
|
+
this.disposeEntry(entry);
|
|
308
|
+
}
|
|
309
|
+
this.currentSpec = spec;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Reclaim an entry whose window died externally (render crash, graceful close,
|
|
313
|
+
* or an owner-reported destruction). A POOLED death frees a spare → refill
|
|
314
|
+
* toward target; an IN-USE death is the caller's window → drop the bookkeeping
|
|
315
|
+
* WITHOUT destroying it from under them, and do not refill (the spare count is
|
|
316
|
+
* unaffected). `destroyIfPooled` destroys the husk for a pooled render crash
|
|
317
|
+
* (the BrowserWindow object can outlive a dead render process); skip it for a
|
|
318
|
+
* `'closed'`/owner-reported window that is already gone. No-op if the entry was
|
|
319
|
+
* already disposed by us (disposeEntry deletes before its own destroy fires
|
|
320
|
+
* `'closed'`, so this finds nothing).
|
|
321
|
+
*/
|
|
322
|
+
reclaim(entry, opts) {
|
|
323
|
+
if (!this.entries.has(entry.id))
|
|
324
|
+
return;
|
|
325
|
+
const wasPooled = this.isWarm(entry);
|
|
326
|
+
entry.state = 'disposing';
|
|
327
|
+
this.entries.delete(entry.id);
|
|
328
|
+
if (wasPooled && opts.destroyIfPooled)
|
|
329
|
+
this.destroyWindow(entry.win);
|
|
330
|
+
// Refill ONLY on crash recovery (onGone). A graceful `'closed'` on a pooled
|
|
331
|
+
// window happens exclusively at app/project teardown (the windows are hidden;
|
|
332
|
+
// nothing else closes them) — refilling there would `new BrowserWindow` while
|
|
333
|
+
// Electron is trying to quit, recreating windows faster than they close and
|
|
334
|
+
// hanging the shutdown. So `refill` is opt-in per caller.
|
|
335
|
+
if (!opts.refill || this.disposed || !this.currentSpec || !wasPooled)
|
|
336
|
+
return;
|
|
337
|
+
while (this.warmCount() < this.targetSize) {
|
|
338
|
+
void this.warmOne(this.currentSpec);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/** A pooled window's render process died — reclaim + refill (destroy the husk). */
|
|
342
|
+
onGone(entry) {
|
|
343
|
+
this.reclaim(entry, { destroyIfPooled: true, refill: true });
|
|
344
|
+
}
|
|
345
|
+
disposeEntry(entry) {
|
|
346
|
+
entry.state = 'disposing';
|
|
347
|
+
this.entries.delete(entry.id);
|
|
348
|
+
this.destroyWindow(entry.win);
|
|
349
|
+
}
|
|
350
|
+
destroyWindow(win) {
|
|
351
|
+
try {
|
|
352
|
+
if (!win || (typeof win.isDestroyed === 'function' && win.isDestroyed()))
|
|
353
|
+
return;
|
|
354
|
+
if (typeof win.destroy === 'function')
|
|
355
|
+
win.destroy();
|
|
356
|
+
else if (typeof win.close === 'function')
|
|
357
|
+
win.close();
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// A window already gone (e.g. crashed process) is fine to ignore.
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=pool.js.map
|
|
@@ -2,8 +2,42 @@ export type SimulatorApiHandler = (params: unknown) => unknown | Promise<unknown
|
|
|
2
2
|
export interface SimulatorApiRegistry {
|
|
3
3
|
register(name: string, handler: SimulatorApiHandler): () => void;
|
|
4
4
|
list(): string[];
|
|
5
|
+
has(name: string): boolean;
|
|
5
6
|
invoke(name: string, params: unknown): Promise<unknown>;
|
|
6
7
|
clear(): void;
|
|
7
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Bridge request/response shapes shared by the simulator-side preload
|
|
11
|
+
* (`src/preload/runtime/custom-apis.ts`) and the two host-side dispatchers that
|
|
12
|
+
* service it (the trusted main-window renderer proxy was the original path; the
|
|
13
|
+
* native-host top-level WebContentsView dispatcher in the ViewManager is the
|
|
14
|
+
* current one). Kept here next to the registry so both stay in lockstep with
|
|
15
|
+
* the preload contract.
|
|
16
|
+
*/
|
|
17
|
+
export type CustomApiBridgeRequest = {
|
|
18
|
+
id: number;
|
|
19
|
+
op: 'list';
|
|
20
|
+
} | {
|
|
21
|
+
id: number;
|
|
22
|
+
op: 'invoke';
|
|
23
|
+
name: string;
|
|
24
|
+
params: unknown;
|
|
25
|
+
};
|
|
26
|
+
export type CustomApiBridgeResponse = {
|
|
27
|
+
id: number;
|
|
28
|
+
result: unknown;
|
|
29
|
+
} | {
|
|
30
|
+
id: number;
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Run a single custom-apis bridge request against the given registry and return
|
|
35
|
+
* the id-correlated response the preload's pending-map expects. Never throws:
|
|
36
|
+
* a handler rejection / malformed request becomes an `{ id, error }` response so
|
|
37
|
+
* the caller only has to ship it back over the bridge channel. The one shared
|
|
38
|
+
* implementation behind both the `SimulatorCustomApiChannel` IPC handlers and
|
|
39
|
+
* the native-host `ipc-message-host` dispatcher.
|
|
40
|
+
*/
|
|
41
|
+
export declare function handleCustomApiBridgeRequest(apis: Pick<SimulatorApiRegistry, 'list' | 'invoke'>, req: CustomApiBridgeRequest): Promise<CustomApiBridgeResponse>;
|
|
8
42
|
export declare function createSimulatorApiRegistry(): SimulatorApiRegistry;
|
|
9
43
|
//# sourceMappingURL=custom-apis.d.ts.map
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run a single custom-apis bridge request against the given registry and return
|
|
3
|
+
* the id-correlated response the preload's pending-map expects. Never throws:
|
|
4
|
+
* a handler rejection / malformed request becomes an `{ id, error }` response so
|
|
5
|
+
* the caller only has to ship it back over the bridge channel. The one shared
|
|
6
|
+
* implementation behind both the `SimulatorCustomApiChannel` IPC handlers and
|
|
7
|
+
* the native-host `ipc-message-host` dispatcher.
|
|
8
|
+
*/
|
|
9
|
+
export async function handleCustomApiBridgeRequest(apis, req) {
|
|
10
|
+
try {
|
|
11
|
+
const result = req.op === 'list'
|
|
12
|
+
? apis.list()
|
|
13
|
+
: await apis.invoke(req.name, req.params);
|
|
14
|
+
return { id: req.id, result };
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
return { id: req.id, error: err instanceof Error ? err.message : String(err) };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
1
20
|
export function createSimulatorApiRegistry() {
|
|
2
21
|
const handlers = new Map();
|
|
3
22
|
return {
|
|
@@ -11,6 +30,9 @@ export function createSimulatorApiRegistry() {
|
|
|
11
30
|
list() {
|
|
12
31
|
return Array.from(handlers.keys());
|
|
13
32
|
},
|
|
33
|
+
has(name) {
|
|
34
|
+
return handlers.has(name);
|
|
35
|
+
},
|
|
14
36
|
async invoke(name, params) {
|
|
15
37
|
const handler = handlers.get(name);
|
|
16
38
|
if (!handler)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native-host AppData panel service.
|
|
3
|
+
*
|
|
4
|
+
* Taps the service→render setData stream (bridge-router SERVICE_PUBLISH) — under
|
|
5
|
+
* native-host the service logic runs in the hidden service-host window, not a
|
|
6
|
+
* Worker in the simulator guest, so the default Worker-hook source goes blind.
|
|
7
|
+
* Decodes + accumulates per app via the SHARED `AppDataAccumulator`, mirroring
|
|
8
|
+
* the Storage/WXML main→renderer contract:
|
|
9
|
+
* - PULL: answers `SimulatorAppDataChannel.GetSnapshot` with the ACTIVE app's
|
|
10
|
+
* cumulative `accumulator.snapshot()` (empty snapshot when it has no data).
|
|
11
|
+
* - PUSH: on each accepted message / bridge eviction for the ACTIVE app,
|
|
12
|
+
* pushes the updated snapshot via `SimulatorAppDataChannel.Event`.
|
|
13
|
+
*/
|
|
14
|
+
import type { WebContents } from 'electron';
|
|
15
|
+
import type { MessageEnvelope } from '../../../shared/bridge-channels.js';
|
|
16
|
+
import { type Disposable } from '@dimina-kit/electron-deck/main';
|
|
17
|
+
import { type SenderPolicy } from '../../utils/ipc-registry.js';
|
|
18
|
+
export interface AppDataTap {
|
|
19
|
+
/** Feed one service→render message for an app; decodes + accumulates + pushes if active. */
|
|
20
|
+
onServiceToRender(appId: string, msg: MessageEnvelope): void;
|
|
21
|
+
/** Evict a page bridge (page teardown) from an app's accumulator; pushes if active. */
|
|
22
|
+
evictBridge(appId: string, bridgeId: string): void;
|
|
23
|
+
/** The ACTIVE app's current reactive page state for a bridge (`{}` when none). */
|
|
24
|
+
getPageData(bridgeId: string): Record<string, unknown>;
|
|
25
|
+
/** Snapshot all accumulated appdata for an app. Optional for compatibility with tests/mocks. */
|
|
26
|
+
snapshot?: (appId: string) => unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface SimulatorAppDataService extends AppDataTap, Disposable {
|
|
29
|
+
}
|
|
30
|
+
export interface SimulatorAppDataOptions {
|
|
31
|
+
getActiveAppId: () => string | null;
|
|
32
|
+
senderPolicy?: SenderPolicy;
|
|
33
|
+
}
|
|
34
|
+
export declare function setupSimulatorAppData(host: WebContents, options: SimulatorAppDataOptions): SimulatorAppDataService;
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { SimulatorAppDataChannel } from '../../../shared/ipc-channels.js';
|
|
2
|
+
import { AppDataAccumulator, decodeWorkerMessage, decodedToInput, } from '../../../shared/appdata-accumulator.js';
|
|
3
|
+
import { DisposableRegistry } from '@dimina-kit/electron-deck/main';
|
|
4
|
+
import { IpcRegistry } from '../../utils/ipc-registry.js';
|
|
5
|
+
const EMPTY_SNAPSHOT = { bridges: [], entries: {} };
|
|
6
|
+
export function setupSimulatorAppData(host, options) {
|
|
7
|
+
const { getActiveAppId } = options;
|
|
8
|
+
// One accumulator per app — native-host can host multiple sessions; the panel
|
|
9
|
+
// only ever shows the active one (resolved via getActiveAppId).
|
|
10
|
+
const accumulators = new Map();
|
|
11
|
+
function accumulatorFor(appId) {
|
|
12
|
+
let acc = accumulators.get(appId);
|
|
13
|
+
if (!acc) {
|
|
14
|
+
acc = new AppDataAccumulator();
|
|
15
|
+
accumulators.set(appId, acc);
|
|
16
|
+
}
|
|
17
|
+
return acc;
|
|
18
|
+
}
|
|
19
|
+
/** Push the active app's snapshot to the renderer (no-op for non-active/destroyed). */
|
|
20
|
+
function pushIfActive(appId) {
|
|
21
|
+
if (appId !== getActiveAppId())
|
|
22
|
+
return;
|
|
23
|
+
if (host.isDestroyed())
|
|
24
|
+
return;
|
|
25
|
+
host.send(SimulatorAppDataChannel.Event, accumulatorFor(appId).snapshot());
|
|
26
|
+
}
|
|
27
|
+
function onServiceToRender(appId, msg) {
|
|
28
|
+
const entries = decodeWorkerMessage(msg);
|
|
29
|
+
if (!entries)
|
|
30
|
+
return;
|
|
31
|
+
const acc = accumulatorFor(appId);
|
|
32
|
+
let mutated = false;
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (acc.apply(decodedToInput(entry)))
|
|
35
|
+
mutated = true;
|
|
36
|
+
}
|
|
37
|
+
if (mutated)
|
|
38
|
+
pushIfActive(appId);
|
|
39
|
+
}
|
|
40
|
+
function evictBridge(appId, bridgeId) {
|
|
41
|
+
accumulatorFor(appId).clearBridge(bridgeId);
|
|
42
|
+
pushIfActive(appId);
|
|
43
|
+
}
|
|
44
|
+
function getPageData(bridgeId) {
|
|
45
|
+
const activeAppId = getActiveAppId();
|
|
46
|
+
if (!activeAppId)
|
|
47
|
+
return {};
|
|
48
|
+
return accumulators.get(activeAppId)?.pageData(bridgeId) ?? {};
|
|
49
|
+
}
|
|
50
|
+
const ipc = new IpcRegistry(options.senderPolicy);
|
|
51
|
+
ipc.handle(SimulatorAppDataChannel.GetSnapshot, () => {
|
|
52
|
+
const appId = getActiveAppId();
|
|
53
|
+
if (!appId)
|
|
54
|
+
return EMPTY_SNAPSHOT;
|
|
55
|
+
return accumulators.get(appId)?.snapshot() ?? EMPTY_SNAPSHOT;
|
|
56
|
+
});
|
|
57
|
+
// disposeAll runs LIFO; add the IPC registry LAST so it is torn down first —
|
|
58
|
+
// its removeHandler then runs synchronously (before the first `await` yields),
|
|
59
|
+
// which callers that `dispose()` without awaiting rely on.
|
|
60
|
+
const registry = new DisposableRegistry();
|
|
61
|
+
registry.add(() => accumulators.clear());
|
|
62
|
+
registry.add(ipc);
|
|
63
|
+
return {
|
|
64
|
+
onServiceToRender,
|
|
65
|
+
evictBridge,
|
|
66
|
+
getPageData,
|
|
67
|
+
snapshot: (appId) => accumulatorFor(appId).snapshot(),
|
|
68
|
+
dispose: () => registry.dispose(),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native-host current-page push service.
|
|
3
|
+
*
|
|
4
|
+
* The devtools toolbar shows the visible top-of-stack page route. Under
|
|
5
|
+
* native-host the page stack lives in the DeviceShell WebContentsView (each
|
|
6
|
+
* page a nested render-host `<webview>`), not a renderer `<webview>` the main
|
|
7
|
+
* window can observe — so in-app navigation (navigateTo / switchTab / back)
|
|
8
|
+
* never reaches the renderer's `<webview>` did-navigate events. This service
|
|
9
|
+
* taps the bridge's active-page signal and pushes the new route to the
|
|
10
|
+
* main-window renderer (`SimulatorChannel.CurrentPage`), keeping the toolbar in
|
|
11
|
+
* sync without polling.
|
|
12
|
+
*
|
|
13
|
+
* Inert on the default dimina-fe path (which derives the current page from the
|
|
14
|
+
* simulator `<webview>`'s navigation events); app.ts gates the call on
|
|
15
|
+
* `ctx.bridge.isNativeHost()`.
|
|
16
|
+
*/
|
|
17
|
+
import type { WebContents } from 'electron';
|
|
18
|
+
import type { BridgeRouterHandle } from '../../ipc/bridge-router.js';
|
|
19
|
+
import { type Disposable } from '@dimina-kit/electron-deck/main';
|
|
20
|
+
export interface SimulatorCurrentPageOptions {
|
|
21
|
+
bridge: BridgeRouterHandle;
|
|
22
|
+
}
|
|
23
|
+
export declare function setupSimulatorCurrentPage(host: WebContents, options: SimulatorCurrentPageOptions): Disposable;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SimulatorChannel } from '../../../shared/ipc-channels.js';
|
|
2
|
+
import { DisposableRegistry } from '@dimina-kit/electron-deck/main';
|
|
3
|
+
export function setupSimulatorCurrentPage(host, options) {
|
|
4
|
+
const registry = new DisposableRegistry();
|
|
5
|
+
// Push the new route whenever the visible top-of-stack page changes. domReady
|
|
6
|
+
// events (same kind stream) carry no pagePath and are ignored; the renderer
|
|
7
|
+
// already seeds the entry route from the launch URL, so the first meaningful
|
|
8
|
+
// update is the first post-boot navigation.
|
|
9
|
+
const off = options.bridge.onRenderEvent((ev) => {
|
|
10
|
+
if (ev.kind !== 'activePage' || !ev.pagePath)
|
|
11
|
+
return;
|
|
12
|
+
if (host.isDestroyed())
|
|
13
|
+
return;
|
|
14
|
+
host.send(SimulatorChannel.CurrentPage, ev.pagePath);
|
|
15
|
+
});
|
|
16
|
+
registry.add(off);
|
|
17
|
+
return registry;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -16,8 +16,27 @@
|
|
|
16
16
|
* events stop flowing until they close it.
|
|
17
17
|
*/
|
|
18
18
|
import { type WebContents } from 'electron';
|
|
19
|
-
import { type Disposable } from '
|
|
19
|
+
import { type ConnectionRegistry, type Disposable } from '@dimina-kit/electron-deck/main';
|
|
20
20
|
import { type SenderPolicy } from '../../utils/ipc-registry.js';
|
|
21
|
+
import type { BridgeRouterHandle } from '../../ipc/bridge-router.js';
|
|
22
|
+
import type { RenderInspector } from '../render-inspect/index.js';
|
|
23
|
+
/**
|
|
24
|
+
* Runtime async-storage handler (native-host). bridge-router routes async
|
|
25
|
+
* `wx.setStorage`/`getStorage`/… here so they hit the SAME service-host `file://`
|
|
26
|
+
* store as the sync APIs — eliminating the default-arch split where async writes
|
|
27
|
+
* landed on the simulator `http://` origin and sync writes on `file://`. Returns
|
|
28
|
+
* the value for the wx success callback (or throws → wx fail callback).
|
|
29
|
+
*/
|
|
30
|
+
export interface StorageApi {
|
|
31
|
+
invoke(appId: string, name: string, params: Record<string, unknown>): Promise<unknown>;
|
|
32
|
+
}
|
|
33
|
+
/** Async wx storage API names routed through the unified service-window store. */
|
|
34
|
+
export declare const STORAGE_API_NAMES: Set<string>;
|
|
35
|
+
/** The storage panel's return — a Disposable plus the native-host runtime hook. */
|
|
36
|
+
export interface SimulatorStorageHandle extends Disposable {
|
|
37
|
+
/** Present only under native-host; wired to ctx.storageApi for bridge-router. */
|
|
38
|
+
storageApi: StorageApi | null;
|
|
39
|
+
}
|
|
21
40
|
export interface SimulatorStorageOptions {
|
|
22
41
|
/**
|
|
23
42
|
* Sender gate applied to `SimulatorStorageChannel.GetSnapshot`.
|
|
@@ -36,6 +55,22 @@ export interface SimulatorStorageOptions {
|
|
|
36
55
|
* CDP snapshot/event stream to the active appId.
|
|
37
56
|
*/
|
|
38
57
|
getActiveAppId: () => string | null;
|
|
58
|
+
/**
|
|
59
|
+
* Bridge-router accessor (native-host only). When present and native-host is
|
|
60
|
+
* on, element inspection targets the active render-host <webview> guest via
|
|
61
|
+
* `renderInspector` instead of the simulator iframe's `__simulatorData`.
|
|
62
|
+
*/
|
|
63
|
+
bridge?: BridgeRouterHandle;
|
|
64
|
+
/** Injects/drives the render-guest inspector; required for native-host inspect. */
|
|
65
|
+
renderInspector?: RenderInspector;
|
|
66
|
+
/**
|
|
67
|
+
* Connection-layer registry (`@dimina-kit/electron-deck/main`). When provided,
|
|
68
|
+
* per-webContents teardowns are routed through `acquire(wc).own(cleanup)` so
|
|
69
|
+
* they fire deterministically on wc destroy / connection reset instead of the
|
|
70
|
+
* bespoke `wc.once('destroyed', cleanup)` hook. Optional so callers that have
|
|
71
|
+
* not adopted the connection layer compile and behave unchanged.
|
|
72
|
+
*/
|
|
73
|
+
connections?: ConnectionRegistry;
|
|
39
74
|
}
|
|
40
|
-
export declare function setupSimulatorStorage(host: WebContents, options: SimulatorStorageOptions):
|
|
75
|
+
export declare function setupSimulatorStorage(host: WebContents, options: SimulatorStorageOptions): SimulatorStorageHandle;
|
|
41
76
|
//# sourceMappingURL=index.d.ts.map
|