@aiot-nexus/plugin-sdk 2.0.2 → 2.1.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.
Files changed (32) hide show
  1. package/README.md +18 -8
  2. package/dev-host/host.html +13 -12
  3. package/dev-host/main.js +134 -9
  4. package/dev-host/plugin-host.js +1 -427
  5. package/dist/.generated/plugin-framework-version.d.ts +1 -1
  6. package/dist/.generated/plugin-framework-version.js +1 -1
  7. package/dist/.generated/plugin-manifest.schema.json +1 -1
  8. package/dist/internal/src-electron/main/core/runtime-result.js +42 -0
  9. package/dist/internal/src-electron/main/plugins/plugin-electron-facade-broker.js +890 -0
  10. package/dist/internal/src-electron/plugin-host-bootstrap.js +417 -0
  11. package/dist/internal/src-electron/shared/contracts.js +2 -0
  12. package/dist/internal/src-electron/shared/plugin-electron-facade-client.js +181 -0
  13. package/dist/internal/src-electron/shared/plugin-electron-facade-descriptors.js +70 -0
  14. package/dist/internal/src-electron/shared/plugin-electron-facade.js +243 -0
  15. package/dist/internal/src-electron/shared/plugin-rpc.js +245 -0
  16. package/dist/src/electron-facade.d.ts +4 -0
  17. package/dist/src/electron-facade.js +56 -0
  18. package/dist/src/index.d.ts +2 -1
  19. package/dist/src/index.js +2 -1
  20. package/dist/src/nexus-dev.js +5 -4
  21. package/dist/src/scaffold.js +2 -2
  22. package/dist/src/types.d.ts +86 -2
  23. package/dist/src/web-dev.js +2 -2
  24. package/package.json +7 -2
  25. package/templates/minimal/README.md.tmpl +84 -3
  26. package/templates/minimal/src/main.ts.tmpl +2 -1
  27. package/templates/sqlite/README.md.tmpl +30 -2
  28. package/templates/sqlite/src/better-sqlite3.d.ts +12 -1
  29. package/templates/sqlite/src/main.ts.tmpl +1034 -124
  30. package/templates/sqlite/src/renderer/app.ts.tmpl +691 -180
  31. package/templates/sqlite/src/renderer/index.html +40 -22
  32. package/templates/sqlite/src/renderer/plugin.css +302 -90
