@janhendry/nanostore-ipc-bridge 0.0.1
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 +188 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +229 -0
- package/dist/index.mjs.map +1 -0
- package/dist/main/index.d.mts +56 -0
- package/dist/main/index.d.ts +56 -0
- package/dist/main/index.js +475 -0
- package/dist/main/index.js.map +1 -0
- package/dist/main/index.mjs +472 -0
- package/dist/main/index.mjs.map +1 -0
- package/dist/preload/index.d.mts +21 -0
- package/dist/preload/index.d.ts +21 -0
- package/dist/preload/index.js +122 -0
- package/dist/preload/index.js.map +1 -0
- package/dist/preload/index.mjs +120 -0
- package/dist/preload/index.mjs.map +1 -0
- package/dist/types-CyJJt8gf.d.mts +25 -0
- package/dist/types-CyJJt8gf.d.ts +25 -0
- package/dist/universal/index.d.mts +124 -0
- package/dist/universal/index.d.ts +124 -0
- package/dist/universal/index.js +232 -0
- package/dist/universal/index.js.map +1 -0
- package/dist/universal/index.mjs +229 -0
- package/dist/universal/index.mjs.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { ipcMain, app } from 'electron';
|
|
2
|
+
|
|
3
|
+
// src/main/initNanoStoreIPC.ts
|
|
4
|
+
|
|
5
|
+
// src/internal/symbols.ts
|
|
6
|
+
var WF_NS_QUEUE = /* @__PURE__ */ Symbol.for("wf.nanostore.ipc.queue");
|
|
7
|
+
var WF_NS_SERVICE_QUEUE = /* @__PURE__ */ Symbol.for("wf.nanostore.ipc.serviceQueue");
|
|
8
|
+
var WF_NS_MAIN_API = /* @__PURE__ */ Symbol.for("wf.nanostore.ipc.mainApi");
|
|
9
|
+
|
|
10
|
+
// src/internal/types.ts
|
|
11
|
+
var NanoStoreIPCError = class extends Error {
|
|
12
|
+
constructor(message, code, storeId, originalError) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.storeId = storeId;
|
|
16
|
+
this.originalError = originalError;
|
|
17
|
+
this.name = "NanoStoreIPCError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function initServices(windows, opts = {}) {
|
|
21
|
+
const channelPrefix = opts.channelPrefix ?? "";
|
|
22
|
+
const enableLogging = opts.enableLogging ?? false;
|
|
23
|
+
const services = /* @__PURE__ */ new Map();
|
|
24
|
+
const log = (...args) => {
|
|
25
|
+
if (enableLogging) console.log("[services]", ...args);
|
|
26
|
+
};
|
|
27
|
+
const handleError = (error) => {
|
|
28
|
+
if (enableLogging) {
|
|
29
|
+
console.error("[services:error]", {
|
|
30
|
+
code: error.code,
|
|
31
|
+
message: error.message,
|
|
32
|
+
originalError: error.originalError
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (opts.onError) {
|
|
36
|
+
try {
|
|
37
|
+
opts.onError(error);
|
|
38
|
+
} catch (error_) {
|
|
39
|
+
console.error("[services] Error in error handler:", error_);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const ch2 = (c) => channelPrefix ? `${channelPrefix}:${c}` : c;
|
|
44
|
+
const broadcast = (serviceId, eventName, data) => {
|
|
45
|
+
const channel = ch2("svc:event");
|
|
46
|
+
const payload = { serviceId, eventName, data };
|
|
47
|
+
for (const win of windows) {
|
|
48
|
+
if (win.isDestroyed()) {
|
|
49
|
+
windows.delete(win);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const win of windows) {
|
|
53
|
+
try {
|
|
54
|
+
win.webContents.send(channel, payload);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
handleError(
|
|
57
|
+
new NanoStoreIPCError(
|
|
58
|
+
`Failed to broadcast service event "${eventName}"`,
|
|
59
|
+
"IPC_FAILED",
|
|
60
|
+
serviceId,
|
|
61
|
+
err
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
windows.delete(win);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
log("broadcast:", serviceId, eventName, `(${windows.size} windows)`);
|
|
68
|
+
};
|
|
69
|
+
const registerService = (id, definition) => {
|
|
70
|
+
if (services.has(id)) {
|
|
71
|
+
log("service already registered, skipping:", id);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
services.set(id, { id, definition });
|
|
75
|
+
log("service registered:", id);
|
|
76
|
+
};
|
|
77
|
+
const callChannel = ch2("svc:call");
|
|
78
|
+
if (ipcMain.listenerCount(callChannel) === 0) {
|
|
79
|
+
ipcMain.handle(
|
|
80
|
+
callChannel,
|
|
81
|
+
async (_event, serviceId, methodName, args) => {
|
|
82
|
+
const startTime = performance.now();
|
|
83
|
+
try {
|
|
84
|
+
const service = services.get(serviceId);
|
|
85
|
+
if (!service) {
|
|
86
|
+
const err = new NanoStoreIPCError(
|
|
87
|
+
`Service not found: ${serviceId}`,
|
|
88
|
+
"SERVICE_NOT_FOUND",
|
|
89
|
+
serviceId
|
|
90
|
+
);
|
|
91
|
+
handleError(err);
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
error: {
|
|
95
|
+
message: err.message,
|
|
96
|
+
code: err.code,
|
|
97
|
+
stack: err.stack
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const handler = service.definition.handlers[methodName];
|
|
102
|
+
if (!handler || typeof handler !== "function") {
|
|
103
|
+
const err = new NanoStoreIPCError(
|
|
104
|
+
`Method not found: ${serviceId}.${methodName}`,
|
|
105
|
+
"SERVICE_METHOD_NOT_FOUND",
|
|
106
|
+
serviceId
|
|
107
|
+
);
|
|
108
|
+
handleError(err);
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
error: {
|
|
112
|
+
message: err.message,
|
|
113
|
+
code: err.code,
|
|
114
|
+
stack: err.stack
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (service.definition.beforeAll) {
|
|
119
|
+
await service.definition.beforeAll(methodName, args);
|
|
120
|
+
}
|
|
121
|
+
const result = await handler(...args);
|
|
122
|
+
const duration = performance.now() - startTime;
|
|
123
|
+
if (service.definition.afterAll) {
|
|
124
|
+
await service.definition.afterAll(methodName, result, duration);
|
|
125
|
+
}
|
|
126
|
+
log(
|
|
127
|
+
`${serviceId}.${methodName}() completed in ${duration.toFixed(2)}ms`
|
|
128
|
+
);
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
result
|
|
132
|
+
};
|
|
133
|
+
} catch (err) {
|
|
134
|
+
const duration = performance.now() - startTime;
|
|
135
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
136
|
+
const service = services.get(serviceId);
|
|
137
|
+
if (service?.definition.afterAll) {
|
|
138
|
+
try {
|
|
139
|
+
await service.definition.afterAll(
|
|
140
|
+
methodName,
|
|
141
|
+
void 0,
|
|
142
|
+
duration
|
|
143
|
+
);
|
|
144
|
+
} catch {
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
handleError(
|
|
148
|
+
new NanoStoreIPCError(
|
|
149
|
+
`Service call failed: ${serviceId}.${methodName}`,
|
|
150
|
+
"IPC_FAILED",
|
|
151
|
+
serviceId,
|
|
152
|
+
err
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
error: {
|
|
158
|
+
message: error.message,
|
|
159
|
+
code: error.name,
|
|
160
|
+
stack: error.stack
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
log("service IPC initialized", { channelPrefix });
|
|
168
|
+
return {
|
|
169
|
+
registerService,
|
|
170
|
+
broadcast,
|
|
171
|
+
destroy: () => {
|
|
172
|
+
services.clear();
|
|
173
|
+
try {
|
|
174
|
+
ipcMain.removeHandler(callChannel);
|
|
175
|
+
} catch (err) {
|
|
176
|
+
handleError(
|
|
177
|
+
new NanoStoreIPCError(
|
|
178
|
+
"Failed to remove service IPC handler",
|
|
179
|
+
"IPC_FAILED",
|
|
180
|
+
void 0,
|
|
181
|
+
err
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
log("destroyed");
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/main/initNanoStoreIPC.ts
|
|
191
|
+
function ch(prefix, c) {
|
|
192
|
+
return prefix ? `${prefix}:${c}` : c;
|
|
193
|
+
}
|
|
194
|
+
var instanceCreated = false;
|
|
195
|
+
function initNanoStoreIPC(opts = {}) {
|
|
196
|
+
if (instanceCreated) {
|
|
197
|
+
throw new NanoStoreIPCError(
|
|
198
|
+
"initNanoStoreIPC() has already been called. Multiple initializations are not allowed.",
|
|
199
|
+
"ALREADY_INITIALIZED"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
instanceCreated = true;
|
|
203
|
+
const channelPrefix = opts.channelPrefix ?? "";
|
|
204
|
+
const enableLogging = opts.enableLogging ?? false;
|
|
205
|
+
const autoRegisterWindows = opts.autoRegisterWindows ?? true;
|
|
206
|
+
const allowRendererSet = opts.allowRendererSet ?? true;
|
|
207
|
+
const validateSerialization = opts.validateSerialization ?? false;
|
|
208
|
+
const windows = /* @__PURE__ */ new Set();
|
|
209
|
+
const stores = /* @__PURE__ */ new Map();
|
|
210
|
+
const globalWithSymbols = globalThis;
|
|
211
|
+
const queue = globalWithSymbols[WF_NS_QUEUE] ?? /* @__PURE__ */ new Map();
|
|
212
|
+
globalWithSymbols[WF_NS_QUEUE] = queue;
|
|
213
|
+
const log = (...args) => {
|
|
214
|
+
if (enableLogging) console.log("[nanostore-ipc]", ...args);
|
|
215
|
+
};
|
|
216
|
+
const pendingBroadcasts = /* @__PURE__ */ new Map();
|
|
217
|
+
let broadcastScheduled = false;
|
|
218
|
+
const handleError = (error) => {
|
|
219
|
+
if (enableLogging) {
|
|
220
|
+
console.error("[nanostore-ipc:error]", {
|
|
221
|
+
code: error.code,
|
|
222
|
+
message: error.message,
|
|
223
|
+
storeId: error.storeId,
|
|
224
|
+
originalError: error.originalError
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (opts.onError) {
|
|
228
|
+
try {
|
|
229
|
+
opts.onError(error);
|
|
230
|
+
} catch (error_) {
|
|
231
|
+
console.error("[nanostore-ipc] Error in error handler:", error_);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const broadcastPending = () => {
|
|
236
|
+
const channel = ch(channelPrefix, "ns:update");
|
|
237
|
+
for (const win of windows) {
|
|
238
|
+
if (win.isDestroyed()) {
|
|
239
|
+
windows.delete(win);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const snap of pendingBroadcasts.values()) {
|
|
243
|
+
for (const win of windows) {
|
|
244
|
+
try {
|
|
245
|
+
win.webContents.send(channel, snap);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
handleError(
|
|
248
|
+
new NanoStoreIPCError(
|
|
249
|
+
`Failed to broadcast update for store "${snap.id}"`,
|
|
250
|
+
"IPC_FAILED",
|
|
251
|
+
snap.id,
|
|
252
|
+
err
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
windows.delete(win);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
log("broadcast:", snap.id, `(${windows.size} windows)`);
|
|
259
|
+
}
|
|
260
|
+
pendingBroadcasts.clear();
|
|
261
|
+
broadcastScheduled = false;
|
|
262
|
+
};
|
|
263
|
+
const broadcast = (snap) => {
|
|
264
|
+
pendingBroadcasts.set(snap.id, snap);
|
|
265
|
+
if (!broadcastScheduled) {
|
|
266
|
+
broadcastScheduled = true;
|
|
267
|
+
queueMicrotask(broadcastPending);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const registerStore = (id, store) => {
|
|
271
|
+
if (stores.has(id)) {
|
|
272
|
+
log("store already registered, skipping:", id);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const entry = {
|
|
276
|
+
store,
|
|
277
|
+
rev: 0,
|
|
278
|
+
unsubscribe: () => {
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const unsubscribe = store.subscribe((value) => {
|
|
282
|
+
entry.rev += 1;
|
|
283
|
+
const snap = { id, rev: entry.rev, value };
|
|
284
|
+
if (validateSerialization) {
|
|
285
|
+
try {
|
|
286
|
+
structuredClone(value);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
handleError(
|
|
289
|
+
new NanoStoreIPCError(
|
|
290
|
+
`Store value not serializable: ${id}`,
|
|
291
|
+
"SERIALIZATION_FAILED",
|
|
292
|
+
id,
|
|
293
|
+
err
|
|
294
|
+
)
|
|
295
|
+
);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
broadcast(snap);
|
|
300
|
+
});
|
|
301
|
+
entry.unsubscribe = unsubscribe;
|
|
302
|
+
stores.set(id, entry);
|
|
303
|
+
log("store registered:", id);
|
|
304
|
+
};
|
|
305
|
+
const serviceManager = initServices(windows, {
|
|
306
|
+
channelPrefix,
|
|
307
|
+
enableLogging,
|
|
308
|
+
onError: opts.onError
|
|
309
|
+
});
|
|
310
|
+
const api = {
|
|
311
|
+
registerStore,
|
|
312
|
+
isInitialized: () => true,
|
|
313
|
+
registerService: serviceManager.registerService,
|
|
314
|
+
broadcast: serviceManager.broadcast
|
|
315
|
+
};
|
|
316
|
+
globalWithSymbols[WF_NS_MAIN_API] = api;
|
|
317
|
+
for (const [id, store] of queue.entries()) {
|
|
318
|
+
registerStore(id, store);
|
|
319
|
+
}
|
|
320
|
+
queue.clear();
|
|
321
|
+
const serviceQueue = globalWithSymbols[WF_NS_SERVICE_QUEUE] ?? /* @__PURE__ */ new Map();
|
|
322
|
+
for (const [id, entry] of serviceQueue.entries()) {
|
|
323
|
+
api.registerService(id, entry.definition);
|
|
324
|
+
log("service registered from queue:", id);
|
|
325
|
+
}
|
|
326
|
+
serviceQueue.clear();
|
|
327
|
+
const getChannel = ch(channelPrefix, "ns:get");
|
|
328
|
+
const setChannel = ch(channelPrefix, "ns:set");
|
|
329
|
+
if (ipcMain.listenerCount(getChannel) === 0) {
|
|
330
|
+
ipcMain.handle(getChannel, (_e, id) => {
|
|
331
|
+
const entry = stores.get(id);
|
|
332
|
+
if (!entry) {
|
|
333
|
+
const err = new NanoStoreIPCError(
|
|
334
|
+
`Store not found: ${id}`,
|
|
335
|
+
"STORE_NOT_FOUND",
|
|
336
|
+
id
|
|
337
|
+
);
|
|
338
|
+
handleError(err);
|
|
339
|
+
throw err;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
id,
|
|
343
|
+
rev: entry.rev,
|
|
344
|
+
value: entry.store.get()
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
if (ipcMain.listenerCount(setChannel) === 0) {
|
|
349
|
+
ipcMain.handle(setChannel, (_e, id, value) => {
|
|
350
|
+
if (!allowRendererSet) {
|
|
351
|
+
const err = new NanoStoreIPCError(
|
|
352
|
+
"Renderer writes are disabled (allowRendererSet=false)",
|
|
353
|
+
"RENDERER_WRITE_DISABLED",
|
|
354
|
+
id
|
|
355
|
+
);
|
|
356
|
+
handleError(err);
|
|
357
|
+
throw err;
|
|
358
|
+
}
|
|
359
|
+
const entry = stores.get(id);
|
|
360
|
+
if (!entry) {
|
|
361
|
+
const err = new NanoStoreIPCError(
|
|
362
|
+
`Store not found: ${id}`,
|
|
363
|
+
"STORE_NOT_FOUND",
|
|
364
|
+
id
|
|
365
|
+
);
|
|
366
|
+
handleError(err);
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
369
|
+
const store = entry.store;
|
|
370
|
+
if (typeof store.set === "function") {
|
|
371
|
+
store.set(value);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const registerWindow = (win) => {
|
|
376
|
+
if (windows.has(win)) return;
|
|
377
|
+
windows.add(win);
|
|
378
|
+
const onClosed = () => {
|
|
379
|
+
windows.delete(win);
|
|
380
|
+
log("window unregistered:", win.id);
|
|
381
|
+
};
|
|
382
|
+
win.once("closed", onClosed);
|
|
383
|
+
const pushSnapshots = () => {
|
|
384
|
+
if (win.isDestroyed()) return;
|
|
385
|
+
log("pushing snapshots to window:", win.id);
|
|
386
|
+
for (const [id, entry] of stores.entries()) {
|
|
387
|
+
const snap = {
|
|
388
|
+
id,
|
|
389
|
+
rev: entry.rev,
|
|
390
|
+
value: entry.store.get()
|
|
391
|
+
};
|
|
392
|
+
try {
|
|
393
|
+
win.webContents.send(ch(channelPrefix, "ns:update"), snap);
|
|
394
|
+
} catch (err) {
|
|
395
|
+
handleError(
|
|
396
|
+
new NanoStoreIPCError(
|
|
397
|
+
`Failed to push snapshot for store "${id}"`,
|
|
398
|
+
"IPC_FAILED",
|
|
399
|
+
id,
|
|
400
|
+
err
|
|
401
|
+
)
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
win.webContents.once("did-finish-load", pushSnapshots);
|
|
407
|
+
log("window registered:", win.id);
|
|
408
|
+
};
|
|
409
|
+
if (autoRegisterWindows) {
|
|
410
|
+
app.on("browser-window-created", (_event, win) => {
|
|
411
|
+
registerWindow(win);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
log("IPC initialized", {
|
|
415
|
+
channelPrefix,
|
|
416
|
+
autoRegisterWindows,
|
|
417
|
+
allowRendererSet
|
|
418
|
+
});
|
|
419
|
+
return {
|
|
420
|
+
registerStore,
|
|
421
|
+
registerWindow,
|
|
422
|
+
destroy: () => {
|
|
423
|
+
log("destroying IPC bridge...");
|
|
424
|
+
for (const [id, entry] of stores.entries()) {
|
|
425
|
+
try {
|
|
426
|
+
entry.unsubscribe();
|
|
427
|
+
log("unsubscribed store:", id);
|
|
428
|
+
} catch (err) {
|
|
429
|
+
handleError(
|
|
430
|
+
new NanoStoreIPCError(
|
|
431
|
+
`Failed to unsubscribe store "${id}"`,
|
|
432
|
+
"IPC_FAILED",
|
|
433
|
+
id,
|
|
434
|
+
err
|
|
435
|
+
)
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
stores.clear();
|
|
440
|
+
windows.clear();
|
|
441
|
+
try {
|
|
442
|
+
ipcMain.removeHandler(getChannel);
|
|
443
|
+
ipcMain.removeHandler(setChannel);
|
|
444
|
+
} catch (err) {
|
|
445
|
+
handleError(
|
|
446
|
+
new NanoStoreIPCError(
|
|
447
|
+
"Failed to remove IPC handlers",
|
|
448
|
+
"IPC_FAILED",
|
|
449
|
+
void 0,
|
|
450
|
+
err
|
|
451
|
+
)
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
try {
|
|
455
|
+
serviceManager.destroy();
|
|
456
|
+
} catch (err) {
|
|
457
|
+
handleError(
|
|
458
|
+
new NanoStoreIPCError(
|
|
459
|
+
"Failed to destroy services",
|
|
460
|
+
"IPC_FAILED",
|
|
461
|
+
void 0,
|
|
462
|
+
err
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export { initNanoStoreIPC, initServices };
|
|
471
|
+
//# sourceMappingURL=index.mjs.map
|
|
472
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/internal/symbols.ts","../../src/internal/types.ts","../../src/main/services.ts","../../src/main/initNanoStoreIPC.ts"],"names":["ch","ipcMain"],"mappings":"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","file":"index.mjs","sourcesContent":["export const WF_NS_QUEUE = Symbol.for(\"wf.nanostore.ipc.queue\");\nexport const WF_NS_SERVICE_QUEUE = Symbol.for(\"wf.nanostore.ipc.serviceQueue\");\nexport const WF_NS_MAIN_API = Symbol.for(\"wf.nanostore.ipc.mainApi\");\n","import type { Store } from \"nanostores\";\n\nexport type Snapshot<T> = { id: string; rev: number; value: T };\n\nexport type MainRegisterFn = <T = unknown>(id: string, store: Store<T>) => void;\n\nexport type MainApi = {\n\tregisterStore: MainRegisterFn;\n\tisInitialized: () => boolean;\n\tregisterService: ServiceRegisterFn;\n\tbroadcast: (serviceId: string, eventName: string, data?: unknown) => void;\n};\n\nexport type ErrorHandler = (error: NanoStoreIPCError) => void;\n\nexport class NanoStoreIPCError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic code:\n\t\t\t| \"STORE_NOT_FOUND\"\n\t\t\t| \"RENDERER_WRITE_DISABLED\"\n\t\t\t| \"SERIALIZATION_FAILED\"\n\t\t\t| \"IPC_FAILED\"\n\t\t\t| \"SERVICE_NOT_FOUND\"\n\t\t\t| \"SERVICE_METHOD_NOT_FOUND\"\n\t\t\t| \"ALREADY_INITIALIZED\",\n\t\tpublic storeId?: string,\n\t\tpublic originalError?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"NanoStoreIPCError\";\n\t}\n}\n\n// Service types\nexport type ServiceHandler = (...args: unknown[]) => Promise<unknown>;\n\nexport type ServiceHandlers = Record<string, ServiceHandler>;\n\nexport type ServiceHooks = {\n\tbeforeAll?: (methodName: string, args: unknown[]) => void | Promise<void>;\n\tafterAll?: (\n\t\tmethodName: string,\n\t\tresult: unknown,\n\t\tduration: number,\n\t) => void | Promise<void>;\n};\n\nexport type ServiceDefinition<T extends ServiceHandlers = ServiceHandlers> = {\n\thandlers: T;\n\tbeforeAll?: (methodName: string, args: unknown[]) => void | Promise<void>;\n\tafterAll?: (\n\t\tmethodName: string,\n\t\tresult: unknown,\n\t\tduration: number,\n\t) => void | Promise<void>;\n};\n\nexport type ServiceQueueEntry<T extends ServiceHandlers = ServiceHandlers> = {\n\tid: string;\n\tdefinition: ServiceDefinition<T>;\n};\n\nexport type ServiceEventCallback = (data: unknown) => void;\n\nexport type ServiceBroadcastFn = (eventName: string, data: unknown) => void;\n\nexport type ServiceRegisterFn = <T extends ServiceHandlers>(\n\tid: string,\n\thandlers: ServiceDefinition<T>,\n) => void;\n\nexport type ServiceCallResult<T = unknown> = {\n\tsuccess: boolean;\n\tresult?: T;\n\terror?: {\n\t\tmessage: string;\n\t\tcode: string;\n\t\tstack?: string;\n\t};\n};\n","import { type BrowserWindow, ipcMain } from \"electron\";\nimport {\n\tNanoStoreIPCError,\n\ttype ServiceCallResult,\n\ttype ServiceDefinition,\n\ttype ServiceHandlers,\n} from \"../internal/types\";\n\ntype ServiceEntry<T extends ServiceHandlers = ServiceHandlers> = {\n\tid: string;\n\tdefinition: ServiceDefinition<T>;\n};\n\nexport interface InitServicesOptions {\n\tchannelPrefix?: string;\n\tenableLogging?: boolean;\n\tonError?: (error: NanoStoreIPCError) => void;\n}\n\n/**\n * Initialize service IPC handlers in main process\n * Should be called after initNanoStoreIPC()\n */\nexport function initServices(\n\twindows: Set<BrowserWindow>,\n\topts: InitServicesOptions = {},\n) {\n\tconst channelPrefix = opts.channelPrefix ?? \"\";\n\tconst enableLogging = opts.enableLogging ?? false;\n\n\tconst services = new Map<string, ServiceEntry>();\n\n\tconst log = (...args: unknown[]) => {\n\t\tif (enableLogging) console.log(\"[services]\", ...args);\n\t};\n\n\tconst handleError = (error: NanoStoreIPCError) => {\n\t\tif (enableLogging) {\n\t\t\tconsole.error(\"[services:error]\", {\n\t\t\t\tcode: error.code,\n\t\t\t\tmessage: error.message,\n\t\t\t\toriginalError: error.originalError,\n\t\t\t});\n\t\t}\n\n\t\tif (opts.onError) {\n\t\t\ttry {\n\t\t\t\topts.onError(error);\n\t\t\t} catch (error_) {\n\t\t\t\tconsole.error(\"[services] Error in error handler:\", error_);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst ch = (c: string) => (channelPrefix ? `${channelPrefix}:${c}` : c);\n\n\t// Broadcast event to all windows\n\tconst broadcast = (serviceId: string, eventName: string, data: unknown) => {\n\t\tconst channel = ch(\"svc:event\");\n\t\tconst payload = { serviceId, eventName, data };\n\n\t\t// Clean up destroyed windows\n\t\tfor (const win of windows) {\n\t\t\tif (win.isDestroyed()) {\n\t\t\t\twindows.delete(win);\n\t\t\t}\n\t\t}\n\n\t\t// Broadcast to all active windows\n\t\tfor (const win of windows) {\n\t\t\ttry {\n\t\t\t\twin.webContents.send(channel, payload);\n\t\t\t} catch (err) {\n\t\t\t\thandleError(\n\t\t\t\t\tnew NanoStoreIPCError(\n\t\t\t\t\t\t`Failed to broadcast service event \"${eventName}\"`,\n\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\tserviceId,\n\t\t\t\t\t\terr,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\twindows.delete(win);\n\t\t\t}\n\t\t}\n\n\t\tlog(\"broadcast:\", serviceId, eventName, `(${windows.size} windows)`);\n\t};\n\n\t// Register a service\n\tconst registerService = <T extends ServiceHandlers>(\n\t\tid: string,\n\t\tdefinition: ServiceDefinition<T>,\n\t) => {\n\t\tif (services.has(id)) {\n\t\t\tlog(\"service already registered, skipping:\", id);\n\t\t\treturn;\n\t\t}\n\n\t\tservices.set(id, { id, definition });\n\t\tlog(\"service registered:\", id);\n\t};\n\n\t// IPC handler for service calls\n\tconst callChannel = ch(\"svc:call\");\n\n\tif (ipcMain.listenerCount(callChannel) === 0) {\n\t\tipcMain.handle(\n\t\t\tcallChannel,\n\t\t\tasync (\n\t\t\t\t_event,\n\t\t\t\tserviceId: string,\n\t\t\t\tmethodName: string,\n\t\t\t\targs: unknown[],\n\t\t\t): Promise<ServiceCallResult> => {\n\t\t\t\tconst startTime = performance.now();\n\n\t\t\t\ttry {\n\t\t\t\t\t// Find service\n\t\t\t\t\tconst service = services.get(serviceId);\n\t\t\t\t\tif (!service) {\n\t\t\t\t\t\tconst err = new NanoStoreIPCError(\n\t\t\t\t\t\t\t`Service not found: ${serviceId}`,\n\t\t\t\t\t\t\t\"SERVICE_NOT_FOUND\",\n\t\t\t\t\t\t\tserviceId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\thandleError(err);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t\t\t\tcode: err.code,\n\t\t\t\t\t\t\t\tstack: err.stack,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Find method\n\t\t\t\t\tconst handler = service.definition.handlers[methodName];\n\t\t\t\t\tif (!handler || typeof handler !== \"function\") {\n\t\t\t\t\t\tconst err = new NanoStoreIPCError(\n\t\t\t\t\t\t\t`Method not found: ${serviceId}.${methodName}`,\n\t\t\t\t\t\t\t\"SERVICE_METHOD_NOT_FOUND\",\n\t\t\t\t\t\t\tserviceId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\thandleError(err);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t\t\t\tcode: err.code,\n\t\t\t\t\t\t\t\tstack: err.stack,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Execute beforeAll hook\n\t\t\t\t\tif (service.definition.beforeAll) {\n\t\t\t\t\t\tawait service.definition.beforeAll(methodName, args);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Execute handler\n\t\t\t\t\tconst result = await handler(...args);\n\n\t\t\t\t\t// Execute afterAll hook\n\t\t\t\t\tconst duration = performance.now() - startTime;\n\t\t\t\t\tif (service.definition.afterAll) {\n\t\t\t\t\t\tawait service.definition.afterAll(methodName, result, duration);\n\t\t\t\t\t}\n\n\t\t\t\t\tlog(\n\t\t\t\t\t\t`${serviceId}.${methodName}() completed in ${duration.toFixed(2)}ms`,\n\t\t\t\t\t);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tresult,\n\t\t\t\t\t};\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst duration = performance.now() - startTime;\n\t\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\n\t\t\t\t\t// Still call afterAll on error\n\t\t\t\t\tconst service = services.get(serviceId);\n\t\t\t\t\tif (service?.definition.afterAll) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait service.definition.afterAll(\n\t\t\t\t\t\t\t\tmethodName,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} catch {}\n\t\t\t\t\t}\n\n\t\t\t\t\thandleError(\n\t\t\t\t\t\tnew NanoStoreIPCError(\n\t\t\t\t\t\t\t`Service call failed: ${serviceId}.${methodName}`,\n\t\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\t\tserviceId,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\t\tcode: error.name,\n\t\t\t\t\t\t\tstack: error.stack,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t}\n\n\tlog(\"service IPC initialized\", { channelPrefix });\n\n\treturn {\n\t\tregisterService,\n\t\tbroadcast,\n\t\tdestroy: () => {\n\t\t\tservices.clear();\n\t\t\ttry {\n\t\t\t\tipcMain.removeHandler(callChannel);\n\t\t\t} catch (err) {\n\t\t\t\thandleError(\n\t\t\t\t\tnew NanoStoreIPCError(\n\t\t\t\t\t\t\"Failed to remove service IPC handler\",\n\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\terr,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tlog(\"destroyed\");\n\t\t},\n\t};\n}\n","import { app, type BrowserWindow, ipcMain } from \"electron\";\nimport type { Store } from \"nanostores\";\nimport {\n\tWF_NS_MAIN_API,\n\tWF_NS_QUEUE,\n\tWF_NS_SERVICE_QUEUE,\n} from \"../internal/symbols\";\nimport type {\n\tErrorHandler,\n\tMainApi,\n\tNanoStoreIPCError,\n\tServiceQueueEntry,\n\tSnapshot,\n} from \"../internal/types\";\nimport { NanoStoreIPCError as IPCError } from \"../internal/types\";\nimport { initServices } from \"./services\";\n\nexport interface InitNanoStoreIPCOptions {\n\tchannelPrefix?: string;\n\tenableLogging?: boolean;\n\tautoRegisterWindows?: boolean;\n\t/**\n\t * If false, renderer cannot call set() (write access). Reads + updates still work.\n\t * Default: true (DX-first).\n\t */\n\tallowRendererSet?: boolean;\n\t/**\n\t * Error handler called when errors occur in IPC operations.\n\t */\n\tonError?: ErrorHandler;\n\t/**\n\t * If true, validates that store values are serializable before broadcasting.\n\t * Recommended only for development (performance cost).\n\t * Default: false\n\t */\n\tvalidateSerialization?: boolean;\n}\n\ntype StoreEntry<T = unknown> = {\n\tstore: Store<T>;\n\trev: number;\n\tunsubscribe: () => void;\n};\n\ntype Queue = Map<string, Store<unknown>>;\n\nfunction ch(prefix: string, c: string) {\n\treturn prefix ? `${prefix}:${c}` : c;\n}\n\nlet instanceCreated = false;\n\n/**\n * Initializes a minimal IPC surface:\n * - invoke: ns:get(id) -> {id,rev,value}\n * - invoke: ns:set(id,value) -> void\n * - event: ns:update({id,rev,value}) broadcast to all registered windows\n *\n * Windows are auto-registered by default.\n *\n * Stores can be created before init; they are kept in a global queue and registered on init.\n */\nexport function initNanoStoreIPC(opts: InitNanoStoreIPCOptions = {}) {\n\tif (instanceCreated) {\n\t\tthrow new IPCError(\n\t\t\t\"initNanoStoreIPC() has already been called. Multiple initializations are not allowed.\",\n\t\t\t\"ALREADY_INITIALIZED\",\n\t\t);\n\t}\n\tinstanceCreated = true;\n\n\tconst channelPrefix = opts.channelPrefix ?? \"\";\n\tconst enableLogging = opts.enableLogging ?? false;\n\tconst autoRegisterWindows = opts.autoRegisterWindows ?? true;\n\tconst allowRendererSet = opts.allowRendererSet ?? true;\n\tconst validateSerialization = opts.validateSerialization ?? false;\n\n\tconst windows = new Set<BrowserWindow>();\n\tconst stores = new Map<string, StoreEntry>();\n\n\tconst globalWithSymbols = globalThis as Record<symbol, unknown>;\n\tconst queue: Queue =\n\t\t(globalWithSymbols[WF_NS_QUEUE] as Queue | undefined) ??\n\t\tnew Map<string, Store<unknown>>();\n\tglobalWithSymbols[WF_NS_QUEUE] = queue;\n\n\tconst log = (...args: unknown[]) => {\n\t\tif (enableLogging) console.log(\"[nanostore-ipc]\", ...args);\n\t};\n\n\t// Batching state\n\tconst pendingBroadcasts = new Map<string, Snapshot<unknown>>();\n\tlet broadcastScheduled = false;\n\n\tconst handleError = (error: NanoStoreIPCError) => {\n\t\tif (enableLogging) {\n\t\t\tconsole.error(\"[nanostore-ipc:error]\", {\n\t\t\t\tcode: error.code,\n\t\t\t\tmessage: error.message,\n\t\t\t\tstoreId: error.storeId,\n\t\t\t\toriginalError: error.originalError,\n\t\t\t});\n\t\t}\n\n\t\tif (opts.onError) {\n\t\t\ttry {\n\t\t\t\topts.onError(error);\n\t\t\t} catch (error_) {\n\t\t\t\tconsole.error(\"[nanostore-ipc] Error in error handler:\", error_);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst broadcastPending = () => {\n\t\tconst channel = ch(channelPrefix, \"ns:update\");\n\n\t\t// Clean up destroyed windows first\n\t\tfor (const win of windows) {\n\t\t\tif (win.isDestroyed()) {\n\t\t\t\twindows.delete(win);\n\t\t\t}\n\t\t}\n\n\t\t// Broadcast all pending updates\n\t\tfor (const snap of pendingBroadcasts.values()) {\n\t\t\tfor (const win of windows) {\n\t\t\t\ttry {\n\t\t\t\t\twin.webContents.send(channel, snap);\n\t\t\t\t} catch (err) {\n\t\t\t\t\thandleError(\n\t\t\t\t\t\tnew IPCError(\n\t\t\t\t\t\t\t`Failed to broadcast update for store \"${snap.id}\"`,\n\t\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\t\tsnap.id,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\t// Window might have closed during send - remove it\n\t\t\t\t\twindows.delete(win);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlog(\"broadcast:\", snap.id, `(${windows.size} windows)`);\n\t\t}\n\n\t\tpendingBroadcasts.clear();\n\t\tbroadcastScheduled = false;\n\t};\n\n\tconst broadcast = (snap: Snapshot<unknown>) => {\n\t\t// Add to batch\n\t\tpendingBroadcasts.set(snap.id, snap);\n\n\t\t// Schedule broadcast if not already scheduled\n\t\tif (!broadcastScheduled) {\n\t\t\tbroadcastScheduled = true;\n\t\t\tqueueMicrotask(broadcastPending);\n\t\t}\n\t};\n\n\tconst registerStore = <T = unknown>(id: string, store: Store<T>) => {\n\t\tif (stores.has(id)) {\n\t\t\tlog(\"store already registered, skipping:\", id);\n\t\t\treturn;\n\t\t}\n\n\t\tconst entry: StoreEntry<T> = {\n\t\t\tstore,\n\t\t\trev: 0,\n\t\t\tunsubscribe: () => {},\n\t\t};\n\n\t\tconst unsubscribe = store.subscribe((value) => {\n\t\t\tentry.rev += 1;\n\t\t\tconst snap: Snapshot<T> = { id, rev: entry.rev, value };\n\n\t\t\t// Optional: Serialization validation\n\t\t\tif (validateSerialization) {\n\t\t\t\ttry {\n\t\t\t\t\tstructuredClone(value);\n\t\t\t\t} catch (err) {\n\t\t\t\t\thandleError(\n\t\t\t\t\t\tnew IPCError(\n\t\t\t\t\t\t\t`Store value not serializable: ${id}`,\n\t\t\t\t\t\t\t\"SERIALIZATION_FAILED\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn; // Don't broadcast invalid values\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbroadcast(snap);\n\t\t});\n\n\t\tentry.unsubscribe = unsubscribe;\n\t\tstores.set(id, entry);\n\n\t\tlog(\"store registered:\", id);\n\t};\n\n\t// Initialize services\n\tconst serviceManager = initServices(windows, {\n\t\tchannelPrefix,\n\t\tenableLogging,\n\t\tonError: opts.onError,\n\t});\n\n\tconst api: MainApi = {\n\t\tregisterStore,\n\t\tisInitialized: () => true,\n\t\tregisterService: serviceManager.registerService,\n\t\tbroadcast: serviceManager.broadcast,\n\t};\n\tglobalWithSymbols[WF_NS_MAIN_API] = api;\n\n\t// Drain the store queue: auto-register stores that were created before initNanoStoreIPC()\n\tfor (const [id, store] of queue.entries()) {\n\t\tregisterStore(id, store);\n\t}\n\tqueue.clear();\n\n\t// Drain the service queue: auto-register services that were created before initNanoStoreIPC()\n\tconst serviceQueue: Map<string, ServiceQueueEntry> =\n\t\t(globalWithSymbols[WF_NS_SERVICE_QUEUE] as\n\t\t\t| Map<string, ServiceQueueEntry>\n\t\t\t| undefined) ?? new Map();\n\tfor (const [id, entry] of serviceQueue.entries()) {\n\t\tapi.registerService(id, entry.definition);\n\t\tlog(\"service registered from queue:\", id);\n\t}\n\tserviceQueue.clear();\n\n\t// IPC handlers (generic, no per-store handlers)\n\tconst getChannel = ch(channelPrefix, \"ns:get\");\n\tconst setChannel = ch(channelPrefix, \"ns:set\");\n\n\tif (ipcMain.listenerCount(getChannel) === 0) {\n\t\tipcMain.handle(getChannel, (_e, id: string) => {\n\t\t\tconst entry = stores.get(id);\n\t\t\tif (!entry) {\n\t\t\t\tconst err = new IPCError(\n\t\t\t\t\t`Store not found: ${id}`,\n\t\t\t\t\t\"STORE_NOT_FOUND\",\n\t\t\t\t\tid,\n\t\t\t\t);\n\t\t\t\thandleError(err);\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\trev: entry.rev,\n\t\t\t\tvalue: entry.store.get(),\n\t\t\t} satisfies Snapshot<unknown>;\n\t\t});\n\t}\n\n\tif (ipcMain.listenerCount(setChannel) === 0) {\n\t\tipcMain.handle(setChannel, (_e, id: string, value: unknown) => {\n\t\t\tif (!allowRendererSet) {\n\t\t\t\tconst err = new IPCError(\n\t\t\t\t\t\"Renderer writes are disabled (allowRendererSet=false)\",\n\t\t\t\t\t\"RENDERER_WRITE_DISABLED\",\n\t\t\t\t\tid,\n\t\t\t\t);\n\t\t\t\thandleError(err);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tconst entry = stores.get(id);\n\t\t\tif (!entry) {\n\t\t\t\tconst err = new IPCError(\n\t\t\t\t\t`Store not found: ${id}`,\n\t\t\t\t\t\"STORE_NOT_FOUND\",\n\t\t\t\t\tid,\n\t\t\t\t);\n\t\t\t\thandleError(err);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\t// Type-safe set with existence check\n\t\t\tconst store = entry.store as { set?: (val: unknown) => void };\n\t\t\tif (typeof store.set === \"function\") {\n\t\t\t\tstore.set(value);\n\t\t\t}\n\t\t});\n\t}\n\n\tconst registerWindow = (win: BrowserWindow) => {\n\t\tif (windows.has(win)) return;\n\t\twindows.add(win);\n\n\t\t// Cleanup on close\n\t\tconst onClosed = () => {\n\t\t\twindows.delete(win);\n\t\t\tlog(\"window unregistered:\", win.id);\n\t\t};\n\t\twin.once(\"closed\", onClosed);\n\n\t\t// Push snapshots only once per load\n\t\tconst pushSnapshots = () => {\n\t\t\tif (win.isDestroyed()) return;\n\n\t\t\tlog(\"pushing snapshots to window:\", win.id);\n\t\t\tfor (const [id, entry] of stores.entries()) {\n\t\t\t\tconst snap: Snapshot<unknown> = {\n\t\t\t\t\tid,\n\t\t\t\t\trev: entry.rev,\n\t\t\t\t\tvalue: entry.store.get(),\n\t\t\t\t};\n\t\t\t\ttry {\n\t\t\t\t\twin.webContents.send(ch(channelPrefix, \"ns:update\"), snap);\n\t\t\t\t} catch (err) {\n\t\t\t\t\thandleError(\n\t\t\t\t\t\tnew IPCError(\n\t\t\t\t\t\t\t`Failed to push snapshot for store \"${id}\"`,\n\t\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Use once() instead of on() to prevent memory leak\n\t\twin.webContents.once(\"did-finish-load\", pushSnapshots);\n\n\t\tlog(\"window registered:\", win.id);\n\t};\n\n\tif (autoRegisterWindows) {\n\t\tapp.on(\"browser-window-created\", (_event, win) => {\n\t\t\tregisterWindow(win);\n\t\t});\n\t}\n\n\tlog(\"IPC initialized\", {\n\t\tchannelPrefix,\n\t\tautoRegisterWindows,\n\t\tallowRendererSet,\n\t});\n\n\treturn {\n\t\tregisterStore,\n\t\tregisterWindow,\n\t\tdestroy: () => {\n\t\t\tlog(\"destroying IPC bridge...\");\n\n\t\t\t// Unsubscribe all stores\n\t\t\tfor (const [id, entry] of stores.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tentry.unsubscribe();\n\t\t\t\t\tlog(\"unsubscribed store:\", id);\n\t\t\t\t} catch (err) {\n\t\t\t\t\thandleError(\n\t\t\t\t\t\tnew IPCError(\n\t\t\t\t\t\t\t`Failed to unsubscribe store \"${id}\"`,\n\t\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear collections\n\t\t\tstores.clear();\n\t\t\twindows.clear();\n\n\t\t\t// Remove IPC handlers\n\t\t\ttry {\n\t\t\t\tipcMain.removeHandler(getChannel);\n\t\t\t\tipcMain.removeHandler(setChannel);\n\t\t\t} catch (err) {\n\t\t\t\thandleError(\n\t\t\t\t\tnew IPCError(\n\t\t\t\t\t\t\"Failed to remove IPC handlers\",\n\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\terr,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Destroy services\n\t\t\ttry {\n\t\t\t\tserviceManager.destroy();\n\t\t\t} catch (err) {\n\t\t\t\thandleError(\n\t\t\t\t\tnew IPCError(\n\t\t\t\t\t\t\"Failed to destroy services\",\n\t\t\t\t\t\t\"IPC_FAILED\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\terr,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { S as Snapshot } from '../types-CyJJt8gf.mjs';
|
|
2
|
+
|
|
3
|
+
interface ExposeNanoStoreIPCOptions {
|
|
4
|
+
channelPrefix?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Name under which the API is exposed to window.
|
|
7
|
+
* Default: "nanostoreIPC"
|
|
8
|
+
*/
|
|
9
|
+
globalName?: string;
|
|
10
|
+
}
|
|
11
|
+
type NanoStoreIPC = {
|
|
12
|
+
get: <T = unknown>(id: string) => Promise<Snapshot<T>>;
|
|
13
|
+
set: <T = unknown>(id: string, value: T) => Promise<void>;
|
|
14
|
+
subscribe: <T = unknown>(id: string, cb: (snap: Snapshot<T>) => void) => () => void;
|
|
15
|
+
subscribeAll: (cb: (snap: Snapshot<unknown>) => void) => () => void;
|
|
16
|
+
callService: <T = unknown>(serviceId: string, method: string, ...args: unknown[]) => Promise<T>;
|
|
17
|
+
subscribeServiceEvent: (serviceId: string, eventName: string, cb: (data: unknown) => void) => () => void;
|
|
18
|
+
};
|
|
19
|
+
declare function exposeNanoStoreIPC(opts?: ExposeNanoStoreIPCOptions): NanoStoreIPC;
|
|
20
|
+
|
|
21
|
+
export { type ExposeNanoStoreIPCOptions, type NanoStoreIPC, exposeNanoStoreIPC };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { S as Snapshot } from '../types-CyJJt8gf.js';
|
|
2
|
+
|
|
3
|
+
interface ExposeNanoStoreIPCOptions {
|
|
4
|
+
channelPrefix?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Name under which the API is exposed to window.
|
|
7
|
+
* Default: "nanostoreIPC"
|
|
8
|
+
*/
|
|
9
|
+
globalName?: string;
|
|
10
|
+
}
|
|
11
|
+
type NanoStoreIPC = {
|
|
12
|
+
get: <T = unknown>(id: string) => Promise<Snapshot<T>>;
|
|
13
|
+
set: <T = unknown>(id: string, value: T) => Promise<void>;
|
|
14
|
+
subscribe: <T = unknown>(id: string, cb: (snap: Snapshot<T>) => void) => () => void;
|
|
15
|
+
subscribeAll: (cb: (snap: Snapshot<unknown>) => void) => () => void;
|
|
16
|
+
callService: <T = unknown>(serviceId: string, method: string, ...args: unknown[]) => Promise<T>;
|
|
17
|
+
subscribeServiceEvent: (serviceId: string, eventName: string, cb: (data: unknown) => void) => () => void;
|
|
18
|
+
};
|
|
19
|
+
declare function exposeNanoStoreIPC(opts?: ExposeNanoStoreIPCOptions): NanoStoreIPC;
|
|
20
|
+
|
|
21
|
+
export { type ExposeNanoStoreIPCOptions, type NanoStoreIPC, exposeNanoStoreIPC };
|