@dimina-kit/devtools 0.4.0-dev.20260716214527 → 0.4.0-dev.20260717120050
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/dist/main/app/app.js +3 -0
- package/dist/main/index.bundle.js +79 -34
- package/dist/main/services/simulator-appdata/index.d.ts +6 -0
- package/dist/main/services/simulator-appdata/index.js +20 -1
- package/dist/main/utils/ipc-registry.d.ts +2 -1
- package/dist/main/utils/ipc-registry.js +30 -10
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/renderer/assets/index-U9F8MrtV.js +49 -0
- package/dist/renderer/assets/{input-7P0WbXyg.js → input-c2OcrrZy.js} +2 -2
- package/dist/renderer/assets/ipc-transport-Blac3sOj.css +1 -0
- package/dist/renderer/assets/{ipc-transport-DTEmLpxx.js → ipc-transport-CuDJ07aE.js} +2 -2
- package/dist/renderer/assets/{popover-DsOcyIS6.js → popover-kv2qspWz.js} +2 -2
- package/dist/renderer/assets/{select-Bzbhbg9Z.js → select-DKqzMtTh.js} +2 -2
- package/dist/renderer/assets/{settings-api-B1wVFAPB.js → settings-api-qpXKDRXt.js} +2 -2
- package/dist/renderer/assets/{settings-CR1TyBWu.js → settings-dJNKTGr8.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-CFbToT4Q.js → workbenchSettings-DQ_VP5-W.js} +2 -2
- package/dist/renderer/entries/main/index.html +6 -6
- package/dist/renderer/entries/popover/index.html +5 -5
- package/dist/renderer/entries/settings/index.html +5 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/service-host/appdata-set-data.cjs +39 -0
- package/dist/service-host/preload.cjs +21 -0
- package/dist/shared/ipc-channels.d.ts +9 -0
- package/dist/shared/ipc-channels.js +11 -0
- package/package.json +5 -5
- package/dist/renderer/assets/index-CZd3LyY8.js +0 -49
- package/dist/renderer/assets/ipc-transport-BGtDnv6p.css +0 -1
package/dist/main/app/app.js
CHANGED
|
@@ -537,6 +537,9 @@ export async function createDevtoolsRuntime(config = {}) {
|
|
|
537
537
|
const appDataService = setupSimulatorAppData(mainWindow.webContents, {
|
|
538
538
|
senderPolicy: context.senderPolicy,
|
|
539
539
|
getActiveAppId,
|
|
540
|
+
// AppData-panel edit write-back target: the service-host window owning
|
|
541
|
+
// the edited page bridge.
|
|
542
|
+
bridge: context.bridge,
|
|
540
543
|
});
|
|
541
544
|
// bridge-router feeds this via ctx.appData (service→render tap + evict).
|
|
542
545
|
context.appData = appDataService;
|
|
@@ -171,7 +171,6 @@ import { z } from "zod";
|
|
|
171
171
|
|
|
172
172
|
// src/main/utils/ipc-registry.ts
|
|
173
173
|
import { ipcMain } from "electron";
|
|
174
|
-
import { DisposableRegistry } from "@dimina-kit/electron-deck/main";
|
|
175
174
|
|
|
176
175
|
// src/main/utils/ipc-schema.ts
|
|
177
176
|
var IpcValidationError = class extends Error {
|
|
@@ -230,7 +229,13 @@ var IpcRegistry = class {
|
|
|
230
229
|
this.policy = policy;
|
|
231
230
|
}
|
|
232
231
|
policy;
|
|
233
|
-
|
|
232
|
+
// Every cleanup is a synchronous ipcMain.removeHandler/removeListener call,
|
|
233
|
+
// so dispose() can (and must) run them all before returning: callers
|
|
234
|
+
// `dispose()` without awaiting and rely on every channel being unregistered
|
|
235
|
+
// synchronously — an async registry would leave all but the first handler
|
|
236
|
+
// live until a later microtask.
|
|
237
|
+
cleanups = [];
|
|
238
|
+
_disposed = false;
|
|
234
239
|
handle(channel, fn) {
|
|
235
240
|
const policy = this.policy;
|
|
236
241
|
const guarded = policy ? async (event, ...args) => {
|
|
@@ -244,7 +249,7 @@ var IpcRegistry = class {
|
|
|
244
249
|
return fn(event, ...args);
|
|
245
250
|
} : fn;
|
|
246
251
|
ipcMain.handle(channel, guarded);
|
|
247
|
-
this.
|
|
252
|
+
this.cleanups.push(() => ipcMain.removeHandler(channel));
|
|
248
253
|
return this;
|
|
249
254
|
}
|
|
250
255
|
/**
|
|
@@ -282,9 +287,7 @@ var IpcRegistry = class {
|
|
|
282
287
|
}
|
|
283
288
|
};
|
|
284
289
|
ipcMain.on(channel, listener);
|
|
285
|
-
this.
|
|
286
|
-
ipcMain.removeListener(channel, listener);
|
|
287
|
-
});
|
|
290
|
+
this.cleanups.push(() => ipcMain.removeListener(channel, listener));
|
|
288
291
|
return this;
|
|
289
292
|
}
|
|
290
293
|
on(channel, fn) {
|
|
@@ -311,13 +314,28 @@ var IpcRegistry = class {
|
|
|
311
314
|
safeInvoke(event, args);
|
|
312
315
|
} : (event, ...args) => safeInvoke(event, args);
|
|
313
316
|
ipcMain.on(channel, guarded);
|
|
314
|
-
this.
|
|
315
|
-
ipcMain.removeListener(channel, guarded);
|
|
316
|
-
});
|
|
317
|
+
this.cleanups.push(() => ipcMain.removeListener(channel, guarded));
|
|
317
318
|
return this;
|
|
318
319
|
}
|
|
319
320
|
dispose() {
|
|
320
|
-
|
|
321
|
+
if (this._disposed) return Promise.resolve();
|
|
322
|
+
this._disposed = true;
|
|
323
|
+
const items = this.cleanups.slice().reverse();
|
|
324
|
+
this.cleanups = [];
|
|
325
|
+
const errors = [];
|
|
326
|
+
for (const cleanup of items) {
|
|
327
|
+
try {
|
|
328
|
+
cleanup();
|
|
329
|
+
} catch (e) {
|
|
330
|
+
errors.push(e);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (errors.length > 0) {
|
|
334
|
+
return Promise.reject(
|
|
335
|
+
new AggregateError(errors, "IpcRegistry encountered errors during dispose")
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
return Promise.resolve();
|
|
321
339
|
}
|
|
322
340
|
};
|
|
323
341
|
|
|
@@ -352,7 +370,15 @@ var ServiceHostChannel = {
|
|
|
352
370
|
* change without a relaunch. The service-host preload mutates
|
|
353
371
|
* `__diminaSpawnContext.hostEnvSnapshot` in place (see `service-host/preload.cjs`).
|
|
354
372
|
*/
|
|
355
|
-
HostEnvUpdate: "service-host:host-env:update"
|
|
373
|
+
HostEnvUpdate: "service-host:host-env:update",
|
|
374
|
+
/**
|
|
375
|
+
* NATIVE-HOST ONLY. Deliver an AppData-panel edit (`{bridgeId, data}`) into
|
|
376
|
+
* the service-host window. The preload resolves the page instance via
|
|
377
|
+
* `getCurrentPages()` and calls `page.setData(data)`, so the resulting `ub`
|
|
378
|
+
* publish flows back through the normal service→render tap — the panel
|
|
379
|
+
* refreshes from the runtime's own state, not an optimistic local echo.
|
|
380
|
+
*/
|
|
381
|
+
AppDataSetData: "service-host:appdata:set-data"
|
|
356
382
|
};
|
|
357
383
|
var SimulatorCustomApiChannel = {
|
|
358
384
|
Invoke: "simulator:custom-apis:invoke"
|
|
@@ -384,7 +410,10 @@ var SimulatorWxmlChannel = {
|
|
|
384
410
|
};
|
|
385
411
|
var SimulatorAppDataChannel = {
|
|
386
412
|
GetSnapshot: "simulator:appdata:snapshot",
|
|
387
|
-
Event: "simulator:appdata:event"
|
|
413
|
+
Event: "simulator:appdata:event",
|
|
414
|
+
// renderer → main invoke: write an AppData-panel edit back into the running
|
|
415
|
+
// page (forwarded to the service host via ServiceHostChannel.AppDataSetData).
|
|
416
|
+
SetData: "simulator:appdata:set-data"
|
|
388
417
|
};
|
|
389
418
|
var WorkbenchSettingsChannel = {
|
|
390
419
|
Get: "workbenchSettings:get",
|
|
@@ -1008,9 +1037,9 @@ function createMainWindow(opts) {
|
|
|
1008
1037
|
|
|
1009
1038
|
// src/main/windows/main-window/events.ts
|
|
1010
1039
|
import { globalShortcut } from "electron";
|
|
1011
|
-
import { DisposableRegistry
|
|
1040
|
+
import { DisposableRegistry, toDisposable as toDisposable2 } from "@dimina-kit/electron-deck/main";
|
|
1012
1041
|
function wireMainWindowEvents(win, state = {}) {
|
|
1013
|
-
const registry = new
|
|
1042
|
+
const registry = new DisposableRegistry();
|
|
1014
1043
|
const mainWebView = win.contentView.children[0];
|
|
1015
1044
|
const resizeMainWebView = () => {
|
|
1016
1045
|
if (mainWebView) {
|
|
@@ -1096,7 +1125,7 @@ function resolveNativeAppDataKeys(context, appId) {
|
|
|
1096
1125
|
// src/main/services/workbench-context.ts
|
|
1097
1126
|
import {
|
|
1098
1127
|
createConnectionRegistry,
|
|
1099
|
-
DisposableRegistry as
|
|
1128
|
+
DisposableRegistry as DisposableRegistry2
|
|
1100
1129
|
} from "@dimina-kit/electron-deck/main";
|
|
1101
1130
|
|
|
1102
1131
|
// src/main/utils/sender-policy.ts
|
|
@@ -4539,7 +4568,7 @@ function createWorkbenchContext(opts) {
|
|
|
4539
4568
|
appName: opts.appName ?? "Dimina DevTools",
|
|
4540
4569
|
brandingProvider: opts.brandingProvider
|
|
4541
4570
|
};
|
|
4542
|
-
ctx.registry = new
|
|
4571
|
+
ctx.registry = new DisposableRegistry2();
|
|
4543
4572
|
ctx.connections = createConnectionRegistry();
|
|
4544
4573
|
ctx.trustedWindowSenderIds = /* @__PURE__ */ new Map();
|
|
4545
4574
|
ctx.simulatorApis = createSimulatorApiRegistry();
|
|
@@ -5184,7 +5213,7 @@ var settingsModule = {
|
|
|
5184
5213
|
};
|
|
5185
5214
|
|
|
5186
5215
|
// src/main/ipc/simulator-module.ts
|
|
5187
|
-
import { DisposableRegistry as
|
|
5216
|
+
import { DisposableRegistry as DisposableRegistry5 } from "@dimina-kit/electron-deck/main";
|
|
5188
5217
|
|
|
5189
5218
|
// src/main/ipc/bridge-router.ts
|
|
5190
5219
|
import { app as app12, ipcMain as ipcMain3, protocol as protocol2, session as electronSession, webContents as webContents3 } from "electron";
|
|
@@ -5735,7 +5764,7 @@ var ServiceHostPool = class {
|
|
|
5735
5764
|
};
|
|
5736
5765
|
|
|
5737
5766
|
// src/main/services/console-forward/index.ts
|
|
5738
|
-
import { DisposableRegistry as
|
|
5767
|
+
import { DisposableRegistry as DisposableRegistry3, toDisposable as toDisposable3 } from "@dimina-kit/electron-deck/main";
|
|
5739
5768
|
var FORWARDABLE_LEVELS = /* @__PURE__ */ new Set(["log", "warn", "error", "info", "debug"]);
|
|
5740
5769
|
function buildForwardScript(level, args) {
|
|
5741
5770
|
const method = FORWARDABLE_LEVELS.has(level) ? level : "log";
|
|
@@ -5756,7 +5785,7 @@ function buildDiagnosticScript(severity, message) {
|
|
|
5756
5785
|
}
|
|
5757
5786
|
function createConsoleForwarder(bridge, diagnostics) {
|
|
5758
5787
|
const sinks = /* @__PURE__ */ new Set();
|
|
5759
|
-
const registry = new
|
|
5788
|
+
const registry = new DisposableRegistry3();
|
|
5760
5789
|
const pendingBySession = /* @__PURE__ */ new Map();
|
|
5761
5790
|
const pendingGlobal = [];
|
|
5762
5791
|
const readySessions = /* @__PURE__ */ new Set();
|
|
@@ -5902,7 +5931,7 @@ function createDiagnosticsBus(opts) {
|
|
|
5902
5931
|
|
|
5903
5932
|
// src/main/services/simulator-storage/index.ts
|
|
5904
5933
|
import { app as app11, webContents as wcStatic } from "electron";
|
|
5905
|
-
import { DisposableRegistry as
|
|
5934
|
+
import { DisposableRegistry as DisposableRegistry4 } from "@dimina-kit/electron-deck/main";
|
|
5906
5935
|
|
|
5907
5936
|
// src/main/services/simulator-storage/service-storage-ops.ts
|
|
5908
5937
|
function encodeStorageValue(data) {
|
|
@@ -5989,7 +6018,7 @@ function safeOff(target, event, fn) {
|
|
|
5989
6018
|
}
|
|
5990
6019
|
}
|
|
5991
6020
|
function setupSimulatorStorage(host, options) {
|
|
5992
|
-
const registry = new
|
|
6021
|
+
const registry = new DisposableRegistry4();
|
|
5993
6022
|
const getActiveAppId = options.getActiveAppId;
|
|
5994
6023
|
function nativeServiceWc() {
|
|
5995
6024
|
if (!options.bridge?.isNativeHost()) return null;
|
|
@@ -6027,7 +6056,7 @@ function setupSimulatorStorage(host, options) {
|
|
|
6027
6056
|
wc.debugger.attach("1.3");
|
|
6028
6057
|
}
|
|
6029
6058
|
await wc.debugger.sendCommand("DOMStorage.enable");
|
|
6030
|
-
const attach = new
|
|
6059
|
+
const attach = new DisposableRegistry4();
|
|
6031
6060
|
attachDisposables = attach;
|
|
6032
6061
|
const onMessage = (_event, method, params) => forwardCdpMessage(method, params);
|
|
6033
6062
|
wc.debugger.on("message", onMessage);
|
|
@@ -6250,7 +6279,7 @@ function setupSimulatorStorage(host, options) {
|
|
|
6250
6279
|
}
|
|
6251
6280
|
const wcSubs = /* @__PURE__ */ new Map();
|
|
6252
6281
|
const onWcCreated = (_event, wc) => {
|
|
6253
|
-
const sub = new
|
|
6282
|
+
const sub = new DisposableRegistry4();
|
|
6254
6283
|
wcSubs.set(wc, sub);
|
|
6255
6284
|
const onFinishLoad = () => {
|
|
6256
6285
|
if (isSimulatorWebview(wc)) void attachToSim(wc);
|
|
@@ -7971,7 +8000,7 @@ function registerViewsIpc(ctx) {
|
|
|
7971
8000
|
// src/main/ipc/simulator-module.ts
|
|
7972
8001
|
var simulatorModule = {
|
|
7973
8002
|
setup: (ctx) => {
|
|
7974
|
-
const reg = new
|
|
8003
|
+
const reg = new DisposableRegistry5();
|
|
7975
8004
|
reg.add(registerSimulatorIpc(ctx));
|
|
7976
8005
|
reg.add(registerViewsIpc(ctx));
|
|
7977
8006
|
installBridgeRouter(ctx);
|
|
@@ -9361,7 +9390,7 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
|
9361
9390
|
}
|
|
9362
9391
|
|
|
9363
9392
|
// src/main/services/network-forward/index.ts
|
|
9364
|
-
import { DisposableRegistry as
|
|
9393
|
+
import { DisposableRegistry as DisposableRegistry6, toDisposable as toDisposable5 } from "@dimina-kit/electron-deck/main";
|
|
9365
9394
|
|
|
9366
9395
|
// src/main/services/network-forward/dispatch-batch.ts
|
|
9367
9396
|
function packDispatchBatch(queue, maxSingleChars, maxBatchChars) {
|
|
@@ -9508,7 +9537,7 @@ var MAX_DISPATCH_QUEUE = 2e3;
|
|
|
9508
9537
|
var DEVTOOLS_READY_TIMEOUT_MS = 5e3;
|
|
9509
9538
|
var READY_RETRY_MS = 100;
|
|
9510
9539
|
function createNetworkForwarder(bridge) {
|
|
9511
|
-
const registry = new
|
|
9540
|
+
const registry = new DisposableRegistry6();
|
|
9512
9541
|
let simWc = null;
|
|
9513
9542
|
let attachDisposables = null;
|
|
9514
9543
|
let devtoolsWc = null;
|
|
@@ -9731,7 +9760,7 @@ function createNetworkForwarder(bridge) {
|
|
|
9731
9760
|
return;
|
|
9732
9761
|
}
|
|
9733
9762
|
simWc = wc;
|
|
9734
|
-
const attach = new
|
|
9763
|
+
const attach = new DisposableRegistry6();
|
|
9735
9764
|
attachDisposables = attach;
|
|
9736
9765
|
const ns = new RequestIdNamespace(String(Date.now()));
|
|
9737
9766
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -9907,10 +9936,10 @@ function createNetworkForwarder(bridge) {
|
|
|
9907
9936
|
}
|
|
9908
9937
|
|
|
9909
9938
|
// src/main/services/simulator-wxml/index.ts
|
|
9910
|
-
import { DisposableRegistry as
|
|
9939
|
+
import { DisposableRegistry as DisposableRegistry7 } from "@dimina-kit/electron-deck/main";
|
|
9911
9940
|
function setupSimulatorWxml(host, options) {
|
|
9912
9941
|
const { bridge, inspector, getActiveAppId } = options;
|
|
9913
|
-
const registry = new
|
|
9942
|
+
const registry = new DisposableRegistry7();
|
|
9914
9943
|
let active = false;
|
|
9915
9944
|
let seq = 0;
|
|
9916
9945
|
let pulling = false;
|
|
@@ -10001,7 +10030,7 @@ import {
|
|
|
10001
10030
|
decodeWorkerMessage,
|
|
10002
10031
|
decodedToInput
|
|
10003
10032
|
} from "@dimina-kit/inspect";
|
|
10004
|
-
import { DisposableRegistry as
|
|
10033
|
+
import { DisposableRegistry as DisposableRegistry8 } from "@dimina-kit/electron-deck/main";
|
|
10005
10034
|
var EMPTY_SNAPSHOT = { bridges: [], entries: {} };
|
|
10006
10035
|
function setupSimulatorAppData(host, options) {
|
|
10007
10036
|
const { getActiveAppId } = options;
|
|
@@ -10044,7 +10073,20 @@ function setupSimulatorAppData(host, options) {
|
|
|
10044
10073
|
if (!appId) return EMPTY_SNAPSHOT;
|
|
10045
10074
|
return accumulators.get(appId)?.snapshot() ?? EMPTY_SNAPSHOT;
|
|
10046
10075
|
});
|
|
10047
|
-
|
|
10076
|
+
function isValidSetDataPatch(data) {
|
|
10077
|
+
return !!data && typeof data === "object" && !Array.isArray(data) && Object.keys(data).length > 0;
|
|
10078
|
+
}
|
|
10079
|
+
ipc.handle(SimulatorAppDataChannel.SetData, (_event, payload) => {
|
|
10080
|
+
const p = payload;
|
|
10081
|
+
if (!p || typeof p !== "object") return false;
|
|
10082
|
+
if (typeof p.bridgeId !== "string" || p.bridgeId.length === 0) return false;
|
|
10083
|
+
if (!isValidSetDataPatch(p.data)) return false;
|
|
10084
|
+
const wc = options.bridge?.getServiceWcForBridge(p.bridgeId);
|
|
10085
|
+
if (!wc || wc.isDestroyed()) return false;
|
|
10086
|
+
wc.send(ServiceHostChannel.AppDataSetData, { bridgeId: p.bridgeId, data: p.data });
|
|
10087
|
+
return true;
|
|
10088
|
+
});
|
|
10089
|
+
const registry = new DisposableRegistry8();
|
|
10048
10090
|
registry.add(() => accumulators.clear());
|
|
10049
10091
|
registry.add(ipc);
|
|
10050
10092
|
return {
|
|
@@ -10057,9 +10099,9 @@ function setupSimulatorAppData(host, options) {
|
|
|
10057
10099
|
}
|
|
10058
10100
|
|
|
10059
10101
|
// src/main/services/simulator-current-page/index.ts
|
|
10060
|
-
import { DisposableRegistry as
|
|
10102
|
+
import { DisposableRegistry as DisposableRegistry9 } from "@dimina-kit/electron-deck/main";
|
|
10061
10103
|
function setupSimulatorCurrentPage(host, options) {
|
|
10062
|
-
const registry = new
|
|
10104
|
+
const registry = new DisposableRegistry9();
|
|
10063
10105
|
const off = options.bridge.onRenderEvent((ev) => {
|
|
10064
10106
|
if (ev.kind !== "activePage" || !ev.pagePath) return;
|
|
10065
10107
|
if (host.isDestroyed()) return;
|
|
@@ -11673,7 +11715,10 @@ async function createDevtoolsRuntime(config = {}) {
|
|
|
11673
11715
|
}));
|
|
11674
11716
|
const appDataService = setupSimulatorAppData(mainWindow.webContents, {
|
|
11675
11717
|
senderPolicy: context.senderPolicy,
|
|
11676
|
-
getActiveAppId
|
|
11718
|
+
getActiveAppId,
|
|
11719
|
+
// AppData-panel edit write-back target: the service-host window owning
|
|
11720
|
+
// the edited page bridge.
|
|
11721
|
+
bridge: context.bridge
|
|
11677
11722
|
});
|
|
11678
11723
|
context.appData = appDataService;
|
|
11679
11724
|
context.registry.add(appDataService);
|
|
@@ -30,6 +30,12 @@ export interface SimulatorAppDataService extends AppDataTap, Disposable {
|
|
|
30
30
|
export interface SimulatorAppDataOptions {
|
|
31
31
|
getActiveAppId: () => string | null;
|
|
32
32
|
senderPolicy?: SenderPolicy;
|
|
33
|
+
/** Resolves the service-host WebContents owning a page bridge — the SetData
|
|
34
|
+
* write-back target. Absent (host without a service bridge) → edits are
|
|
35
|
+
* rejected with `false`. */
|
|
36
|
+
bridge?: {
|
|
37
|
+
getServiceWcForBridge: (bridgeId: string) => WebContents | null;
|
|
38
|
+
};
|
|
33
39
|
}
|
|
34
40
|
export declare function setupSimulatorAppData(host: WebContents, options: SimulatorAppDataOptions): SimulatorAppDataService;
|
|
35
41
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SimulatorAppDataChannel } from '../../../shared/ipc-channels.js';
|
|
1
|
+
import { ServiceHostChannel, SimulatorAppDataChannel } from '../../../shared/ipc-channels.js';
|
|
2
2
|
import { AppDataAccumulator, decodeWorkerMessage, decodedToInput, } from '@dimina-kit/inspect';
|
|
3
3
|
import { DisposableRegistry } from '@dimina-kit/electron-deck/main';
|
|
4
4
|
import { IpcRegistry } from '../../utils/ipc-registry.js';
|
|
@@ -54,6 +54,25 @@ export function setupSimulatorAppData(host, options) {
|
|
|
54
54
|
return EMPTY_SNAPSHOT;
|
|
55
55
|
return accumulators.get(appId)?.snapshot() ?? EMPTY_SNAPSHOT;
|
|
56
56
|
});
|
|
57
|
+
/** A plain, non-empty `{key: value}` patch — the only shape page.setData accepts. */
|
|
58
|
+
function isValidSetDataPatch(data) {
|
|
59
|
+
return !!data && typeof data === 'object' && !Array.isArray(data)
|
|
60
|
+
&& Object.keys(data).length > 0;
|
|
61
|
+
}
|
|
62
|
+
ipc.handle(SimulatorAppDataChannel.SetData, (_event, payload) => {
|
|
63
|
+
const p = payload;
|
|
64
|
+
if (!p || typeof p !== 'object')
|
|
65
|
+
return false;
|
|
66
|
+
if (typeof p.bridgeId !== 'string' || p.bridgeId.length === 0)
|
|
67
|
+
return false;
|
|
68
|
+
if (!isValidSetDataPatch(p.data))
|
|
69
|
+
return false;
|
|
70
|
+
const wc = options.bridge?.getServiceWcForBridge(p.bridgeId);
|
|
71
|
+
if (!wc || wc.isDestroyed())
|
|
72
|
+
return false;
|
|
73
|
+
wc.send(ServiceHostChannel.AppDataSetData, { bridgeId: p.bridgeId, data: p.data });
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
57
76
|
// disposeAll runs LIFO; add the IPC registry LAST so it is torn down first —
|
|
58
77
|
// its removeHandler then runs synchronously (before the first `await` yields),
|
|
59
78
|
// which callers that `dispose()` without awaiting rely on.
|
|
@@ -29,7 +29,8 @@ export type SenderPolicy = (sender: WebContents) => boolean;
|
|
|
29
29
|
*/
|
|
30
30
|
export declare class IpcRegistry implements Disposable {
|
|
31
31
|
private policy?;
|
|
32
|
-
private
|
|
32
|
+
private cleanups;
|
|
33
|
+
private _disposed;
|
|
33
34
|
constructor(policy?: SenderPolicy | undefined);
|
|
34
35
|
handle(channel: string, fn: HandleFn): this;
|
|
35
36
|
/**
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ipcMain } from 'electron';
|
|
2
|
-
import { DisposableRegistry } from '@dimina-kit/electron-deck/main';
|
|
3
2
|
import { IpcValidationError } from './ipc-schema.js';
|
|
4
3
|
import { createLogger } from './logger.js';
|
|
5
4
|
const log = createLogger('ipc');
|
|
@@ -66,7 +65,13 @@ function isMainFrameSender(event) {
|
|
|
66
65
|
*/
|
|
67
66
|
export class IpcRegistry {
|
|
68
67
|
policy;
|
|
69
|
-
|
|
68
|
+
// Every cleanup is a synchronous ipcMain.removeHandler/removeListener call,
|
|
69
|
+
// so dispose() can (and must) run them all before returning: callers
|
|
70
|
+
// `dispose()` without awaiting and rely on every channel being unregistered
|
|
71
|
+
// synchronously — an async registry would leave all but the first handler
|
|
72
|
+
// live until a later microtask.
|
|
73
|
+
cleanups = [];
|
|
74
|
+
_disposed = false;
|
|
70
75
|
constructor(policy) {
|
|
71
76
|
this.policy = policy;
|
|
72
77
|
}
|
|
@@ -87,7 +92,7 @@ export class IpcRegistry {
|
|
|
87
92
|
}
|
|
88
93
|
: fn;
|
|
89
94
|
ipcMain.handle(channel, guarded);
|
|
90
|
-
this.
|
|
95
|
+
this.cleanups.push(() => ipcMain.removeHandler(channel));
|
|
91
96
|
return this;
|
|
92
97
|
}
|
|
93
98
|
/**
|
|
@@ -128,9 +133,7 @@ export class IpcRegistry {
|
|
|
128
133
|
}
|
|
129
134
|
};
|
|
130
135
|
ipcMain.on(channel, listener);
|
|
131
|
-
this.
|
|
132
|
-
ipcMain.removeListener(channel, listener);
|
|
133
|
-
});
|
|
136
|
+
this.cleanups.push(() => ipcMain.removeListener(channel, listener));
|
|
134
137
|
return this;
|
|
135
138
|
}
|
|
136
139
|
on(channel, fn) {
|
|
@@ -161,13 +164,30 @@ export class IpcRegistry {
|
|
|
161
164
|
}
|
|
162
165
|
: (event, ...args) => safeInvoke(event, args);
|
|
163
166
|
ipcMain.on(channel, guarded);
|
|
164
|
-
this.
|
|
165
|
-
ipcMain.removeListener(channel, guarded);
|
|
166
|
-
});
|
|
167
|
+
this.cleanups.push(() => ipcMain.removeListener(channel, guarded));
|
|
167
168
|
return this;
|
|
168
169
|
}
|
|
169
170
|
dispose() {
|
|
170
|
-
|
|
171
|
+
// Synchronous drain (LIFO, idempotent): every channel is unregistered
|
|
172
|
+
// before this returns; the promise only carries aggregated errors.
|
|
173
|
+
if (this._disposed)
|
|
174
|
+
return Promise.resolve();
|
|
175
|
+
this._disposed = true;
|
|
176
|
+
const items = this.cleanups.slice().reverse();
|
|
177
|
+
this.cleanups = [];
|
|
178
|
+
const errors = [];
|
|
179
|
+
for (const cleanup of items) {
|
|
180
|
+
try {
|
|
181
|
+
cleanup();
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
errors.push(e);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (errors.length > 0) {
|
|
188
|
+
return Promise.reject(new AggregateError(errors, 'IpcRegistry encountered errors during dispose'));
|
|
189
|
+
}
|
|
190
|
+
return Promise.resolve();
|
|
171
191
|
}
|
|
172
192
|
}
|
|
173
193
|
//# sourceMappingURL=ipc-registry.js.map
|