@dimina-kit/electron-deck 0.1.0-dev.20260610082053

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/client/index.d.ts +20 -0
  4. package/dist/client/index.d.ts.map +1 -0
  5. package/dist/client/index.js +310 -0
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/client/layout-client.d.ts +51 -0
  8. package/dist/client/layout-client.d.ts.map +1 -0
  9. package/dist/electron-deck-DfFKeFEC.js +2295 -0
  10. package/dist/electron-deck-DfFKeFEC.js.map +1 -0
  11. package/dist/electron-deck.d.ts +57 -0
  12. package/dist/electron-deck.d.ts.map +1 -0
  13. package/dist/errors-NE2ig7L5.js +53 -0
  14. package/dist/errors-NE2ig7L5.js.map +1 -0
  15. package/dist/errors.d.ts +33 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/events.d.ts +14 -0
  18. package/dist/events.d.ts.map +1 -0
  19. package/dist/host/capability.d.ts +50 -0
  20. package/dist/host/capability.d.ts.map +1 -0
  21. package/dist/host/control-bus.d.ts +92 -0
  22. package/dist/host/control-bus.d.ts.map +1 -0
  23. package/dist/host/index.d.ts +42 -0
  24. package/dist/host/index.d.ts.map +1 -0
  25. package/dist/host/index.js +2 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +3 -0
  29. package/dist/internal/deck-app.d.ts +415 -0
  30. package/dist/internal/deck-app.d.ts.map +1 -0
  31. package/dist/internal/electron-types.d.ts +131 -0
  32. package/dist/internal/electron-types.d.ts.map +1 -0
  33. package/dist/internal/event-bus.d.ts +29 -0
  34. package/dist/internal/event-bus.d.ts.map +1 -0
  35. package/dist/internal/ipc-registry-memory.d.ts +25 -0
  36. package/dist/internal/ipc-registry-memory.d.ts.map +1 -0
  37. package/dist/internal/lifecycle-manager.d.ts +28 -0
  38. package/dist/internal/lifecycle-manager.d.ts.map +1 -0
  39. package/dist/internal/resource-registry.d.ts +9 -0
  40. package/dist/internal/resource-registry.d.ts.map +1 -0
  41. package/dist/internal/trust-set.d.ts +53 -0
  42. package/dist/internal/trust-set.d.ts.map +1 -0
  43. package/dist/internal/wire-transport.d.ts +159 -0
  44. package/dist/internal/wire-transport.d.ts.map +1 -0
  45. package/dist/main/compositor.d.ts +123 -0
  46. package/dist/main/compositor.d.ts.map +1 -0
  47. package/dist/main/connection.d.ts +37 -0
  48. package/dist/main/connection.d.ts.map +1 -0
  49. package/dist/main/debug-tap.d.ts +55 -0
  50. package/dist/main/debug-tap.d.ts.map +1 -0
  51. package/dist/main/disposable.d.ts +18 -0
  52. package/dist/main/disposable.d.ts.map +1 -0
  53. package/dist/main/index.d.ts +16 -0
  54. package/dist/main/index.d.ts.map +1 -0
  55. package/dist/main/index.js +152 -0
  56. package/dist/main/index.js.map +1 -0
  57. package/dist/main/logger.d.ts +15 -0
  58. package/dist/main/logger.d.ts.map +1 -0
  59. package/dist/main/scope.d.ts +52 -0
  60. package/dist/main/scope.d.ts.map +1 -0
  61. package/dist/main/view-handle.d.ts +131 -0
  62. package/dist/main/view-handle.d.ts.map +1 -0
  63. package/dist/preload/index.cjs +105 -0
  64. package/dist/preload/index.cjs.map +1 -0
  65. package/dist/preload/index.d.ts +43 -0
  66. package/dist/preload/index.d.ts.map +1 -0
  67. package/dist/preload/index.js +80 -0
  68. package/dist/preload/index.js.map +1 -0
  69. package/dist/protocol-BJqo-vP2.js +23 -0
  70. package/dist/protocol-BJqo-vP2.js.map +1 -0
  71. package/dist/shared/protocol.d.ts +76 -0
  72. package/dist/shared/protocol.d.ts.map +1 -0
  73. package/dist/types.d.ts +504 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/view-handle-CR-yWNfr.js +701 -0
  76. package/dist/view-handle-CR-yWNfr.js.map +1 -0
  77. package/package.json +86 -0
