@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.
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/client/index.d.ts +20 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +310 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/layout-client.d.ts +51 -0
- package/dist/client/layout-client.d.ts.map +1 -0
- package/dist/electron-deck-DfFKeFEC.js +2295 -0
- package/dist/electron-deck-DfFKeFEC.js.map +1 -0
- package/dist/electron-deck.d.ts +57 -0
- package/dist/electron-deck.d.ts.map +1 -0
- package/dist/errors-NE2ig7L5.js +53 -0
- package/dist/errors-NE2ig7L5.js.map +1 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/events.d.ts +14 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/host/capability.d.ts +50 -0
- package/dist/host/capability.d.ts.map +1 -0
- package/dist/host/control-bus.d.ts +92 -0
- package/dist/host/control-bus.d.ts.map +1 -0
- package/dist/host/index.d.ts +42 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/internal/deck-app.d.ts +415 -0
- package/dist/internal/deck-app.d.ts.map +1 -0
- package/dist/internal/electron-types.d.ts +131 -0
- package/dist/internal/electron-types.d.ts.map +1 -0
- package/dist/internal/event-bus.d.ts +29 -0
- package/dist/internal/event-bus.d.ts.map +1 -0
- package/dist/internal/ipc-registry-memory.d.ts +25 -0
- package/dist/internal/ipc-registry-memory.d.ts.map +1 -0
- package/dist/internal/lifecycle-manager.d.ts +28 -0
- package/dist/internal/lifecycle-manager.d.ts.map +1 -0
- package/dist/internal/resource-registry.d.ts +9 -0
- package/dist/internal/resource-registry.d.ts.map +1 -0
- package/dist/internal/trust-set.d.ts +53 -0
- package/dist/internal/trust-set.d.ts.map +1 -0
- package/dist/internal/wire-transport.d.ts +159 -0
- package/dist/internal/wire-transport.d.ts.map +1 -0
- package/dist/main/compositor.d.ts +123 -0
- package/dist/main/compositor.d.ts.map +1 -0
- package/dist/main/connection.d.ts +37 -0
- package/dist/main/connection.d.ts.map +1 -0
- package/dist/main/debug-tap.d.ts +55 -0
- package/dist/main/debug-tap.d.ts.map +1 -0
- package/dist/main/disposable.d.ts +18 -0
- package/dist/main/disposable.d.ts.map +1 -0
- package/dist/main/index.d.ts +16 -0
- package/dist/main/index.d.ts.map +1 -0
- package/dist/main/index.js +152 -0
- package/dist/main/index.js.map +1 -0
- package/dist/main/logger.d.ts +15 -0
- package/dist/main/logger.d.ts.map +1 -0
- package/dist/main/scope.d.ts +52 -0
- package/dist/main/scope.d.ts.map +1 -0
- package/dist/main/view-handle.d.ts +131 -0
- package/dist/main/view-handle.d.ts.map +1 -0
- package/dist/preload/index.cjs +105 -0
- package/dist/preload/index.cjs.map +1 -0
- package/dist/preload/index.d.ts +43 -0
- package/dist/preload/index.d.ts.map +1 -0
- package/dist/preload/index.js +80 -0
- package/dist/preload/index.js.map +1 -0
- package/dist/protocol-BJqo-vP2.js +23 -0
- package/dist/protocol-BJqo-vP2.js.map +1 -0
- package/dist/shared/protocol.d.ts +76 -0
- package/dist/shared/protocol.d.ts.map +1 -0
- package/dist/types.d.ts +504 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/view-handle-CR-yWNfr.js +701 -0
- package/dist/view-handle-CR-yWNfr.js.map +1 -0
- 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"}
|