@@ -0,0 +1,890 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.PluginElectronFacadeBrokerService = void 0;
27
+ const path = __importStar(require("path"));
28
+ const plugin_electron_facade_1 = require("../../shared/plugin-electron-facade");
29
+ const plugin_electron_facade_descriptors_1 = require("../../shared/plugin-electron-facade-descriptors");
30
+ const runtime_result_1 = require("../core/runtime-result");
31
+ class ElectronFacadeBrokerError extends Error {
32
+ constructor(code, message) {
33
+ super(message);
34
+ this.code = code;
35
+ this.name = "ElectronFacadeBrokerError";
36
+ }
37
+ }
38
+ const basicModules = new Set(plugin_electron_facade_descriptors_1.electronFacadeExportDescriptors
39
+ .filter((descriptor) => descriptor.defaultAccess === "basic")
40
+ .map((descriptor) => descriptor.name));
41
+ const ownedConstructorModules = new Set(plugin_electron_facade_descriptors_1.electronFacadeExportDescriptors
42
+ .filter((descriptor) => descriptor.defaultAccess === "owned" && descriptor.kind === "constructor")
43
+ .map((descriptor) => descriptor.name));
44
+ const ownedServiceModules = new Set(plugin_electron_facade_descriptors_1.electronFacadeExportDescriptors
45
+ .filter((descriptor) => descriptor.defaultAccess === "owned" && descriptor.kind === "module")
46
+ .map((descriptor) => descriptor.name));
47
+ const defaultAppMembers = new Set([
48
+ "getAppPath",
49
+ "getLocale",
50
+ "getLocaleCountryCode",
51
+ "getName",
52
+ "getPath",
53
+ "getVersion",
54
+ "isDefaultProtocolClient",
55
+ "isPackaged",
56
+ "isReady",
57
+ "name",
58
+ "userAgentFallback"
59
+ ]);
60
+ class PluginElectronFacadeBrokerService {
61
+ constructor(injectedRoot) {
62
+ this.injectedRoot = injectedRoot;
63
+ this.states = new Map();
64
+ this.nextGeneration = 0;
65
+ }
66
+ attachPlugin(pluginId, binding) {
67
+ this.disposePlugin(pluginId);
68
+ const namespaceId = pluginId.replace(/[^a-z0-9._-]/gi, "-");
69
+ this.states.set(pluginId, {
70
+ pluginId,
71
+ binding,
72
+ generation: ++this.nextGeneration,
73
+ nextHandleId: 0,
74
+ handles: new Map(),
75
+ reverseHandles: new WeakMap(),
76
+ shortcuts: new Set(),
77
+ powerSaveBlockers: new Set(),
78
+ ipcHandlers: new Set(),
79
+ ipcListeners: [],
80
+ protocols: [],
81
+ sessionPartition: "persist:nexus-plugin:" + namespaceId,
82
+ ipcNamespace: "nexus-plugin:" + namespaceId + ":",
83
+ protocolNamespace: "nexus-plugin-" + namespaceId + "-"
84
+ });
85
+ }
86
+ async invoke(pluginId, value) {
87
+ if (!(0, plugin_electron_facade_1.isElectronFacadeRequest)(value)) {
88
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.InvalidRequest, "Electron facade request is invalid.");
89
+ }
90
+ const state = this.states.get(pluginId);
91
+ if (!state) {
92
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ProcessUnavailable, "Electron facade plugin generation is unavailable.");
93
+ }
94
+ try {
95
+ if (value.operation === "release") {
96
+ if (!this.releaseHandle(state, value.handleId)) {
97
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.HandleReleased, "Electron facade handle was already released.", {
98
+ handleId: value.handleId
99
+ });
100
+ }
101
+ return (0, runtime_result_1.ok)({ $nexusElectron: "undefined" });
102
+ }
103
+ if (value.operation === "construct") {
104
+ return this.construct(pluginId, state, value);
105
+ }
106
+ const targetResult = this.resolveTarget(state, value);
107
+ if (!targetResult.ok) {
108
+ return targetResult;
109
+ }
110
+ const target = targetResult.value;
111
+ const member = value.member;
112
+ if (value.operation === "get") {
113
+ const handle = value.handleId ? state.handles.get(value.handleId) : undefined;
114
+ if (handle?.typeName === "Session" && member === "protocol") {
115
+ const protocol = Reflect.get(handle.value, member);
116
+ if (!isObjectLike(protocol)) {
117
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.MemberUnavailable, "Electron Session protocol is unavailable.");
118
+ }
119
+ return (0, runtime_result_1.ok)(this.encodeHandle(pluginId, state, this.createProtocolModuleTarget(state, state.binding.hostControl, protocol), { typeName: state.binding.hostControl ? "Protocol" : "PluginProtocol" }));
120
+ }
121
+ return (0, runtime_result_1.ok)(this.encodeResult(pluginId, state, Reflect.get(target, member)));
122
+ }
123
+ if (value.operation === "set") {
124
+ if (value.module && !state.binding.hostControl) {
125
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.MemberUnavailable, "Electron facade modules are read-only.", { module: value.module, member });
126
+ }
127
+ const decoded = this.decodeArguments(pluginId, state, value.args || []);
128
+ if (!Reflect.set(target, member, decoded[0])) {
129
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.InvocationFailed, "Electron facade property could not be assigned.", { member });
130
+ }
131
+ return (0, runtime_result_1.ok)({ $nexusElectron: "undefined" });
132
+ }
133
+ if (value.operation === "on" || value.operation === "once") {
134
+ return this.subscribe(pluginId, state, target, member, value.args?.[0], value.operation === "once");
135
+ }
136
+ const candidate = Reflect.get(target, member);
137
+ if (typeof candidate !== "function") {
138
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.MemberUnavailable, "Electron facade member is not callable.", {
139
+ module: value.module,
140
+ handleId: value.handleId,
141
+ member
142
+ });
143
+ }
144
+ this.assertSpecialMemberAvailable(state, value, member);
145
+ const args = this.decodeArguments(pluginId, state, value.args || []);
146
+ const result = await candidate.apply(target, args);
147
+ if (this.states.get(pluginId) !== state) {
148
+ this.disposeDetachedValue(result);
149
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ProcessUnavailable, "Electron facade plugin generation changed during invocation.");
150
+ }
151
+ return (0, runtime_result_1.ok)(this.encodeCallResult(pluginId, state, value.module, member, result));
152
+ }
153
+ catch (error) {
154
+ if (error instanceof ElectronFacadeBrokerError) {
155
+ return (0, runtime_result_1.fail)(error.code, error.message);
156
+ }
157
+ if (error instanceof plugin_electron_facade_1.ElectronFacadeCodecError) {
158
+ return (0, runtime_result_1.fail)(error.code, error.message);
159
+ }
160
+ const message = error instanceof Error ? error.message : String(error);
161
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.InvocationFailed, "Electron facade invocation failed.", message);
162
+ }
163
+ }
164
+ disposePlugin(pluginId) {
165
+ const state = this.states.get(pluginId);
166
+ if (!state) {
167
+ return;
168
+ }
169
+ this.states.delete(pluginId);
170
+ for (const handleId of Array.from(state.handles.keys()).reverse()) {
171
+ this.releaseHandle(state, handleId);
172
+ }
173
+ const root = this.electronRootIfAvailable();
174
+ const globalShortcut = asObject(root?.globalShortcut);
175
+ for (const accelerator of state.shortcuts) {
176
+ safeCall(globalShortcut, "unregister", accelerator);
177
+ }
178
+ const powerSaveBlocker = asObject(root?.powerSaveBlocker);
179
+ for (const blockerId of state.powerSaveBlockers) {
180
+ safeCall(powerSaveBlocker, "stop", blockerId);
181
+ }
182
+ state.shortcuts.clear();
183
+ state.powerSaveBlockers.clear();
184
+ const ipcMain = asObject(root?.ipcMain);
185
+ for (const channel of state.ipcHandlers) {
186
+ safeCall(ipcMain, "removeHandler", channel);
187
+ }
188
+ for (const registration of state.ipcListeners) {
189
+ safeCall(ipcMain, "removeListener", registration.channel, registration.listener);
190
+ }
191
+ for (const registration of state.protocols) {
192
+ safeCall(registration.target, "unregisterProtocol", registration.scheme);
193
+ }
194
+ state.ipcHandlers.clear();
195
+ state.ipcListeners.length = 0;
196
+ state.protocols.length = 0;
197
+ }
198
+ construct(pluginId, state, request) {
199
+ const moduleName = request.module;
200
+ const descriptor = (0, plugin_electron_facade_descriptors_1.getElectronFacadeExportDescriptor)(moduleName);
201
+ const canConstruct = descriptor?.kind === "constructor"
202
+ && (ownedConstructorModules.has(moduleName) || state.binding.hostControl);
203
+ if (!canConstruct) {
204
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ModuleUnavailable, "Electron facade constructor is unavailable.", { module: moduleName });
205
+ }
206
+ const candidate = this.electronRoot()[moduleName];
207
+ if (typeof candidate !== "function") {
208
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ModuleUnavailable, "Electron constructor is unavailable in this process.", { module: moduleName });
209
+ }
210
+ const args = this.decodeArguments(pluginId, state, request.args || []);
211
+ const normalizedArgs = !state.binding.hostControl && (moduleName === "BrowserWindow" || moduleName === "BrowserView")
212
+ ? this.normalizeWebContentsConstructorArgs(state, args)
213
+ : args;
214
+ const result = Reflect.construct(candidate, normalizedArgs);
215
+ const encoded = this.encodeHandle(pluginId, state, result, {
216
+ typeName: moduleName,
217
+ dispose: this.createOwnedDisposer(moduleName, result)
218
+ });
219
+ if (moduleName === "BrowserWindow") {
220
+ this.attachClosedHandleInvalidation(state, result, readHandleId(encoded));
221
+ }
222
+ return (0, runtime_result_1.ok)(encoded);
223
+ }
224
+ resolveTarget(state, request) {
225
+ if (request.module) {
226
+ const moduleName = request.module;
227
+ const descriptor = (0, plugin_electron_facade_descriptors_1.getElectronFacadeExportDescriptor)(moduleName);
228
+ if (descriptor?.process === "renderer") {
229
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ProcessUnavailable, "Electron renderer-only API is unavailable in the plugin worker.", {
230
+ module: moduleName
231
+ });
232
+ }
233
+ if (state.binding.hostControl && descriptor) {
234
+ if (moduleName === "ipcMain") {
235
+ return (0, runtime_result_1.ok)(this.createIpcMainModuleTarget(state, true));
236
+ }
237
+ if (moduleName === "protocol") {
238
+ return (0, runtime_result_1.ok)(this.createProtocolModuleTarget(state, true));
239
+ }
240
+ const hostModule = this.electronRoot()[moduleName];
241
+ if (!isObjectLike(hostModule)) {
242
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ModuleUnavailable, "Electron host module is unavailable in this process.", {
243
+ module: moduleName
244
+ });
245
+ }
246
+ return (0, runtime_result_1.ok)(hostModule);
247
+ }
248
+ if (basicModules.has(moduleName)) {
249
+ if (moduleName === "app" && !defaultAppMembers.has(request.member)) {
250
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.MemberUnavailable, "Electron app member is not available to the default facade.", {
251
+ member: request.member
252
+ });
253
+ }
254
+ const moduleValue = this.electronRoot()[moduleName];
255
+ if (!isObjectLike(moduleValue)) {
256
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ModuleUnavailable, "Electron facade module is unavailable in this process.", {
257
+ module: moduleName
258
+ });
259
+ }
260
+ return (0, runtime_result_1.ok)(moduleValue);
261
+ }
262
+ if (ownedConstructorModules.has(moduleName) || ownedServiceModules.has(moduleName)) {
263
+ return (0, runtime_result_1.ok)(this.createOwnedModuleTarget(state, moduleName));
264
+ }
265
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.ModuleUnavailable, "Electron facade module is not available to plugin-owned resources.", {
266
+ module: moduleName
267
+ });
268
+ }
269
+ const record = state.handles.get(request.handleId);
270
+ if (!record) {
271
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.HandleReleased, "Electron facade handle is unavailable or released.", {
272
+ handleId: request.handleId
273
+ });
274
+ }
275
+ return (0, runtime_result_1.ok)(record.value);
276
+ }
277
+ createOwnedModuleTarget(state, moduleName) {
278
+ const root = this.electronRoot();
279
+ if (moduleName === "BrowserWindow") {
280
+ return {
281
+ getAllWindows: () => this.ownedValues(state, "BrowserWindow"),
282
+ getFocusedWindow: () => this.ownedValues(state, "BrowserWindow").find((item) => safeBooleanCall(item, "isFocused")) || null,
283
+ fromId: (id) => this.ownedValues(state, "BrowserWindow").find((item) => Reflect.get(item, "id") === id) || null
284
+ };
285
+ }
286
+ if (moduleName === "BrowserView") {
287
+ return {};
288
+ }
289
+ if (moduleName === "Menu") {
290
+ const menu = asObject(root.Menu);
291
+ return {
292
+ buildFromTemplate: (...args) => requireFunction(menu, "buildFromTemplate").apply(menu, args)
293
+ };
294
+ }
295
+ if (moduleName === "Notification") {
296
+ const notification = asObject(root.Notification);
297
+ return {
298
+ isSupported: () => requireFunction(notification, "isSupported").apply(notification, [])
299
+ };
300
+ }
301
+ if (moduleName === "Tray") {
302
+ return {};
303
+ }
304
+ if (moduleName === "session") {
305
+ const session = asObject(root.session);
306
+ return {
307
+ fromPartition: (partition, options) => requireFunction(session, "fromPartition").call(session, this.namespacedPartition(state, partition), options)
308
+ };
309
+ }
310
+ if (moduleName === "net") {
311
+ const net = asObject(root.net);
312
+ return {
313
+ request: (options) => requireFunction(net, "request").call(net, this.normalizeNetRequestOptions(state, options)),
314
+ isOnline: () => requireFunction(net, "isOnline").call(net),
315
+ resolveHost: (...args) => requireFunction(net, "resolveHost").apply(net, args)
316
+ };
317
+ }
318
+ if (moduleName === "globalShortcut") {
319
+ const shortcuts = asObject(root.globalShortcut);
320
+ return {
321
+ register: (accelerator, callback) => {
322
+ if (typeof accelerator !== "string" || typeof callback !== "function") {
323
+ throw new Error("globalShortcut.register requires an accelerator and callback.");
324
+ }
325
+ const registered = Boolean(requireFunction(shortcuts, "register").call(shortcuts, accelerator, callback));
326
+ if (registered) {
327
+ state.shortcuts.add(accelerator);
328
+ }
329
+ return registered;
330
+ },
331
+ isRegistered: (accelerator) => typeof accelerator === "string"
332
+ && state.shortcuts.has(accelerator)
333
+ && Boolean(requireFunction(shortcuts, "isRegistered").call(shortcuts, accelerator)),
334
+ unregister: (accelerator) => {
335
+ if (typeof accelerator === "string" && state.shortcuts.delete(accelerator)) {
336
+ requireFunction(shortcuts, "unregister").call(shortcuts, accelerator);
337
+ }
338
+ },
339
+ unregisterAll: () => {
340
+ for (const accelerator of state.shortcuts) {
341
+ requireFunction(shortcuts, "unregister").call(shortcuts, accelerator);
342
+ }
343
+ state.shortcuts.clear();
344
+ }
345
+ };
346
+ }
347
+ if (moduleName === "powerSaveBlocker") {
348
+ const blockers = asObject(root.powerSaveBlocker);
349
+ return {
350
+ start: (type) => {
351
+ const blockerId = Number(requireFunction(blockers, "start").call(blockers, type));
352
+ state.powerSaveBlockers.add(blockerId);
353
+ return blockerId;
354
+ },
355
+ isStarted: (blockerId) => typeof blockerId === "number"
356
+ && state.powerSaveBlockers.has(blockerId)
357
+ && Boolean(requireFunction(blockers, "isStarted").call(blockers, blockerId)),
358
+ stop: (blockerId) => {
359
+ if (typeof blockerId === "number" && state.powerSaveBlockers.delete(blockerId)) {
360
+ requireFunction(blockers, "stop").call(blockers, blockerId);
361
+ }
362
+ }
363
+ };
364
+ }
365
+ if (moduleName === "utilityProcess") {
366
+ const utilityProcess = asObject(root.utilityProcess);
367
+ return {
368
+ fork: (modulePath, args, options) => {
369
+ if (typeof modulePath !== "string") {
370
+ throw new Error("utilityProcess.fork module path must be a string.");
371
+ }
372
+ const resolved = path.resolve(state.binding.pluginDirectory, modulePath);
373
+ if (!isPathInside(state.binding.pluginDirectory, resolved)) {
374
+ throw new Error("utilityProcess.fork module path must stay inside the plugin directory.");
375
+ }
376
+ const normalizedOptions = isPlainRecord(options) ? { ...options, cwd: state.binding.pluginDirectory } : { cwd: state.binding.pluginDirectory };
377
+ return requireFunction(utilityProcess, "fork").call(utilityProcess, resolved, Array.isArray(args) ? args : [], normalizedOptions);
378
+ }
379
+ };
380
+ }
381
+ if (moduleName === "ipcMain") {
382
+ return this.createIpcMainModuleTarget(state, false);
383
+ }
384
+ if (moduleName === "protocol") {
385
+ return this.createProtocolModuleTarget(state, false);
386
+ }
387
+ throw new Error("Electron owned module is unavailable: " + moduleName + ".");
388
+ }
389
+ createIpcMainModuleTarget(state, hostControl) {
390
+ const ipcMain = asObject(this.electronRoot().ipcMain);
391
+ const channelName = (value) => hostControl
392
+ ? this.globalIpcChannel(value)
393
+ : this.namespacedIpcChannel(state, value);
394
+ return createFacadeModuleProxy(ipcMain, {
395
+ namespace: hostControl ? null : state.ipcNamespace,
396
+ handle: (channel, listener) => {
397
+ if (typeof listener !== "function") {
398
+ throw new Error("ipcMain.handle requires an async listener.");
399
+ }
400
+ const resolvedChannel = channelName(channel);
401
+ requireFunction(ipcMain, "handle").call(ipcMain, resolvedChannel, listener);
402
+ state.ipcHandlers.add(resolvedChannel);
403
+ },
404
+ handleOnce: (channel, listener) => {
405
+ if (typeof listener !== "function") {
406
+ throw new Error("ipcMain.handleOnce requires an async listener.");
407
+ }
408
+ const resolvedChannel = channelName(channel);
409
+ requireFunction(ipcMain, "handleOnce").call(ipcMain, resolvedChannel, listener);
410
+ state.ipcHandlers.add(resolvedChannel);
411
+ },
412
+ removeHandler: (channel) => {
413
+ const resolvedChannel = channelName(channel);
414
+ if (hostControl || state.ipcHandlers.has(resolvedChannel)) {
415
+ requireFunction(ipcMain, "removeHandler").call(ipcMain, resolvedChannel);
416
+ state.ipcHandlers.delete(resolvedChannel);
417
+ }
418
+ },
419
+ on: (channel, listener) => this.registerIpcListener(state, ipcMain, channelName(channel), listener, "on"),
420
+ once: (channel, listener) => this.registerIpcListener(state, ipcMain, channelName(channel), listener, "once"),
421
+ addListener: (channel, listener) => this.registerIpcListener(state, ipcMain, channelName(channel), listener, "addListener"),
422
+ prependListener: (channel, listener) => this.registerIpcListener(state, ipcMain, channelName(channel), listener, "prependListener"),
423
+ prependOnceListener: (channel, listener) => this.registerIpcListener(state, ipcMain, channelName(channel), listener, "prependOnceListener"),
424
+ removeListener: (channel, listener) => {
425
+ const namespaced = channelName(channel);
426
+ if (typeof listener !== "function") {
427
+ return;
428
+ }
429
+ requireFunction(ipcMain, "removeListener").call(ipcMain, namespaced, listener);
430
+ state.ipcListeners = state.ipcListeners.filter((item) => item.channel !== namespaced || item.listener !== listener);
431
+ },
432
+ off: (channel, listener) => {
433
+ const resolvedChannel = channelName(channel);
434
+ if (typeof listener !== "function") {
435
+ return;
436
+ }
437
+ requireFunction(ipcMain, "off").call(ipcMain, resolvedChannel, listener);
438
+ state.ipcListeners = state.ipcListeners.filter((item) => item.channel !== resolvedChannel || item.listener !== listener);
439
+ },
440
+ removeAllListeners: (channel) => {
441
+ if (hostControl) {
442
+ if (channel === undefined) {
443
+ requireFunction(ipcMain, "removeAllListeners").call(ipcMain);
444
+ state.ipcListeners.length = 0;
445
+ return;
446
+ }
447
+ const resolvedChannel = channelName(channel);
448
+ requireFunction(ipcMain, "removeAllListeners").call(ipcMain, resolvedChannel);
449
+ state.ipcListeners = state.ipcListeners.filter((item) => item.channel !== resolvedChannel);
450
+ return;
451
+ }
452
+ const channels = channel === undefined
453
+ ? Array.from(new Set(state.ipcListeners.map((item) => item.channel)))
454
+ : [channelName(channel)];
455
+ for (const namespaced of channels) {
456
+ requireFunction(ipcMain, "removeAllListeners").call(ipcMain, namespaced);
457
+ state.ipcListeners = state.ipcListeners.filter((item) => item.channel !== namespaced);
458
+ }
459
+ }
460
+ });
461
+ }
462
+ registerIpcListener(state, ipcMain, resolvedChannel, listener, method) {
463
+ if (typeof listener !== "function") {
464
+ throw new Error("ipcMain event registration requires a listener.");
465
+ }
466
+ requireFunction(ipcMain, method).call(ipcMain, resolvedChannel, listener);
467
+ state.ipcListeners.push({ channel: resolvedChannel, listener: listener });
468
+ }
469
+ createProtocolModuleTarget(state, hostControl, protocolOverride) {
470
+ const protocol = protocolOverride || (hostControl
471
+ ? this.electronRoot().protocol
472
+ : asObject(this.pluginSession(state))?.protocol);
473
+ const target = asObject(protocol);
474
+ const schemeName = (value) => hostControl
475
+ ? this.globalProtocolScheme(value)
476
+ : this.namespacedProtocolScheme(state, value);
477
+ return createFacadeModuleProxy(target, {
478
+ namespace: hostControl ? null : state.protocolNamespace,
479
+ isProtocolRegistered: (scheme) => requireFunction(target, "isProtocolRegistered").call(target, schemeName(scheme)),
480
+ unregisterProtocol: (scheme) => this.unregisterProtocol(state, target, schemeName(scheme), hostControl),
481
+ registerFileProtocol: (scheme, handler) => this.registerProtocol(state, target, "registerFileProtocol", schemeName(scheme), handler),
482
+ registerBufferProtocol: (scheme, handler) => this.registerProtocol(state, target, "registerBufferProtocol", schemeName(scheme), handler),
483
+ registerStringProtocol: (scheme, handler) => this.registerProtocol(state, target, "registerStringProtocol", schemeName(scheme), handler),
484
+ registerHttpProtocol: (scheme, handler) => this.registerProtocol(state, target, "registerHttpProtocol", schemeName(scheme), handler),
485
+ registerStreamProtocol: (scheme, handler) => this.registerProtocol(state, target, "registerStreamProtocol", schemeName(scheme), handler)
486
+ });
487
+ }
488
+ registerProtocol(state, protocol, method, scheme, handler) {
489
+ if (typeof handler !== "function") {
490
+ throw new Error(method + " requires an async handler.");
491
+ }
492
+ const nativeHandler = (request, callback) => {
493
+ void Promise.resolve(handler(request)).then((response) => callback(response), () => callback({ error: -2 }));
494
+ };
495
+ const registered = Boolean(requireFunction(protocol, method).call(protocol, scheme, nativeHandler));
496
+ if (registered) {
497
+ state.protocols.push({ scheme, target: protocol });
498
+ }
499
+ return registered;
500
+ }
501
+ unregisterProtocol(state, protocol, scheme, allowUnowned) {
502
+ const registrationIndex = state.protocols.findIndex((item) => item.scheme === scheme && item.target === protocol);
503
+ if (!allowUnowned && registrationIndex < 0) {
504
+ return false;
505
+ }
506
+ const unregistered = Boolean(requireFunction(protocol, "unregisterProtocol").call(protocol, scheme));
507
+ if (unregistered && registrationIndex >= 0) {
508
+ state.protocols.splice(registrationIndex, 1);
509
+ }
510
+ return unregistered;
511
+ }
512
+ subscribe(pluginId, state, target, eventName, callbackValue, once) {
513
+ const callbackId = readCallbackId(callbackValue);
514
+ const addListener = Reflect.get(target, once ? "once" : "on");
515
+ const removeListener = Reflect.get(target, "removeListener");
516
+ if (typeof addListener !== "function" || typeof removeListener !== "function") {
517
+ return (0, runtime_result_1.fail)(plugin_electron_facade_1.ElectronFacadeErrorCodes.MemberUnavailable, "Electron facade target does not support events.", { eventName });
518
+ }
519
+ const listener = (...args) => {
520
+ if (this.states.get(pluginId) !== state) {
521
+ return;
522
+ }
523
+ state.binding.emitEvent(callbackId, args.map((item) => this.encodeEventArgument(pluginId, state, item)));
524
+ };
525
+ addListener.call(target, eventName, listener);
526
+ const subscription = {};
527
+ return (0, runtime_result_1.ok)(this.encodeHandle(pluginId, state, subscription, {
528
+ typeName: "ElectronEventSubscription",
529
+ callbackId,
530
+ dispose: () => {
531
+ removeListener.call(target, eventName, listener);
532
+ }
533
+ }));
534
+ }
535
+ decodeArguments(pluginId, state, values) {
536
+ return values.map((item) => (0, plugin_electron_facade_1.decodeElectronFacadeValue)(item, {
537
+ decodeHandle: (handleId) => {
538
+ const record = state.handles.get(handleId);
539
+ if (!record) {
540
+ throw new ElectronFacadeBrokerError(plugin_electron_facade_1.ElectronFacadeErrorCodes.HandleReleased, "Electron facade handle is unavailable or belongs to another plugin generation.");
541
+ }
542
+ return record.value;
543
+ },
544
+ decodeFunction: (callbackId) => (...args) => this.invokeRemoteCallback(pluginId, state, callbackId, args)
545
+ }));
546
+ }
547
+ async invokeRemoteCallback(pluginId, state, callbackId, args) {
548
+ if (this.states.get(pluginId) !== state) {
549
+ throw new Error("Electron facade callback belongs to an expired plugin generation.");
550
+ }
551
+ const result = await state.binding.invokeCallback(callbackId, args.map((item) => this.encodeResult(pluginId, state, item)));
552
+ if (!result.ok) {
553
+ const error = new Error(result.error.message);
554
+ error.name = result.error.code;
555
+ throw error;
556
+ }
557
+ return (0, plugin_electron_facade_1.decodeElectronFacadeValue)(result.value, {
558
+ decodeHandle: (handleId) => {
559
+ const record = state.handles.get(handleId);
560
+ if (!record) {
561
+ throw new ElectronFacadeBrokerError(plugin_electron_facade_1.ElectronFacadeErrorCodes.HandleReleased, "Electron callback returned an unavailable handle.");
562
+ }
563
+ return record.value;
564
+ }
565
+ });
566
+ }
567
+ encodeCallResult(pluginId, state, moduleName, member, value) {
568
+ if (moduleName === "Menu" && member === "buildFromTemplate" && isObjectLike(value)) {
569
+ return this.encodeHandle(pluginId, state, value, {
570
+ typeName: "Menu",
571
+ dispose: this.createOwnedDisposer("Menu", value)
572
+ });
573
+ }
574
+ if (moduleName === "net" && member === "request" && isObjectLike(value)) {
575
+ return this.encodeHandle(pluginId, state, value, {
576
+ typeName: "ClientRequest",
577
+ dispose: this.createOwnedDisposer("ClientRequest", value)
578
+ });
579
+ }
580
+ if (moduleName === "utilityProcess" && member === "fork" && isObjectLike(value)) {
581
+ return this.encodeHandle(pluginId, state, value, {
582
+ typeName: "UtilityProcess",
583
+ dispose: this.createOwnedDisposer("UtilityProcess", value)
584
+ });
585
+ }
586
+ if (moduleName === "session" && member === "fromPartition" && isObjectLike(value)) {
587
+ return this.encodeHandle(pluginId, state, value, { typeName: "Session" });
588
+ }
589
+ return this.encodeResult(pluginId, state, value);
590
+ }
591
+ encodeResult(pluginId, state, value) {
592
+ return (0, plugin_electron_facade_1.encodeElectronFacadeValue)(value, {
593
+ encodeObject: (objectValue) => this.encodeHandle(pluginId, state, objectValue)
594
+ });
595
+ }
596
+ encodeEventArgument(pluginId, state, value) {
597
+ if (!isElectronEvent(value)) {
598
+ return this.encodeResult(pluginId, state, value);
599
+ }
600
+ const sender = safeGet(value, "sender");
601
+ const senderId = isObjectLike(sender) ? safeGet(sender, "id") : undefined;
602
+ return this.encodeResult(pluginId, state, {
603
+ defaultPrevented: safeGet(value, "defaultPrevented") === true,
604
+ preventDefaultSupported: false,
605
+ ...((typeof senderId === "number" || typeof senderId === "string") ? { senderId } : {})
606
+ });
607
+ }
608
+ encodeHandle(pluginId, state, value, options = {}) {
609
+ const existing = state.reverseHandles.get(value);
610
+ const handleId = existing || pluginId + ":electron:g" + String(state.generation) + ":" + String(++state.nextHandleId);
611
+ const typeName = options.typeName || value.constructor?.name || "ElectronObject";
612
+ if (!existing) {
613
+ state.handles.set(handleId, {
614
+ value,
615
+ typeName,
616
+ ...(options.callbackId ? { callbackId: options.callbackId } : {}),
617
+ ...(options.dispose ? { dispose: options.dispose } : {})
618
+ });
619
+ state.reverseHandles.set(value, handleId);
620
+ }
621
+ const record = state.handles.get(handleId);
622
+ return {
623
+ $nexusElectron: "handle",
624
+ handleId,
625
+ typeName: record.typeName,
626
+ ...(record.callbackId ? { callbackId: record.callbackId } : {})
627
+ };
628
+ }
629
+ releaseHandle(state, handleId, disposeResource = true) {
630
+ const record = state.handles.get(handleId);
631
+ if (!record) {
632
+ return false;
633
+ }
634
+ state.handles.delete(handleId);
635
+ state.reverseHandles.delete(record.value);
636
+ if (disposeResource) {
637
+ try {
638
+ record.dispose?.();
639
+ }
640
+ catch {
641
+ // Cleanup is best effort; all remaining plugin resources must still be released.
642
+ }
643
+ }
644
+ return true;
645
+ }
646
+ normalizeWebContentsConstructorArgs(state, args) {
647
+ const options = isPlainRecord(args[0]) ? { ...args[0] } : {};
648
+ const webPreferences = isPlainRecord(options.webPreferences) ? { ...options.webPreferences } : {};
649
+ delete webPreferences.preload;
650
+ Object.assign(webPreferences, {
651
+ contextIsolation: true,
652
+ allowRunningInsecureContent: false,
653
+ nodeIntegration: false,
654
+ nodeIntegrationInSubFrames: false,
655
+ nodeIntegrationInWorker: false,
656
+ sandbox: true,
657
+ webSecurity: true,
658
+ webviewTag: false,
659
+ partition: state.sessionPartition
660
+ });
661
+ options.webPreferences = webPreferences;
662
+ return [options, ...args.slice(1)];
663
+ }
664
+ normalizeNetRequestOptions(state, value) {
665
+ const session = this.pluginSession(state);
666
+ return typeof value === "string"
667
+ ? { url: value, session }
668
+ : { ...(isPlainRecord(value) ? value : {}), session };
669
+ }
670
+ pluginSession(state) {
671
+ const session = asObject(this.electronRoot().session);
672
+ return requireFunction(session, "fromPartition").call(session, state.sessionPartition);
673
+ }
674
+ pluginSessionIfAvailable(state) {
675
+ try {
676
+ return asObject(this.pluginSession(state));
677
+ }
678
+ catch {
679
+ return undefined;
680
+ }
681
+ }
682
+ namespacedIpcChannel(state, value) {
683
+ return state.ipcNamespace + this.globalIpcChannel(value);
684
+ }
685
+ globalIpcChannel(value) {
686
+ if (typeof value !== "string" || !/^[A-Za-z0-9._:-]{1,120}$/.test(value)) {
687
+ throw new Error("Plugin IPC channel is invalid.");
688
+ }
689
+ return value;
690
+ }
691
+ namespacedProtocolScheme(state, value) {
692
+ return (state.protocolNamespace + this.globalProtocolScheme(value)).toLowerCase();
693
+ }
694
+ globalProtocolScheme(value) {
695
+ if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9+.-]{0,39}$/.test(value)) {
696
+ throw new Error("Plugin protocol scheme is invalid.");
697
+ }
698
+ return value.toLowerCase();
699
+ }
700
+ assertSpecialMemberAvailable(state, request, member) {
701
+ const handle = request.handleId ? state.handles.get(request.handleId) : undefined;
702
+ const owner = request.module || handle?.typeName;
703
+ if (!owner) {
704
+ return;
705
+ }
706
+ const descriptor = (0, plugin_electron_facade_descriptors_1.getElectronFacadeSpecialMemberDescriptor)(owner, member);
707
+ if (!descriptor) {
708
+ return;
709
+ }
710
+ if (descriptor.behavior === "synchronousCallback") {
711
+ if (request.args?.[0] === null) {
712
+ return;
713
+ }
714
+ throw new ElectronFacadeBrokerError(plugin_electron_facade_1.ElectronFacadeErrorCodes.SynchronousCallbackUnsupported, owner + "." + member + " is unavailable because " + descriptor.reason + ".");
715
+ }
716
+ const app = asObject(this.electronRoot().app);
717
+ if (safeBooleanCall(app || {}, "isReady")) {
718
+ throw new ElectronFacadeBrokerError(plugin_electron_facade_1.ElectronFacadeErrorCodes.LifecycleUnavailable, owner + "." + member + " is unavailable after Electron app ready because it " + descriptor.reason + ".");
719
+ }
720
+ }
721
+ namespacedPartition(state, value) {
722
+ if (typeof value !== "string" || value.trim().length === 0) {
723
+ return state.sessionPartition;
724
+ }
725
+ const suffix = value.replace(/^persist:/i, "").replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 64);
726
+ return state.sessionPartition + ":" + (suffix || "default");
727
+ }
728
+ ownedValues(state, typeName) {
729
+ return Array.from(state.handles.values())
730
+ .filter((record) => record.typeName === typeName && (typeName !== "BrowserWindow" || !safeBooleanCall(record.value, "isDestroyed")))
731
+ .map((record) => record.value);
732
+ }
733
+ attachClosedHandleInvalidation(state, value, handleId) {
734
+ const target = value;
735
+ const once = Reflect.get(target, "once");
736
+ if (typeof once === "function") {
737
+ once.call(target, "closed", () => {
738
+ this.releaseHandle(state, handleId, false);
739
+ });
740
+ }
741
+ }
742
+ createOwnedDisposer(typeName, value) {
743
+ const target = value;
744
+ if (typeName === "BrowserWindow") {
745
+ return () => {
746
+ if (!safeBooleanCall(target, "isDestroyed")) {
747
+ safeCall(target, "destroy");
748
+ }
749
+ };
750
+ }
751
+ if (typeName === "BrowserView") {
752
+ return () => {
753
+ const webContents = asObject(Reflect.get(target, "webContents"));
754
+ safeCall(webContents, "close");
755
+ };
756
+ }
757
+ if (typeName === "Tray") {
758
+ return () => safeCall(target, "destroy");
759
+ }
760
+ if (typeName === "Notification") {
761
+ return () => safeCall(target, "close");
762
+ }
763
+ if (typeName === "Menu" || typeName === "ShareMenu") {
764
+ return () => safeCall(target, "closePopup");
765
+ }
766
+ if (typeName === "MessageChannelMain") {
767
+ return () => {
768
+ safeCall(asObject(Reflect.get(target, "port1")), "close");
769
+ safeCall(asObject(Reflect.get(target, "port2")), "close");
770
+ };
771
+ }
772
+ if (typeName === "ClientRequest") {
773
+ return () => safeCall(target, "abort");
774
+ }
775
+ if (typeName === "UtilityProcess") {
776
+ return () => safeCall(target, "kill");
777
+ }
778
+ return undefined;
779
+ }
780
+ disposeDetachedValue(value) {
781
+ if (!isObjectLike(value)) {
782
+ return;
783
+ }
784
+ const typeName = value.constructor?.name || "";
785
+ this.createOwnedDisposer(typeName, value)?.();
786
+ }
787
+ electronRoot() {
788
+ if (this.injectedRoot) {
789
+ return this.injectedRoot;
790
+ }
791
+ const loaded = module.require("electron");
792
+ if (typeof loaded !== "object" || loaded === null) {
793
+ throw new Error("Electron main-process exports are unavailable.");
794
+ }
795
+ return loaded;
796
+ }
797
+ electronRootIfAvailable() {
798
+ try {
799
+ return this.electronRoot();
800
+ }
801
+ catch {
802
+ return undefined;
803
+ }
804
+ }
805
+ }
806
+ exports.PluginElectronFacadeBrokerService = PluginElectronFacadeBrokerService;
807
+ function readCallbackId(value) {
808
+ if (!isPlainRecord(value) || value.$nexusElectron !== "callback" || typeof value.callbackId !== "string") {
809
+ throw new plugin_electron_facade_1.ElectronFacadeCodecError("Electron facade event callback is invalid.");
810
+ }
811
+ return value.callbackId;
812
+ }
813
+ function readHandleId(value) {
814
+ if (!isPlainRecord(value) || value.$nexusElectron !== "handle" || typeof value.handleId !== "string") {
815
+ throw new plugin_electron_facade_1.ElectronFacadeCodecError("Electron facade handle result is invalid.");
816
+ }
817
+ return value.handleId;
818
+ }
819
+ function requireFunction(target, member) {
820
+ const candidate = target && Reflect.get(target, member);
821
+ if (typeof candidate !== "function") {
822
+ throw new Error("Electron member is unavailable: " + member + ".");
823
+ }
824
+ return candidate;
825
+ }
826
+ function safeCall(target, member, ...args) {
827
+ try {
828
+ const candidate = target && Reflect.get(target, member);
829
+ if (typeof candidate === "function") {
830
+ const result = candidate.apply(target, args);
831
+ if (result && typeof result.then === "function") {
832
+ void Promise.resolve(result).catch(() => undefined);
833
+ }
834
+ }
835
+ }
836
+ catch {
837
+ // Resource cleanup is intentionally best effort.
838
+ }
839
+ }
840
+ function safeBooleanCall(target, member) {
841
+ try {
842
+ const candidate = Reflect.get(target, member);
843
+ return typeof candidate === "function" && Boolean(candidate.call(target));
844
+ }
845
+ catch {
846
+ return false;
847
+ }
848
+ }
849
+ function asObject(value) {
850
+ return isObjectLike(value) ? value : undefined;
851
+ }
852
+ function createFacadeModuleProxy(target, overrides) {
853
+ return new Proxy(overrides, {
854
+ get: (overrideTarget, property) => {
855
+ if (Reflect.has(overrideTarget, property)) {
856
+ return Reflect.get(overrideTarget, property);
857
+ }
858
+ const value = target && Reflect.get(target, property);
859
+ return typeof value === "function" ? value.bind(target) : value;
860
+ },
861
+ has: (overrideTarget, property) => Reflect.has(overrideTarget, property) || Boolean(target && Reflect.has(target, property))
862
+ });
863
+ }
864
+ function isObjectLike(value) {
865
+ return (typeof value === "object" && value !== null) || typeof value === "function";
866
+ }
867
+ function isPlainRecord(value) {
868
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
869
+ return false;
870
+ }
871
+ const prototype = Object.getPrototypeOf(value);
872
+ return prototype === Object.prototype || prototype === null;
873
+ }
874
+ function isElectronEvent(value) {
875
+ return isPlainRecord(value)
876
+ && typeof safeGet(value, "preventDefault") === "function"
877
+ && Reflect.has(value, "sender");
878
+ }
879
+ function safeGet(target, property) {
880
+ try {
881
+ return Reflect.get(target, property);
882
+ }
883
+ catch {
884
+ return undefined;
885
+ }
886
+ }
887
+ function isPathInside(root, candidate) {
888
+ const relative = path.relative(path.resolve(root), path.resolve(candidate));
889
+ return relative === "" || (!relative.startsWith(".." + path.sep) && relative !== ".." && !path.isAbsolute(relative));
890
+ }