@@ -0,0 +1,415 @@
1
+ import type { Disposable, RuntimeBackend, Runtime, SenderPolicy, DeckConfig } from '../types.js';
2
+ import type { MinimalBrowserWindow, MinimalElectron } from './electron-types.js';
3
+ import type { LifecyclePhase } from './lifecycle-manager.js';
4
+ import { type Scope } from '../main/scope.js';
5
+ import { type CapabilityPolicy } from '../host/capability.js';
6
+ import { type MinimalIpcMain, type MinimalWebContents } from './wire-transport.js';
7
+ /**
8
+ * Optional dependencies for {@link DeckApp} —— Phase 3b 用于注入真 (或
9
+ * mock) Electron `ipcMain` + trusted webContents 集合,让 framework 接通跨进程
10
+ * wire transport。不注入则保持 Phase 2 main-internal-only 行为。
11
+ *
12
+ * @internal
13
+ */
14
+ export interface DeckAppOptions {
15
+ readonly wireTransport?: {
16
+ readonly ipcMain: MinimalIpcMain;
17
+ /** 默认返回 framework 内部维护的 trusted set(Phase 4 由 windows.trust 填)。 */
18
+ readonly trustedWebContents?: () => readonly MinimalWebContents[];
19
+ /** 自定义 senderPolicy(默认按 trusted set 判断)。 */
20
+ readonly senderPolicy?: SenderPolicy;
21
+ };
22
+ /**
23
+ * Phase 4 — 注入真 (或 fake) Electron `BrowserWindow` / `WebContentsView`
24
+ * 构造器;提供后 framework 会装配 mainWindow / toolbarView / declared
25
+ * windows,否则保持 Phase 3b electron-unavailable 行为。
26
+ */
27
+ readonly electron?: MinimalElectron;
28
+ /**
29
+ * v2 — 领域 backend。提供后 framework 在 whenReady 前跑 `beforeReady`,
30
+ * 在 setup 阶段跑 `assemble(runtime)`。不提供则退化为纯框架(桩 context,
31
+ * 仅测试/演示用)。
32
+ */
33
+ readonly backend?: RuntimeBackend;
34
+ }
35
+ /**
36
+ * Framework-internal "app" object —— `electronDeck(config)` 顶层入口的 plain-class
37
+ * 形态,便于 Phase 2 测试驱动 lifecycle 转换。Phase 3b 加 wireTransport 注入
38
+ * 后可接真 ipcMain;Phase 4 加 electron 注入后可装配 mainWindow / toolbarView
39
+ * / declared windows。两者都不注入时退化为同进程内存 fake。
40
+ *
41
+ * @internal
42
+ */
43
+ export declare class DeckApp {
44
+ readonly config: DeckConfig;
45
+ private readonly lifecycle;
46
+ private readonly registry;
47
+ private readonly bus;
48
+ private readonly ipc;
49
+ private readonly fwListeners;
50
+ private readonly trustSet;
51
+ /** P4 Phase B — privileged-command grant registry. The policy gates
52
+ * ControlBus.dispatch; grants are minted via `runtime.grants.issue`. */
53
+ private readonly capability;
54
+ /** P4 Phase B — the grant-gated command bus for PRIVILEGED `layout.*`
55
+ * commands. Constructed in `bindWireTransport` with the capability policy
56
+ * injected, so `dispatch` default-DENIES any command lacking a live grant.
57
+ * Privileged commands are registered via `runtime.layout.command`; ordinary
58
+ * domain APIs stay on the un-gated `InMemoryTypedIpcRegistry`. */
59
+ private controlBus;
60
+ /** v2 — per-webContents backend `onWindowTrusted` Disposable, so a window's
61
+ * trust mirror is undone when THAT window closes (not only at teardown). */
62
+ private readonly backendTrustDisposables;
63
+ private readonly options;
64
+ private wireTransport;
65
+ /** v2 — the live wire senderPolicy, reused by buildRuntime so
66
+ * `context._senderPolicy` reflects real trust instead of a `() => true` stub. */
67
+ private wireSenderPolicy;
68
+ private mainWindow;
69
+ private toolbarView;
70
+ private readonly declaredWindows;
71
+ private readonly trackedWindows;
72
+ /**
73
+ * unified-lifetime P0 (observation-only, zero-regression). The root lifetime
74
+ * scope of the app + a shadow map mirroring `trackedWindows`, keyed by each
75
+ * window's webContents, carrying a per-window child Scope. P0 is PURELY
76
+ * OBSERVATIONAL: these windowScopes own NO resources (no destroy/trust/wire is
77
+ * moved onto them), so `rootScope.close()` during shutdown is a no-op for app
78
+ * behaviour — it exists only to lay the foundation P1 will take over. The
79
+ * shadow's key set is kept in lock-step with `trackedWindows` at every
80
+ * maintenance point.
81
+ */
82
+ private readonly rootScope;
83
+ private readonly lifetimeShadow;
84
+ /**
85
+ * unified-lifetime P1b: per-trusted-webContents trust record. `wcScope` is a
86
+ * child of the owning window's `windowScope`; it OWNS the wc's trust ref-count
87
+ * lease(s). When the window closes, `windowScope.close()` cascades into this
88
+ * `wcScope` (children-first LIFO), disposing every lease → ref-count hits 0 →
89
+ * the wc leaves the trust set. This replaces the imperative `deleteEntry`.
90
+ * A wc can be trusted more than once (framework auto-trust + host
91
+ * `windows.trust`), so `leases` is a Set.
92
+ */
93
+ private readonly wcRecords;
94
+ private readonly pendingWindowCreated;
95
+ private readonly pendingLoadFailed;
96
+ /**
97
+ * host-view slice 1 — per-window native-view substrate, keyed by the window's
98
+ * webContents (same key discipline as `lifetimeShadow`). Created at both
99
+ * window-construction sites; dropped in `handleSubWindowClosed`.
100
+ */
101
+ private readonly windowSubstrates;
102
+ /**
103
+ * P3 — per-adopted-window registration handle, keyed by the adopted window's
104
+ * webContents. `runtime.windows.adopt` is idempotent by wc identity: a second
105
+ * adopt of the same window returns this stored Disposable (no double-admit, no
106
+ * second substrate). The entry is removed when the registration is disposed
107
+ * (early un-adopt) or the window's windowScope closes.
108
+ */
109
+ private readonly adoptedWindows;
110
+ /**
111
+ * C1/C4 — per-registered-window {@link DeckWindow} facade + its per-window
112
+ * close deciders, keyed by the window's control wc. Populated by
113
+ * {@link registerWindow} for framework-created and adopted windows, plus the
114
+ * framework-built main window. `deciders` is an ORDERED list run in
115
+ * registration order on a close attempt; `closing` is the per-window in-flight
116
+ * decision latch (mirrors the main-window `closingDecisionPromise`).
117
+ */
118
+ private readonly windowRegistrations;
119
+ /** C1 — the framework-built main window's control wc, so `runtime.windows.main`
120
+ * can resolve its {@link DeckWindow}. Null under an `ownsWindows:true` backend. */
121
+ private mainControlWc;
122
+ /** host-view slice 1 — monotonic id source for `runtime.view` native views. */
123
+ private viewSeq;
124
+ /**
125
+ * P2 — provenance map for `runtime.scopes.create()` sessions: maps an opaque
126
+ * {@link DeckSession} to its internal `rootScope.child()` Scope. A WeakMap so a
127
+ * dropped session is GC'd; the framework holds the scope's lifetime via
128
+ * rootScope anyway (the session scope is a rootScope child). `runtime.view`
129
+ * resolves a passed session through this map — a foreign/raw Scope is absent
130
+ * and therefore REJECTED.
131
+ */
132
+ private readonly sessions;
133
+ /**
134
+ * keepAlive B3.2 — opt-in per-group LRU of HIDDEN keep-alive views. Group key is
135
+ * `lru:${max}` (all `keepAlive:{policy:'lru',max:N}` views share one group per
136
+ * `max`). Each group holds an ORDERED list of HIDDEN view ids (front = least
137
+ * recently visible = first to evict) + a map from view id to its host handle so
138
+ * an eviction can dispose it (→ its WebContents is destroyed). Views created
139
+ * without `keepAlive` never participate.
140
+ */
141
+ private readonly keepAliveGroups;
142
+ /** C3: wcs that already have the `did-start-navigation` grant-reset hook bound,
143
+ * so {@link bindNavigationGrantReset} is idempotent — a wc admitted to trust
144
+ * more than once (e.g. constructed `autoTrust:false` then `windows.trust()`ed,
145
+ * or un-adopted then re-adopted) never accumulates duplicate nav listeners. */
146
+ private readonly navHookBound;
147
+ /**
148
+ * slot-token registry (build-plan §2(e) / capability-and-lifecycle §A5-2):
149
+ * each anchored `placeIn` mints an unguessable token bound to (viewId, slotId,
150
+ * authorizedWcId). The `__electron-deck:place` apply path looks the token up,
151
+ * checks the sender is the authorized wc (anti-spoof), validates the placement,
152
+ * then `apply`s it. `resend` re-pushes the slot-grant (layout-subscribe replay).
153
+ */
154
+ private readonly slotTokens;
155
+ private slotSeq;
156
+ private _runtime;
157
+ private startCalled;
158
+ private shutdownPromise;
159
+ /** Set when shutdown is driven by the `will-quit` handler — the app is already
160
+ * quitting, so `doShutdown()` must NOT re-`app.quit()` (re-entrant quit). */
161
+ private quitInitiated;
162
+ /** v2 close machine: non-null while a close decision is awaiting (in-flight latch). */
163
+ private closingDecisionPromise;
164
+ /** v2 close machine: set once a 'close' decision is committed (guards the
165
+ * window between decision-resolve and the 'closed' event). Never resets. */
166
+ private shuttingDown;
167
+ /** Idempotency latch for {@link runShutdownCleanup}: the cleanup body
168
+ * (beforeClose → backend.onShutdown → rootScope.close) must run AT MOST ONCE.
169
+ * Both `doShutdown` (single-flight via shutdownPromise) AND `cleanupOnError`
170
+ * (which bypasses that promise) reach here; and `rootScope.close()` inside the
171
+ * body can synchronously fire a framework window's `'closed'` → `shutdown()`
172
+ * re-entry. Without this latch that re-entry would invoke `backend.onShutdown`
173
+ * a second time.
174
+ *
175
+ * This is a JOIN, not a boolean early-return: a re-entrant caller AWAITS the
176
+ * SAME in-flight cleanup promise instead of returning immediately. A plain
177
+ * `if (ran) return` let a concurrent shutdown skip past an in-flight cleanup
178
+ * and force `app.quit()` mid-teardown (truncation). Never resets. */
179
+ private cleanupPromise;
180
+ constructor(config: DeckConfig, options?: DeckAppOptions);
181
+ get phase(): LifecyclePhase;
182
+ get runtime(): Runtime;
183
+ start(): Promise<void>;
184
+ /**
185
+ * Process-level Electron lifecycle bindings (post-whenReady, independent of
186
+ * `ownsWindows` — these are app events, not window events):
187
+ * - `will-quit` → framework teardown (idempotent via `shutdownPromise`).
188
+ * - `window-all-closed` → bound ONLY when `quitOnAllWindowsClosed` is set
189
+ * (opt-in; omitted leaves Electron's default / the consumer's own handler).
190
+ * - `second-instance` → backend hook, bound only under `singleInstance`.
191
+ */
192
+ private bindAppLifecycle;
193
+ shutdown(): Promise<void>;
194
+ /**
195
+ * @internal Phase 4 windows.trust() / framework 内部添加 trusted webContents.
196
+ * Backend-owned / untracked-window fallback: there is no framework windowScope
197
+ * for these (the backend manages their lifetime), so the trust lease is owned
198
+ * by `rootScope` as an app-shutdown backstop. The backend still disposes the
199
+ * returned handle early when it untrusts / destroys its own window.
200
+ */
201
+ _trustWebContents(wc: MinimalWebContents): Disposable;
202
+ /**
203
+ * unified-lifetime P1b: admit `wc` to the trust set under `windowScope`. Gets
204
+ * or creates the wc's `wcScope` (a child of `windowScope`), takes a fresh
205
+ * `trustSet.admit(wc, wcScope)` ref-count lease OWNED BY that wcScope, and returns a
206
+ * one-shot host-facing Disposable that releases just THIS lease early. On
207
+ * `wcScope.close()` (window-close cascade) every still-held lease is disposed
208
+ * → ref-count zeroes → the wc leaves the set (equivalent to the old
209
+ * `deleteEntry`, but driven by Scope teardown and covering partially-built
210
+ * windows too). Idempotent registry cleanup is owned by the wcScope.
211
+ */
212
+ private admitTrust;
213
+ /**
214
+ * C3 (P0 security) — bind the main-frame cross-document navigation grant-reset
215
+ * hook on a control `wc`. On a MAIN-FRAME CROSS-DOCUMENT navigation
216
+ * (`isMainFrame && !isInPlace`) this SYNCHRONOUSLY revokes the wc's capability
217
+ * grants (`capability.revokeBySenderId`) and slot tokens, so the navigated-to
218
+ * document can't inherit the prior page's privileges. Trust is LEFT INTACT (the
219
+ * wc stays the framework's control surface) — we deliberately do NOT tear down
220
+ * its `wcScope`, because that would dispose the trust lease and force an async
221
+ * re-admit gap that breaks `runtime.grants.issue`. In-place (hash/pushState) and
222
+ * sub-frame navigations are ignored; the initial document load is a no-op (no
223
+ * grants exist yet).
224
+ *
225
+ * Must be called AFTER {@link admitTrust} so the wc's `wcScope` exists. The
226
+ * `wc.on` guard tolerates the minimal fakes that lack an EventEmitter surface.
227
+ */
228
+ private bindNavigationGrantReset;
229
+ /**
230
+ * C1 — build the {@link DeckWindow} facade over a registered window and record
231
+ * it (so `runtime.windows.create()` / `runtime.windows.main` can return it and
232
+ * the per-window close machine can find its deciders). `newSession()` mints a
233
+ * window-rooted {@link DeckSession} (a `windowScope.child()` registered in the
234
+ * SAME provenance WeakMap as `runtime.scopes.create()`); `onClose()` appends a
235
+ * per-window decider.
236
+ */
237
+ private buildDeckWindow;
238
+ /**
239
+ * Resolve a window argument that may be either a raw `BrowserWindow` (the
240
+ * historical `runtime.view().placeIn(window)` shape) OR a {@link DeckWindow}
241
+ * handle (the C1 `runtime.windows.create()` return). A DeckWindow exposes the
242
+ * underlying window via `.window` and has no `.webContents` of its own; a raw
243
+ * window has `.webContents`. Unwrap so the substrate lookup keys off the real
244
+ * control webContents either way — keeping the `placeIn(deckWindow)` and
245
+ * `placeIn(deckWindow.window)` call shapes both valid (additive, no break).
246
+ */
247
+ private resolveWindowArg;
248
+ /**
249
+ * C4 — run a registered window's per-window close deciders (registration order;
250
+ * any `'keep'` vetoes), with an in-flight latch so a re-entrant close during a
251
+ * pending decision decides ONCE. A throw / rejection fails CLOSED. Returns the
252
+ * committed decision, or `null` when the decision is still in flight / there are
253
+ * no deciders (caller falls back). Mirrors the main-window decision machine.
254
+ */
255
+ private runWindowCloseDeciders;
256
+ /**
257
+ * C4 — arm the per-window cancelable close-decision machine on a CREATED /
258
+ * adopted window. On a `close` attempt: `preventDefault`, dispatch the
259
+ * per-window deciders behind an in-flight latch (re-entrant closes swallowed),
260
+ * and on a `'close'` decision `win.destroy()` (→ `'closed'` → revoke +
261
+ * windowScope cascade). A `'keep'` clears the latch so the next close re-decides.
262
+ * Only armed when at least one decider can exist (i.e. the window has a
263
+ * registration) — windows with no per-window deciders fall through to Electron's
264
+ * default close (the framework does not preventDefault).
265
+ */
266
+ private armSubWindowCloseMachine;
267
+ /**
268
+ * @internal unified-lifetime P0 accessors (observation-only). The live shadow
269
+ * map mirroring `trackedWindows`, the root Scope, and a consistency assertion.
270
+ */
271
+ __lifetimeShadow(): Map<MinimalWebContents, {
272
+ window: MinimalBrowserWindow;
273
+ windowScope: Scope;
274
+ }>;
275
+ /** @internal unified-lifetime P0: the root lifetime Scope. */
276
+ __rootScope(): Scope;
277
+ /** @internal unified-lifetime P1b: the per-trusted-wc trust records. */
278
+ __wcRecords(): Map<MinimalWebContents, {
279
+ wcScope: Scope;
280
+ leases: Set<Disposable>;
281
+ windowScope: Scope;
282
+ }>;
283
+ /** @internal P4 Phase B: the live capability policy (grant gate reads it). */
284
+ __capabilityPolicy(): CapabilityPolicy;
285
+ /**
286
+ * @internal unified-lifetime P0 invariant (A): the shadow's per-window set
287
+ * (the `window` of each entry) must equal `trackedWindows` membership. Throws
288
+ * if violated; no-op otherwise.
289
+ */
290
+ __assertLifetimeConsistent(): void;
291
+ private bindDeclarativeFields;
292
+ /**
293
+ * host-view slice 1 — build a per-window {@link ViewSubstrate}. The
294
+ * `ContentViewHost` adapts `win.contentView` and tracks the Compositor-managed
295
+ * z-order in `order` (the minimal `contentView` has no `children()`). The
296
+ * substrate's `detachAll` is owned on `windowScope` AFTER the window's
297
+ * `win.destroy` own, so LIFO teardown runs detachAll (STEP1) BEFORE destroy
298
+ * (STEP4) — the A4 ordering.
299
+ */
300
+ private createWindowSubstrate;
301
+ private assembleElectron;
302
+ /** Phase 4 race fix:在 wireTransport.start 之后再 loadURL/loadFile。 */
303
+ private loadAssembledSources;
304
+ /**
305
+ * #3 R5/C2 — load is best-effort: log + emit `load-failed` if it rejects,
306
+ * but never let start() reject because of a renderer load issue (we'd
307
+ * leave the host blocked indefinitely while the framework is otherwise
308
+ * happy).
309
+ */
310
+ private safeLoad;
311
+ /**
312
+ * D1 fix: load-failed 在 listener 注册之前到达时 buffer 到 pendingLoadFailed;
313
+ * 第一个 listener 注册时 splice 消费整个队列。已有 listener 时直接 emit。
314
+ */
315
+ private surfaceLoadFailed;
316
+ private bindWireTransport;
317
+ /**
318
+ * The two-route boundary's privileged-name predicate (§A5-1.2 硬约束). A
319
+ * PRIVILEGED command name — by convention `layout.*` — routes through the
320
+ * grant-gated {@link ControlBus} (`controlBus.dispatch`). Ordinary domain
321
+ * APIs (any other name) stay on the un-gated declarative `hostServices`
322
+ * route. Privileged names MUST NOT be registered in `hostServices`.
323
+ */
324
+ private isPrivilegedCommandName;
325
+ /**
326
+ * KA-4: warn ONCE about an invalid `keepAlive.max` (negative / non-integer /
327
+ * NaN). Such a view is not keep-alive-managed (the group is skipped); warning
328
+ * once avoids log spam when many views share the same bad config.
329
+ */
330
+ private warnedInvalidKeepAliveMax;
331
+ private warnInvalidKeepAliveMax;
332
+ private constructWindow;
333
+ /**
334
+ * P3 — register an EXTERNALLY-created window into the framework so
335
+ * `runtime.view().placeIn(win)` works for it: a `rootScope.child()`
336
+ * windowScope, a per-window {@link ViewSubstrate}, and the TRUST lifecycle —
337
+ * mirroring {@link constructWindow}'s registration, but for a window the host
338
+ * built (e.g. under `ownsWindows:true`). The framework holds NO windowScope /
339
+ * substrate / trust for such a window until this call.
340
+ *
341
+ * Ordering MIRRORS the framework's own windows (constructWindow / main-window):
342
+ * 1) build the windowScope (child of rootScope so shutdown cascades it),
343
+ * 2) build + register the per-window substrate (so placeIn can resolve it),
344
+ * 3) for `ownership:'transfer'` ONLY, OWN `() => win.destroy()` on the
345
+ * windowScope so app shutdown destroys the window; for `'observe'` the host
346
+ * keeps lifetime control (the framework never destroys it),
347
+ * 4) arm trust+grant+slot-token revocation as the FIRST `'closed'` listener via
348
+ * `prependListener` (codex R2 — runs before any external `'closed'` listener),
349
+ * 5) `admitTrust` AFTER the revoke listener is registered (constructWindow order).
350
+ *
351
+ * Idempotent by webContents identity: a second adopt of the same window returns
352
+ * the existing registration. Throws if the window/webContents is destroyed.
353
+ */
354
+ private adoptWindow;
355
+ /**
356
+ * unified-lifetime P1b: synchronously revoke BOTH trust leases AND capability
357
+ * grants for every wc admitted under `windowScope` (the window's control wc +
358
+ * any siblings like the toolbar wc). Called from the window's 'closed' handler
359
+ * so both authorizations are gone the instant the window is destroyed —
360
+ * matching the old synchronous `deleteEntry` timing and closing every
361
+ * async-cascade race (a destroyed wc can never be observed trusted OR granted
362
+ * by a later shutdown/beforeClose, nor by a NEW window that reuses the same
363
+ * wc.id before the async scope cascade revokes it). The leases are ALSO owned
364
+ * by their wcScope, so the async `windowScope.close()` still runs (it disposes
365
+ * the already-disposed leases idempotently, plus win.destroy + wcRecords
366
+ * cleanup) and remains the teardown/partial-init fallback for paths where no
367
+ * 'closed' fires (cleanupOnError, shutdown of a never-closed window).
368
+ * `lease.dispose()` is synchronous (trustSet ref-count--) and one-shot, so no
369
+ * double-decrement; `revokeBySenderId` is likewise idempotent, so the grant's
370
+ * own async `senderScope.on('closed')` revoke later becomes a no-op.
371
+ */
372
+ private revokeWindowTrust;
373
+ /**
374
+ * Arm the wire's slot-token inbound channels (idempotent). P5 eager-arm:
375
+ * called once at framework start() (bindWireTransport, right after
376
+ * transport.start()) so a layout client can subscribe before the first
377
+ * anchored placeIn; the anchored `placeIn` call site is now a redundant no-op
378
+ * (kept for safety). No-op when there's no wire transport (main-internal-only
379
+ * builds can't serve a renderer slot anyway).
380
+ */
381
+ private ensureSlotChannelsArmed;
382
+ /**
383
+ * slot-token apply path (`__electron-deck:place`). The token is the credential:
384
+ * look it up, verify the sender IS the wc the token was granted to (anti-spoof),
385
+ * validate the placement, then apply. Any failure → DROP (silent).
386
+ */
387
+ private handleSlotPlace;
388
+ /**
389
+ * Per-wc layout-subscribe replay (`__electron-deck:layout-subscribe`). When the
390
+ * authorized control wc (re)subscribes (e.g. after reload), re-push every grant
391
+ * it owns so it can re-bind its DOM slots. A DIFFERENT wc's subscribe never
392
+ * receives another wc's grants.
393
+ */
394
+ private handleLayoutSubscribe;
395
+ /** Drop every slot token authorized to `wcId` (window-close / shutdown hygiene). */
396
+ private revokeSlotTokensForWc;
397
+ private handleSubWindowClosed;
398
+ /**
399
+ * v2 — notify the backend that the framework has edge-trusted one of its
400
+ * (framework-built) webContents, so the backend can mirror it into the domain
401
+ * trust set. The returned Disposable is disposed when THAT window closes
402
+ * ({@link handleSubWindowClosed}) — honouring the contract "framework disposes
403
+ * on untrust / window destroy" — and is also added to the registry as a
404
+ * teardown safety net (e.g. the main window, which dies with the app). The
405
+ * wrapper is one-shot so the two paths don't double-dispose.
406
+ */
407
+ private _notifyBackendTrusted;
408
+ private emitFrameworkEvent;
409
+ private cleanupOnError;
410
+ private doShutdown;
411
+ private runShutdownCleanup;
412
+ private doShutdownCleanup;
413
+ private buildRuntime;
414
+ }
415
+ //# sourceMappingURL=deck-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deck-app.d.ts","sourceRoot":"","sources":["../../src/internal/deck-app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Phase 4 — minimal structural typing for the Electron surface the electron-deck
3
+ * framework needs to assemble its real windows / views. Tests inject a plain
4
+ * object satisfying these shapes via {@link DeckAppOptions.electron};
5
+ * the framework does **not** import 'electron' directly.
6
+ *
7
+ * Mirror of {@link wire-transport.ts}'s `MinimalIpcMain` / `MinimalWebContents`
8
+ * style — narrow enough to express the contract, wide enough for tests to fake
9
+ * with `vi.fn()`.
10
+ *
11
+ * @internal
12
+ */
13
+ export interface MinimalRect {
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ height: number;
18
+ }
19
+ export interface MinimalBrowserWindowOptions {
20
+ width?: number;
21
+ height?: number;
22
+ minWidth?: number;
23
+ minHeight?: number;
24
+ title?: string;
25
+ icon?: string;
26
+ modal?: boolean;
27
+ show?: boolean;
28
+ backgroundColor?: string;
29
+ parent?: MinimalBrowserWindow;
30
+ /**
31
+ * Carries the merged window prefs (framework defaults + `config.app.window`
32
+ * + backend `mainWindowWebPreferences()`); `preload` is the one key the
33
+ * framework itself sets for declared/toolbar webviews.
34
+ */
35
+ webPreferences?: Record<string, unknown> & {
36
+ preload?: string;
37
+ };
38
+ }
39
+ export interface MinimalWebContentsLike {
40
+ readonly id: number;
41
+ loadURL(url: string): Promise<void>;
42
+ loadFile(path: string): Promise<void>;
43
+ send(channel: string, payload: unknown): void;
44
+ /**
45
+ * Destroy the backing renderer (Electron's `webContents.close()`). Guard with
46
+ * `isDestroyed()` for idempotence — never double-close. Optional so legacy fakes
47
+ * that never need it keep compiling; the real Electron `webContents` always has it.
48
+ * Typed as `Function` (not `() => void`) on purpose: test fakes assign a bare
49
+ * `vi.fn()` Mock whose `Mock<Procedure | Constructable>` carries a construct
50
+ * signature that is not assignable to a plain call signature — only `Function`
51
+ * (or `unknown`) accepts it. Callers invoke it as `close?.()`.
52
+ */
53
+ close?: Function;
54
+ isDestroyed(): boolean;
55
+ }
56
+ export interface MinimalContentView {
57
+ addChildView(view: MinimalWebContentsView): void;
58
+ removeChildView(view: MinimalWebContentsView): void;
59
+ /** The window's live child views (real Electron `View.children`). Used by the
60
+ * moveTo rollback to guard against removing a child the dest never added
61
+ * (codex P0 round-3 BUG 2). Optional so existing fakes stay valid; an absent
62
+ * array is treated as "membership unknown" by the guard's `?? []`. */
63
+ readonly children?: readonly MinimalWebContentsView[];
64
+ }
65
+ export interface MinimalBrowserWindow {
66
+ readonly id: number;
67
+ readonly webContents: MinimalWebContentsLike;
68
+ readonly contentView: MinimalContentView;
69
+ getContentBounds(): MinimalRect;
70
+ show(): void;
71
+ destroy(): void;
72
+ isDestroyed(): boolean;
73
+ /** `close` is cancelable (preventDefault); `resize`/`closed` listeners ignore the arg. */
74
+ on(event: 'resize' | 'closed' | 'close', listener: (e?: {
75
+ preventDefault(): void;
76
+ }) => void): MinimalBrowserWindow;
77
+ /**
78
+ * Register a listener at the FRONT of the listener list (Electron's
79
+ * EventEmitter `prependListener` — prepended listeners run BEFORE any earlier
80
+ * `on`-added listener for the same event). The framework uses this to arm an
81
+ * adopted window's trust/grant revocation so it runs FIRST on `'closed'`,
82
+ * before any external `'closed'` listener the host registered earlier.
83
+ *
84
+ * Optional so legacy fakes that never adopt a window keep compiling without it;
85
+ * the real Electron `BrowserWindow` (an EventEmitter) always provides it, and
86
+ * `runtime.windows.adopt` requires it at runtime. Typed as a bare `Function`
87
+ * (like `close`) so a test fake can assign a plain `vi.fn()` Mock — whose
88
+ * `Mock<Procedure | Constructable>` carries a construct signature that a strict
89
+ * call signature rejects. Callers invoke it as `win.prependListener('closed', fn)`.
90
+ */
91
+ prependListener?: Function;
92
+ }
93
+ export interface MinimalWebContentsView {
94
+ readonly webContents: MinimalWebContentsLike;
95
+ setBounds(rect: MinimalRect): void;
96
+ }
97
+ /**
98
+ * Minimal `app` surface the framework needs to own the Electron process
99
+ * lifecycle as the sole orchestrator. Only methods the framework calls on the
100
+ * host's behalf belong here — host-specific app APIs (dock, getPath, …) go
101
+ * through `runtime.electron.app`. Injected via {@link MinimalElectron.app};
102
+ * the framework never `import`s 'electron'.
103
+ */
104
+ export interface MinimalApp {
105
+ /** Gating: the framework must `await` this before constructing any window. */
106
+ whenReady(): Promise<void>;
107
+ /** Pre-ready: set the default app/dock name (best-effort). */
108
+ setName(name: string): void;
109
+ /** Process lifecycle: drives shutdown / quit-on-all-closed. */
110
+ on(event: 'will-quit' | 'before-quit' | 'window-all-closed' | 'second-instance', listener: (e?: {
111
+ preventDefault(): void;
112
+ }) => void): MinimalApp;
113
+ quit(): void;
114
+ /** Opt-in single-instance; only called when `config.app.singleInstance`. */
115
+ requestSingleInstanceLock?(): boolean;
116
+ }
117
+ export interface MinimalElectron {
118
+ readonly BrowserWindow: new (opts?: MinimalBrowserWindowOptions) => MinimalBrowserWindow;
119
+ readonly WebContentsView: new (opts?: {
120
+ webPreferences?: {
121
+ preload?: string;
122
+ };
123
+ }) => MinimalWebContentsView;
124
+ /**
125
+ * Optional `app` surface. Present on the production path (lazy-imported real
126
+ * electron) and when tests exercise the whenReady gate; absent in legacy
127
+ * fakes that only assemble windows — the gate is then skipped (back-compat).
128
+ */
129
+ readonly app?: MinimalApp;
130
+ }
131
+ //# sourceMappingURL=electron-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electron-types.d.ts","sourceRoot":"","sources":["../../src/internal/electron-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Disposable, HostEvent, JsonValue } from '../types.js';
2
+ /**
3
+ * Framework-internal event bus。
4
+ *
5
+ * 责任:
6
+ * - `bindDeclaredEvents(events)`:在 Bind 阶段调;对每个声明的 HostEvent,调用
7
+ * framework binder,让该 event 的 `publish()` 走 bus。
8
+ * - `publish(name, payload)`:通知 `subscribe(name, ...)` 注册的所有 listener。
9
+ * - `subscribe(name, listener)`:返回 `Disposable`,dispose 后停止通知。
10
+ * - `unbindAll()`:解绑所有 declared event publisher + 清空 subscribers。
11
+ *
12
+ * @internal
13
+ */
14
+ export declare class EventBus {
15
+ private readonly subscribers;
16
+ private readonly allSubscribers;
17
+ private readonly inFlight;
18
+ private boundEvents;
19
+ bindDeclaredEvents(events: readonly HostEvent<JsonValue>[]): void;
20
+ publish(name: string, payload: JsonValue): void;
21
+ subscribe(name: string, listener: (payload: JsonValue) => void): Disposable;
22
+ /**
23
+ * 订阅所有 channel 的 publish —— framework wire-transport 把 declared
24
+ * HostEvent.publish() 桥到 webContents.send 时用。
25
+ */
26
+ subscribeAll(listener: (name: string, payload: JsonValue) => void): Disposable;
27
+ unbindAll(): void;
28
+ }
29
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/internal/event-bus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEnE;;;;;;;;;;;GAWG;AACH,qBAAa,QAAQ;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuD;IACnF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwD;IACvF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,WAAW,CAA6B;IAEhD,kBAAkB,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC,SAAS,CAAC,EAAE,GAAG,IAAI;IAOjE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI;IAwC/C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,GAAG,UAAU;IAe3E;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,KAAK,IAAI,GAAG,UAAU;IAS9E,SAAS,IAAI,IAAI;CAMjB"}
@@ -0,0 +1,25 @@
1
+ import type { BrowserWindow } from 'electron';
2
+ import type { Disposable, JsonValue, MaybePromise, TypedIpcRegistry } from '../types.js';
3
+ /**
4
+ * In-memory `TypedIpcRegistry` impl —— Phase 2 不接真 Electron `ipcMain`,提
5
+ * 供 main-process 同进程 `handle` / `on` / `send` / `invoke` 的 fake。
6
+ *
7
+ * - `handle` / `invoke` 与 `on` / `send` 是**独立 channel space**:send 只触
8
+ * 发 on listener;invoke 只调 handle handler;同 channel 名互不影响。
9
+ * - audience / validator option Phase 2 不强制,仅 accept;Phase 3 接 trusted
10
+ * sender set 时再实施。
11
+ *
12
+ * @internal
13
+ */
14
+ export declare class InMemoryTypedIpcRegistry implements TypedIpcRegistry {
15
+ private readonly handlers;
16
+ private readonly listeners;
17
+ handle<A extends JsonValue[], R extends JsonValue>(channel: string, handler: (...args: A) => MaybePromise<R>, _options?: {
18
+ validator?: (args: unknown[]) => A;
19
+ }): Disposable;
20
+ on<P extends JsonValue>(channel: string, listener: (payload: P) => void): Disposable;
21
+ send(_target: 'mainWindow' | BrowserWindow, channel: string, payload: JsonValue): void;
22
+ /** @internal main-internal helper used by Phase 2 setup tests / framework */
23
+ invoke<R extends JsonValue>(channel: string, ...args: JsonValue[]): Promise<R>;
24
+ }
25
+ //# sourceMappingURL=ipc-registry-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipc-registry-memory.d.ts","sourceRoot":"","sources":["../../src/internal/ipc-registry-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EACX,UAAU,EACV,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,MAAM,aAAa,CAAA;AAKpB;;;;;;;;;;GAUG;AACH,qBAAa,wBAAyB,YAAW,gBAAgB;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAEhE,MAAM,CAAC,CAAC,SAAS,SAAS,EAAE,EAAE,CAAC,SAAS,SAAS,EAChD,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,EACxC,QAAQ,CAAC,EAAE;QACV,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;KAClC,GACC,UAAU;IAab,EAAE,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,GAAG,UAAU;IAgBpF,IAAI,CACH,OAAO,EAAE,YAAY,GAAG,aAAa,EACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,GAChB,IAAI;IAcP,6EAA6E;IACvE,MAAM,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;CAMpF"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Lifecycle phase machine — see workbench-model.md §5.
3
+ *
4
+ * 8 个 phase 严格递增,无跳跃:
5
+ * init → bind → setup → ready → drain → cleanup → destroy → quit
6
+ *
7
+ * @internal
8
+ */
9
+ export type LifecyclePhase = 'init' | 'bind' | 'setup' | 'ready' | 'drain' | 'cleanup' | 'destroy' | 'quit';
10
+ export declare class LifecyclePhaseError extends Error {
11
+ readonly from: LifecyclePhase;
12
+ readonly to: LifecyclePhase;
13
+ constructor(from: LifecyclePhase, to: LifecyclePhase);
14
+ }
15
+ export declare function phaseOrder(p: LifecyclePhase): number;
16
+ export declare class LifecycleManager {
17
+ current: LifecyclePhase;
18
+ enter(to: LifecyclePhase): void;
19
+ assertAtLeast(p: LifecyclePhase): void;
20
+ /**
21
+ * Abnormal-path setter:setup throw / 紧急 shutdown 跳过中间 phase 直达
22
+ * 指定 teardown phase。不做顺序校验,仅 framework 内部使用。
23
+ *
24
+ * @internal
25
+ */
26
+ _force(to: LifecyclePhase): void;
27
+ }
28
+ //# sourceMappingURL=lifecycle-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../../src/internal/lifecycle-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,cAAc,GACvB,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,SAAS,GACT,MAAM,CAAA;AAaT,qBAAa,mBAAoB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAA;gBACf,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc;CAMpD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAIpD;AAED,qBAAa,gBAAgB;IAC5B,OAAO,EAAE,cAAc,CAAS;IAEhC,KAAK,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAS/B,aAAa,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI;IAQtC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;CAGhC"}