@dimina-kit/electron-deck 0.1.0-dev.20260702175315 → 0.1.0-dev.20260703051110
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 +45 -15
- package/dist/client/index.d.ts +8 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/dock-react/dock-view.d.ts +43 -19
- package/dist/dock-react/dock-view.d.ts.map +1 -1
- package/dist/dock-react/drag-redock.d.ts +18 -0
- package/dist/dock-react/drag-redock.d.ts.map +1 -1
- package/dist/dock-react/group-view.d.ts +18 -0
- package/dist/dock-react/group-view.d.ts.map +1 -0
- package/dist/dock-react/index.d.ts +3 -2
- package/dist/dock-react/index.d.ts.map +1 -1
- package/dist/dock-react/index.js +384 -289
- package/dist/dock-react/index.js.map +1 -1
- package/dist/dock-react/panel-body.d.ts +24 -0
- package/dist/dock-react/panel-body.d.ts.map +1 -0
- package/dist/dock-react/split-sizing.d.ts +109 -0
- package/dist/dock-react/split-sizing.d.ts.map +1 -0
- package/dist/dock-react/split-view.d.ts +15 -0
- package/dist/dock-react/split-view.d.ts.map +1 -0
- package/dist/{electron-deck-B9fun93z.js → electron-deck-Bi-u-Aze.js} +175 -79
- package/dist/electron-deck-Bi-u-Aze.js.map +1 -0
- package/dist/electron-deck.d.ts +5 -0
- package/dist/electron-deck.d.ts.map +1 -1
- package/dist/events.d.ts +1 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/host/index.d.ts +5 -0
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/deck-app-view-move.d.ts +17 -0
- package/dist/internal/deck-app-view-move.d.ts.map +1 -0
- package/dist/internal/deck-app.d.ts +58 -1
- package/dist/internal/deck-app.d.ts.map +1 -1
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/model.d.ts.map +1 -1
- package/dist/layout/mutations.d.ts.map +1 -1
- package/dist/layout/tree-query.d.ts +18 -0
- package/dist/layout/tree-query.d.ts.map +1 -0
- package/dist/layout/types.d.ts +11 -9
- package/dist/layout/types.d.ts.map +1 -1
- package/dist/{layout-ByOiOdnc.js → layout-D-LYJIpM.js} +57 -41
- package/dist/layout-D-LYJIpM.js.map +1 -0
- package/dist/main/compositor.d.ts.map +1 -1
- package/dist/main/index.js +1 -1
- package/dist/main/view-handle.d.ts.map +1 -1
- package/dist/preload/index.cjs +5 -0
- package/dist/preload/index.cjs.map +1 -1
- package/dist/preload/index.d.ts +5 -0
- package/dist/preload/index.d.ts.map +1 -1
- package/dist/preload/index.js +5 -0
- package/dist/preload/index.js.map +1 -1
- package/dist/types.d.ts +32 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/{view-handle-DDhpBuW-.js → view-handle-zav25k3-.js} +141 -86
- package/dist/view-handle-zav25k3-.js.map +1 -0
- package/package.json +11 -5
- package/dist/electron-deck-B9fun93z.js.map +0 -1
- package/dist/layout-ByOiOdnc.js.map +0 -1
- package/dist/view-handle-DDhpBuW-.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-view.d.ts","sourceRoot":"","sources":["../../src/dock-react/split-view.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtE,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAU/D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAW/D,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,SAAS,CAAA;IACf,GAAG,EAAE,aAAa,CAAA;CAClB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAmR1D"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as DeckRemoteError, r as EventNotBoundError } from "./errors-NE2ig7L5.js";
|
|
2
2
|
import { i as DeckChannel, t as BRIDGE_PROTOCOL_VERSION } from "./protocol-B6TOT9AP.js";
|
|
3
|
-
import { i as createScope, r as createCompositor, t as createViewHandle } from "./view-handle-
|
|
4
|
-
import { i as reconcile, n as dispatchOps, r as createInitialState, t as cleanSnapshot } from "./layout-
|
|
3
|
+
import { i as createScope, r as createCompositor, t as createViewHandle } from "./view-handle-zav25k3-.js";
|
|
4
|
+
import { i as reconcile, n as dispatchOps, r as createInitialState, t as cleanSnapshot } from "./layout-D-LYJIpM.js";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
//#region src/events.ts
|
|
7
7
|
var internals = /* @__PURE__ */ new WeakMap();
|
|
@@ -33,6 +33,7 @@ var HostEventImpl = class {
|
|
|
33
33
|
} };
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
+
/** @experimental No production consumer yet — see the note on {@link HostEvent}. */
|
|
36
37
|
function defineEvent(name) {
|
|
37
38
|
if (typeof name !== "string" || name.length === 0) throw new TypeError("defineEvent(name): name must be a non-empty string");
|
|
38
39
|
return new HostEventImpl(name);
|
|
@@ -62,6 +63,37 @@ function unbindHostEvent(event) {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
//#endregion
|
|
66
|
+
//#region src/internal/deck-app-view-move.ts
|
|
67
|
+
/**
|
|
68
|
+
* `DeckViewHandle.moveTo` failure cleanup. `inner.moveTo` does the atomic
|
|
69
|
+
* native Compositor migration + Scope adopt/rollback; when it rejects, the
|
|
70
|
+
* Compositor's own rollback only reverts state it TRACKED (`order`) — a
|
|
71
|
+
* native `addChildView` that throws MID-APPLY can leave the WCV attached to
|
|
72
|
+
* the dest window's `contentView` with no tracked record to remove it. This
|
|
73
|
+
* balances that leak with an explicit dest detach, then undoes the dest
|
|
74
|
+
* substrate registration made before the move attempt.
|
|
75
|
+
*
|
|
76
|
+
* Each step is independently guarded (its own try/catch) so a cleanup
|
|
77
|
+
* failure in one step never masks the other step or the ORIGINAL move
|
|
78
|
+
* error — `originalErr` is always rethrown, never shadowed.
|
|
79
|
+
*/
|
|
80
|
+
function cleanupDestOnMoveToFailure(destWin, destSub, wcv, viewId, originalErr) {
|
|
81
|
+
try {
|
|
82
|
+
if (!destWin.isDestroyed()) {
|
|
83
|
+
const destChildren = destWin.contentView.children;
|
|
84
|
+
if (destChildren == null || destChildren.includes(wcv)) destWin.contentView.removeChildView(wcv);
|
|
85
|
+
}
|
|
86
|
+
} catch (cleanupErr) {
|
|
87
|
+
console.error("[electron-deck] moveTo dest detach failed (original error rethrown):", cleanupErr);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
destSub.unregisterView(viewId);
|
|
91
|
+
} catch (cleanupErr) {
|
|
92
|
+
console.error("[electron-deck] moveTo dest unregister failed (original error rethrown):", cleanupErr);
|
|
93
|
+
}
|
|
94
|
+
throw originalErr;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
65
97
|
//#region src/internal/event-bus.ts
|
|
66
98
|
/**
|
|
67
99
|
* Framework-internal event bus。
|
|
@@ -949,42 +981,76 @@ var DeckApp = class {
|
|
|
949
981
|
if (this.startCalled) throw new Error("DeckApp.start() already called");
|
|
950
982
|
this.startCalled = true;
|
|
951
983
|
validateConfig(this.config);
|
|
952
|
-
|
|
953
|
-
if (this.options.electron && hasWebviewContent && !this.options.wireTransport) throw new Error("DeckAppOptions: wireTransport.ipcMain is required when config has toolbar or windows");
|
|
984
|
+
this.assertWireTransportPresentForWebviewContent();
|
|
954
985
|
const app = this.options.electron?.app;
|
|
955
986
|
if (app) {
|
|
956
|
-
if (this.
|
|
957
|
-
if (!app.requestSingleInstanceLock()) {
|
|
958
|
-
try {
|
|
959
|
-
app.quit();
|
|
960
|
-
} catch {}
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
if (this.options.backend?.beforeReady) await this.options.backend.beforeReady(app);
|
|
965
|
-
else if (this.config.app?.name) try {
|
|
966
|
-
app.setName(this.config.app.name);
|
|
967
|
-
} catch {}
|
|
968
|
-
await app.whenReady();
|
|
969
|
-
this.bindAppLifecycle(app);
|
|
987
|
+
if (!await this.runPreReadyGate(app)) return;
|
|
970
988
|
}
|
|
971
989
|
try {
|
|
972
|
-
this.
|
|
973
|
-
this.bindDeclarativeFields();
|
|
974
|
-
this.assembleElectron();
|
|
975
|
-
this.bindWireTransport();
|
|
976
|
-
this.loadAssembledSources();
|
|
977
|
-
this.lifecycle.enter("setup");
|
|
978
|
-
this._runtime = this.buildRuntime();
|
|
979
|
-
if (this.options.backend) await this.options.backend.assemble(this._runtime);
|
|
980
|
-
if (this.config.setup) await this.config.setup(this._runtime);
|
|
981
|
-
this.lifecycle.enter("ready");
|
|
990
|
+
await this.runBindAndSetupPhases();
|
|
982
991
|
} catch (err) {
|
|
983
992
|
await this.cleanupOnError();
|
|
984
993
|
throw err;
|
|
985
994
|
}
|
|
986
995
|
}
|
|
987
996
|
/**
|
|
997
|
+
* half-state guard: electron + (toolbar | windows) without wireTransport
|
|
998
|
+
* would leave the host with webviews that can never reach back via
|
|
999
|
+
* __electron-deck:invoke. Reject early with a clear msg.
|
|
1000
|
+
*/
|
|
1001
|
+
assertWireTransportPresentForWebviewContent() {
|
|
1002
|
+
const hasWebviewContent = !!(this.config.toolbar || this.config.windows);
|
|
1003
|
+
if (this.options.electron && hasWebviewContent && !this.options.wireTransport) throw new Error("DeckAppOptions: wireTransport.ipcMain is required when config has toolbar or windows");
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Pre-`whenReady` sequencing: single-instance lock (opt-in, must quit
|
|
1007
|
+
* BEFORE any other side effect) → `backend.beforeReady` (or the framework's
|
|
1008
|
+
* best-effort `app.setName`) → `await app.whenReady()` → app-level lifecycle
|
|
1009
|
+
* bindings. Returns `false` when the single-instance lock was lost (the
|
|
1010
|
+
* second instance already called `app.quit()` — `start()` must stop with
|
|
1011
|
+
* phase still `init`), `true` otherwise.
|
|
1012
|
+
*/
|
|
1013
|
+
async runPreReadyGate(app) {
|
|
1014
|
+
if (!this.acquireSingleInstanceLock(app)) return false;
|
|
1015
|
+
if (this.options.backend?.beforeReady) await this.options.backend.beforeReady(app);
|
|
1016
|
+
else if (this.config.app?.name) try {
|
|
1017
|
+
app.setName(this.config.app.name);
|
|
1018
|
+
} catch {}
|
|
1019
|
+
await app.whenReady();
|
|
1020
|
+
this.bindAppLifecycle(app);
|
|
1021
|
+
return true;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Single-instance gate (opt-in): a second instance must quit BEFORE any
|
|
1025
|
+
* side effect (beforeReady bootstrap / whenReady / window). Returns `true`
|
|
1026
|
+
* when the caller should continue (not opted in, or this instance holds the
|
|
1027
|
+
* lock); `false` after the losing instance has called `app.quit()`.
|
|
1028
|
+
*/
|
|
1029
|
+
acquireSingleInstanceLock(app) {
|
|
1030
|
+
if (!this.config.app?.singleInstance || typeof app.requestSingleInstanceLock !== "function") return true;
|
|
1031
|
+
if (app.requestSingleInstanceLock()) return true;
|
|
1032
|
+
try {
|
|
1033
|
+
app.quit();
|
|
1034
|
+
} catch {}
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
/** Init → Bind → Setup → Ready. Assembly/bind must run AFTER the
|
|
1038
|
+
* whenReady gate (handled by the caller); loadURL/loadFile must run
|
|
1039
|
+
* AFTER bindWireTransport (a preload calling the bridge before its
|
|
1040
|
+
* ipcMain handler is registered would see a "no handler" reject). */
|
|
1041
|
+
async runBindAndSetupPhases() {
|
|
1042
|
+
this.lifecycle.enter("bind");
|
|
1043
|
+
this.bindDeclarativeFields();
|
|
1044
|
+
this.assembleElectron();
|
|
1045
|
+
this.bindWireTransport();
|
|
1046
|
+
this.loadAssembledSources();
|
|
1047
|
+
this.lifecycle.enter("setup");
|
|
1048
|
+
this._runtime = this.buildRuntime();
|
|
1049
|
+
if (this.options.backend) await this.options.backend.assemble(this._runtime);
|
|
1050
|
+
if (this.config.setup) await this.config.setup(this._runtime);
|
|
1051
|
+
this.lifecycle.enter("ready");
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
988
1054
|
* Process-level Electron lifecycle bindings (post-whenReady, independent of
|
|
989
1055
|
* `ownsWindows` — these are app events, not window events):
|
|
990
1056
|
* - `will-quit` → framework teardown (idempotent via `shutdownPromise`).
|
|
@@ -1180,8 +1246,8 @@ var DeckApp = class {
|
|
|
1180
1246
|
* registration) — windows with no per-window deciders fall through to Electron's
|
|
1181
1247
|
* default close (the framework does not preventDefault).
|
|
1182
1248
|
*/
|
|
1183
|
-
armSubWindowCloseMachine(win, controlWc) {
|
|
1184
|
-
|
|
1249
|
+
armSubWindowCloseMachine(win, controlWc, windowScope) {
|
|
1250
|
+
const onClose = (e) => {
|
|
1185
1251
|
const rec = this.windowRegistrations.get(controlWc);
|
|
1186
1252
|
if (!rec || rec.deciders.length === 0) return;
|
|
1187
1253
|
e?.preventDefault?.();
|
|
@@ -1204,7 +1270,19 @@ var DeckApp = class {
|
|
|
1204
1270
|
rec.closing = null;
|
|
1205
1271
|
if (!win.isDestroyed()) win.destroy();
|
|
1206
1272
|
});
|
|
1207
|
-
}
|
|
1273
|
+
};
|
|
1274
|
+
win.on("close", onClose);
|
|
1275
|
+
windowScope.own(() => this.removeWindowListener(win, "close", onClose));
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Remove a previously-registered window event listener (Electron's
|
|
1279
|
+
* `removeListener`/`off`). Tolerates minimal test fakes that expose neither by
|
|
1280
|
+
* degrading to a no-op — the caller's ownership contract is best-effort cleanup.
|
|
1281
|
+
*/
|
|
1282
|
+
removeWindowListener(win, event, listener) {
|
|
1283
|
+
const w = win;
|
|
1284
|
+
const remove = w.removeListener ?? w.off;
|
|
1285
|
+
if (typeof remove === "function") remove.call(win, event, listener);
|
|
1208
1286
|
}
|
|
1209
1287
|
/**
|
|
1210
1288
|
* @internal Lifetime accessors. The live shadow map mirroring `trackedWindows`,
|
|
@@ -1519,6 +1597,16 @@ var DeckApp = class {
|
|
|
1519
1597
|
this.warnedInvalidKeepAliveMax = true;
|
|
1520
1598
|
console.warn(`[electron-deck] runtime.view keepAlive.max must be a non-negative integer (got: ${max}); the view is NOT keep-alive-managed (no eviction).`);
|
|
1521
1599
|
}
|
|
1600
|
+
/** Drop `viewId` from `groupKey`'s HIDDEN list only (the group's `handles`
|
|
1601
|
+
* entry stays — the view is still keep-alive-managed, just no longer
|
|
1602
|
+
* evictable while visible). Idempotent: a no-op when the view isn't on the
|
|
1603
|
+
* hidden list or the group doesn't exist. */
|
|
1604
|
+
dropViewFromKeepAliveHidden(groupKey, viewId) {
|
|
1605
|
+
const group = this.keepAliveGroups.get(groupKey);
|
|
1606
|
+
if (!group) return;
|
|
1607
|
+
const i = group.hidden.indexOf(viewId);
|
|
1608
|
+
if (i >= 0) group.hidden.splice(i, 1);
|
|
1609
|
+
}
|
|
1522
1610
|
constructWindow(opts, autoTrust, deferLoad = false) {
|
|
1523
1611
|
const electron = this.options.electron;
|
|
1524
1612
|
if (!electron) throw new Error("DeckApp.constructWindow called without electron injection");
|
|
@@ -1550,7 +1638,7 @@ var DeckApp = class {
|
|
|
1550
1638
|
this.bindNavigationGrantReset(wc);
|
|
1551
1639
|
}
|
|
1552
1640
|
this.buildDeckWindow(win, wc, windowScope);
|
|
1553
|
-
this.armSubWindowCloseMachine(win, wc);
|
|
1641
|
+
this.armSubWindowCloseMachine(win, wc, windowScope);
|
|
1554
1642
|
if (!deferLoad) this.safeLoad(win.webContents, opts.source);
|
|
1555
1643
|
return win;
|
|
1556
1644
|
}
|
|
@@ -1593,10 +1681,11 @@ var DeckApp = class {
|
|
|
1593
1681
|
};
|
|
1594
1682
|
if (typeof win.prependListener === "function") win.prependListener("closed", revoke);
|
|
1595
1683
|
else win.on("closed", revoke);
|
|
1684
|
+
windowScope.own(() => this.removeWindowListener(win, "closed", revoke));
|
|
1596
1685
|
this.admitTrust(wc, windowScope);
|
|
1597
1686
|
this.bindNavigationGrantReset(wc);
|
|
1598
1687
|
this.buildDeckWindow(win, wc, windowScope);
|
|
1599
|
-
this.armSubWindowCloseMachine(win, wc);
|
|
1688
|
+
this.armSubWindowCloseMachine(win, wc, windowScope);
|
|
1600
1689
|
windowScope.own(() => {
|
|
1601
1690
|
this.windowSubstrates.delete(wc);
|
|
1602
1691
|
this.adoptedWindows.delete(wc);
|
|
@@ -1902,6 +1991,12 @@ var DeckApp = class {
|
|
|
1902
1991
|
this.bindNavigationGrantReset(wc);
|
|
1903
1992
|
return lease;
|
|
1904
1993
|
}
|
|
1994
|
+
const adopted = this.wcRecords.get(wc);
|
|
1995
|
+
if (adopted) {
|
|
1996
|
+
const lease = this.admitTrust(wc, adopted.windowScope);
|
|
1997
|
+
this.bindNavigationGrantReset(wc);
|
|
1998
|
+
return lease;
|
|
1999
|
+
}
|
|
1905
2000
|
return this._trustWebContents(wc);
|
|
1906
2001
|
},
|
|
1907
2002
|
adopt: (win, opts) => {
|
|
@@ -2042,30 +2137,11 @@ var DeckApp = class {
|
|
|
2042
2137
|
rehome: moveOpts.rehome
|
|
2043
2138
|
});
|
|
2044
2139
|
} catch (e) {
|
|
2045
|
-
|
|
2046
|
-
if (!destWin.isDestroyed()) {
|
|
2047
|
-
const destChildren = destWin.contentView.children;
|
|
2048
|
-
if (destChildren == null || destChildren.includes(wcv)) destWin.contentView.removeChildView(wcv);
|
|
2049
|
-
}
|
|
2050
|
-
} catch (cleanupErr) {
|
|
2051
|
-
console.error("[electron-deck] moveTo dest detach failed (original error rethrown):", cleanupErr);
|
|
2052
|
-
}
|
|
2053
|
-
try {
|
|
2054
|
-
destSub.unregisterView(viewId);
|
|
2055
|
-
} catch (cleanupErr) {
|
|
2056
|
-
console.error("[electron-deck] moveTo dest unregister failed (original error rethrown):", cleanupErr);
|
|
2057
|
-
}
|
|
2058
|
-
throw e;
|
|
2140
|
+
cleanupDestOnMoveToFailure(destWin, destSub, wcv, viewId, e);
|
|
2059
2141
|
}
|
|
2060
2142
|
if (srcSub && srcSub !== destSub) srcSub.unregisterView(viewId);
|
|
2061
2143
|
placedSubstrate = destSub;
|
|
2062
|
-
if (groupKey)
|
|
2063
|
-
const group = this.keepAliveGroups.get(groupKey);
|
|
2064
|
-
if (group) {
|
|
2065
|
-
const hi = group.hidden.indexOf(viewId);
|
|
2066
|
-
if (hi >= 0) group.hidden.splice(hi, 1);
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2144
|
+
if (groupKey) this.dropViewFromKeepAliveHidden(groupKey, viewId);
|
|
2069
2145
|
if (slotToken) {
|
|
2070
2146
|
this.slotTokens.delete(slotToken);
|
|
2071
2147
|
slotToken = void 0;
|
|
@@ -2087,7 +2163,9 @@ var DeckApp = class {
|
|
|
2087
2163
|
while (keepAlive && group.hidden.length > keepAlive.max) {
|
|
2088
2164
|
const victimId = group.hidden.shift();
|
|
2089
2165
|
const victim = group.handles.get(victimId);
|
|
2090
|
-
if (victim) victim.dispose()
|
|
2166
|
+
if (victim) victim.dispose().catch((e) => {
|
|
2167
|
+
console.error("[electron-deck] keepAlive eviction dispose failed:", e);
|
|
2168
|
+
});
|
|
2091
2169
|
}
|
|
2092
2170
|
}
|
|
2093
2171
|
}
|
|
@@ -2209,6 +2287,11 @@ async function electronDeck(config, options) {
|
|
|
2209
2287
|
*
|
|
2210
2288
|
* Invalid config throws a `TypeError` synchronously (matching `electronDeck`'s
|
|
2211
2289
|
* validate-first contract) — the error surfaces, never silently deadlocks.
|
|
2290
|
+
*
|
|
2291
|
+
* `@experimental` No production consumer yet — the only caller in this repo is
|
|
2292
|
+
* `packages/devtools/spike/popout/harness.mjs`; devtools' real entry
|
|
2293
|
+
* (`launch.ts`) uses `electronDeck({ backend })` instead. Contract may change
|
|
2294
|
+
* until a second real consumer adopts it.
|
|
2212
2295
|
*/
|
|
2213
2296
|
function startElectronDeck(config, options) {
|
|
2214
2297
|
validateConfig(config);
|
|
@@ -2280,37 +2363,50 @@ function buildAppOptions(electron, ipcMain, opts) {
|
|
|
2280
2363
|
*/
|
|
2281
2364
|
function validateConfig(config) {
|
|
2282
2365
|
if (config === null || typeof config !== "object") throw new TypeError("electronDeck(config): config must be an object");
|
|
2283
|
-
|
|
2284
|
-
const backend = config.backend;
|
|
2285
|
-
if (backend === null || typeof backend !== "object" || typeof backend.assemble !== "function") throw new TypeError("config.backend must be a RuntimeBackend (an object with an assemble() function)");
|
|
2286
|
-
}
|
|
2366
|
+
validateBackendField(config.backend);
|
|
2287
2367
|
if (config.simulatorApis !== void 0) validateHandlerMap("simulatorApis", config.simulatorApis);
|
|
2288
2368
|
if (config.hostServices !== void 0) validateHandlerMap("hostServices", config.hostServices);
|
|
2289
|
-
if (config.events !== void 0)
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
if (config.toolbar !== void 0) {
|
|
2299
|
-
const { source, preloadPath, height } = config.toolbar;
|
|
2300
|
-
if (source === null || typeof source !== "object") throw new TypeError("toolbar.source must be { url } or { file }");
|
|
2301
|
-
const hasUrl = "url" in source && typeof source.url === "string";
|
|
2302
|
-
const hasFile = "file" in source && typeof source.file === "string";
|
|
2303
|
-
if (hasUrl && hasFile) throw new TypeError("toolbar.source must be either { url } or { file }, not both");
|
|
2304
|
-
if (!hasUrl && !hasFile) throw new TypeError("toolbar.source must be { url } or { file }");
|
|
2305
|
-
if (typeof preloadPath !== "string" || preloadPath.length === 0) throw new TypeError("toolbar.preloadPath is required and must be a non-empty string");
|
|
2306
|
-
if (typeof height !== "number" || !Number.isFinite(height) || height <= 0) throw new TypeError("toolbar.height is required and must be a positive finite number");
|
|
2307
|
-
}
|
|
2369
|
+
if (config.events !== void 0) validateEventsField(config.events);
|
|
2370
|
+
if (config.toolbar !== void 0) validateToolbarField(config.toolbar);
|
|
2371
|
+
}
|
|
2372
|
+
function validateBackendField(backend) {
|
|
2373
|
+
if (backend === void 0) return;
|
|
2374
|
+
const candidate = backend;
|
|
2375
|
+
if (candidate === null || typeof candidate !== "object" || typeof candidate.assemble !== "function") throw new TypeError("config.backend must be a RuntimeBackend (an object with an assemble() function)");
|
|
2308
2376
|
}
|
|
2309
2377
|
function validateHandlerMap(fieldName, value) {
|
|
2310
2378
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${fieldName} must be an object of handlers`);
|
|
2311
2379
|
for (const [name, handler] of Object.entries(value)) if (typeof handler !== "function") throw new TypeError(`${fieldName}["${name}"] must be a function`);
|
|
2312
2380
|
}
|
|
2381
|
+
function validateEventsField(events) {
|
|
2382
|
+
if (!Array.isArray(events)) throw new TypeError("events must be an array of HostEvent (from defineEvent())");
|
|
2383
|
+
assertAllHostEvents(events);
|
|
2384
|
+
assertUniqueEventNames(events);
|
|
2385
|
+
}
|
|
2386
|
+
/** duck-typed 事件形状会被拒绝——只接受 `defineEvent()` 产出的真实 HostEvent,避免 bind-time 才炸。 */
|
|
2387
|
+
function assertAllHostEvents(events) {
|
|
2388
|
+
for (const ev of events) if (!isHostEvent(ev)) throw new TypeError("events: every entry must be a HostEvent produced by defineEvent() — duck-typed shapes are rejected to avoid bind-time failures");
|
|
2389
|
+
}
|
|
2390
|
+
function assertUniqueEventNames(events) {
|
|
2391
|
+
const names = /* @__PURE__ */ new Set();
|
|
2392
|
+
for (const ev of events) {
|
|
2393
|
+
if (names.has(ev.name)) throw new Error(`events: duplicate HostEvent name "${ev.name}"`);
|
|
2394
|
+
names.add(ev.name);
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
function validateToolbarField(toolbar) {
|
|
2398
|
+
assertToolbarSourceShape(toolbar.source);
|
|
2399
|
+
if (typeof toolbar.preloadPath !== "string" || toolbar.preloadPath.length === 0) throw new TypeError("toolbar.preloadPath is required and must be a non-empty string");
|
|
2400
|
+
if (typeof toolbar.height !== "number" || !Number.isFinite(toolbar.height) || toolbar.height <= 0) throw new TypeError("toolbar.height is required and must be a positive finite number");
|
|
2401
|
+
}
|
|
2402
|
+
function assertToolbarSourceShape(source) {
|
|
2403
|
+
if (source === null || typeof source !== "object") throw new TypeError("toolbar.source must be { url } or { file }");
|
|
2404
|
+
const hasUrl = "url" in source && typeof source.url === "string";
|
|
2405
|
+
const hasFile = "file" in source && typeof source.file === "string";
|
|
2406
|
+
if (hasUrl && hasFile) throw new TypeError("toolbar.source must be either { url } or { file }, not both");
|
|
2407
|
+
if (!hasUrl && !hasFile) throw new TypeError("toolbar.source must be { url } or { file }");
|
|
2408
|
+
}
|
|
2313
2409
|
//#endregion
|
|
2314
2410
|
export { WireTransport as a, InMemoryTypedIpcRegistry as c, createControlBus as i, EventBus as l, startElectronDeck as n, createCapabilityRegistry as o, validateConfig as r, createTrustSet as s, electronDeck as t, defineEvent as u };
|
|
2315
2411
|
|
|
2316
|
-
//# sourceMappingURL=electron-deck-
|
|
2412
|
+
//# sourceMappingURL=electron-deck-Bi-u-Aze.js.map
|