@aiot-nexus/plugin-sdk 2.1.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -96
- package/dev-host/main.js +362 -504
- package/dist/.generated/plugin-framework-version.d.ts +1 -1
- package/dist/.generated/plugin-framework-version.js +1 -1
- package/dist/.generated/plugin-manifest.schema.json +1 -1
- package/dist/browser/frame-client.d.ts +1 -0
- package/dist/browser/frame-client.mjs +121 -13
- package/dist/src/browser/frame-client.d.ts +1 -0
- package/dist/src/browser/frame-client.js +121 -13
- package/dist/src/cli/run.js +2 -2
- package/dist/src/dev.js +3 -3
- package/dist/src/{electron-facade.js → electron-namespaces.js} +2 -3
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/nexus-dev-control.d.ts +1 -1
- package/dist/src/nexus-dev-control.js +2 -2
- package/dist/src/nexus-dev.d.ts +0 -3
- package/dist/src/nexus-dev.js +118 -53
- package/dist/src/types.d.ts +14 -98
- package/package.json +1 -1
- package/templates/minimal/.vscode/launch.json +1 -1
- package/templates/minimal/README.md.tmpl +15 -14
- package/templates/minimal/src/main.ts.tmpl +5 -4
- package/templates/sqlite/.vscode/launch.json +1 -1
- package/templates/sqlite/README.md.tmpl +12 -10
- package/templates/sqlite/src/main.ts.tmpl +24 -20
- package/dev-host/plugin-host.js +0 -4
- package/dist/internal/src-electron/main/core/runtime-result.js +0 -42
- package/dist/internal/src-electron/main/plugins/plugin-electron-facade-broker.js +0 -890
- package/dist/internal/src-electron/plugin-host-bootstrap.js +0 -417
- package/dist/internal/src-electron/shared/contracts.js +0 -2
- package/dist/internal/src-electron/shared/plugin-electron-facade-client.js +0 -181
- package/dist/internal/src-electron/shared/plugin-electron-facade-descriptors.js +0 -70
- package/dist/internal/src-electron/shared/plugin-electron-facade.js +0 -243
- package/dist/internal/src-electron/shared/plugin-rpc.js +0 -245
- /package/dist/src/{electron-facade.d.ts → electron-namespaces.d.ts} +0 -0
package/dev-host/main.js
CHANGED
|
@@ -2,19 +2,10 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
3
3
|
|
|
4
4
|
const fs = require("fs");
|
|
5
|
+
const { createRequire } = require("module");
|
|
5
6
|
const path = require("path");
|
|
6
|
-
const { app, BrowserWindow, ipcMain, protocol
|
|
7
|
+
const { app, BrowserWindow, ipcMain, protocol } = require("electron");
|
|
7
8
|
const sdk = require(path.join(__dirname, "..", "dist", "src", "index.js"));
|
|
8
|
-
const { PluginElectronFacadeBrokerService } = require(path.join(
|
|
9
|
-
__dirname,
|
|
10
|
-
"..",
|
|
11
|
-
"dist",
|
|
12
|
-
"internal",
|
|
13
|
-
"src-electron",
|
|
14
|
-
"main",
|
|
15
|
-
"plugins",
|
|
16
|
-
"plugin-electron-facade-broker.js"
|
|
17
|
-
));
|
|
18
9
|
|
|
19
10
|
const pluginRendererProtocol = "nexus-plugin";
|
|
20
11
|
const ipcChannels = {
|
|
@@ -22,13 +13,9 @@ const ipcChannels = {
|
|
|
22
13
|
invoke: "nexus-plugin-dev:invoke",
|
|
23
14
|
frameReady: "nexus-plugin-dev:frame-ready"
|
|
24
15
|
};
|
|
25
|
-
const pluginPermissions = ["businessLog", "settings", "filesystem", "udp", "serial", "externalProcess", "dialog"
|
|
16
|
+
const pluginPermissions = ["businessLog", "settings", "filesystem", "udp", "serial", "externalProcess", "dialog"];
|
|
26
17
|
const implementedPermissions = new Set(["businessLog", "settings"]);
|
|
27
|
-
const hostControlPermissions = new Set(["electronHostControl"]);
|
|
28
18
|
const routeGuardPermissions = new Set(["filesystem", "udp", "serial", "dialog"]);
|
|
29
|
-
const workerProtocolVersion = 3;
|
|
30
|
-
const workerOperationTimeoutMs = 15000;
|
|
31
|
-
const electronFacadeBroker = new PluginElectronFacadeBrokerService();
|
|
32
19
|
|
|
33
20
|
protocol.registerSchemesAsPrivileged([
|
|
34
21
|
{
|
|
@@ -57,13 +44,14 @@ app.setName("Nexus Plugin Dev: " + configuration.manifest.pluginId);
|
|
|
57
44
|
let mainWindow;
|
|
58
45
|
let smokeTimeout;
|
|
59
46
|
let smokeVerificationRunning = false;
|
|
60
|
-
let
|
|
61
|
-
let
|
|
62
|
-
let
|
|
47
|
+
let pluginModule;
|
|
48
|
+
let pluginActivated = false;
|
|
49
|
+
let pluginDeactivated = false;
|
|
50
|
+
const rendererOperations = new Map();
|
|
63
51
|
|
|
64
|
-
app.whenReady().then(
|
|
52
|
+
app.whenReady().then(function startDevHost() {
|
|
65
53
|
registerPluginRendererProtocol();
|
|
66
|
-
|
|
54
|
+
activatePluginMainEntry();
|
|
67
55
|
registerIpcHandlers();
|
|
68
56
|
createWindow();
|
|
69
57
|
}).catch(failStartup);
|
|
@@ -72,26 +60,18 @@ app.on("window-all-closed", function quitAfterWindowClose() {
|
|
|
72
60
|
app.quit();
|
|
73
61
|
});
|
|
74
62
|
|
|
75
|
-
app.on("before-quit", function
|
|
76
|
-
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
event.preventDefault();
|
|
80
|
-
if (workerShutdownStarted) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
workerShutdownStarted = true;
|
|
84
|
-
void pluginWorker.shutdown("quit")
|
|
85
|
-
.catch(function reportWorkerShutdownFailure(error) {
|
|
86
|
-
process.stderr.write("[nexus-plugin-dev] plugin worker shutdown failed: " + errorMessage(error) + "\n");
|
|
87
|
-
})
|
|
88
|
-
.finally(function finishPluginWorkerShutdown() {
|
|
89
|
-
electronFacadeBroker.disposePlugin(configuration.manifest.pluginId);
|
|
90
|
-
workerShutdownComplete = true;
|
|
91
|
-
app.quit();
|
|
92
|
-
});
|
|
63
|
+
app.on("before-quit", function deactivateBeforeQuit() {
|
|
64
|
+
deactivatePluginMainEntry("quit");
|
|
93
65
|
});
|
|
94
66
|
|
|
67
|
+
process.once("SIGINT", requestSignalQuit);
|
|
68
|
+
process.once("SIGTERM", requestSignalQuit);
|
|
69
|
+
|
|
70
|
+
function requestSignalQuit() {
|
|
71
|
+
deactivatePluginMainEntry("quit");
|
|
72
|
+
app.quit();
|
|
73
|
+
}
|
|
74
|
+
|
|
95
75
|
function loadConfiguration(args) {
|
|
96
76
|
const packageDirectory = requiredPathArgument(args, "--plugin-package");
|
|
97
77
|
const projectDirectory = requiredPathArgument(args, "--project-directory");
|
|
@@ -107,7 +87,6 @@ function loadConfiguration(args) {
|
|
|
107
87
|
const language = requiredArgument(args, "--language");
|
|
108
88
|
const themeMode = requiredArgument(args, "--theme");
|
|
109
89
|
const hostVersion = requiredArgument(args, "--host-version");
|
|
110
|
-
const workerInspector = parseWorkerInspector(args);
|
|
111
90
|
if (!isStrictVersion(hostVersion)) {
|
|
112
91
|
throw new Error("--host-version must use X.Y.Z format.");
|
|
113
92
|
}
|
|
@@ -127,7 +106,6 @@ function loadConfiguration(args) {
|
|
|
127
106
|
language,
|
|
128
107
|
themeMode,
|
|
129
108
|
hostVersion,
|
|
130
|
-
workerInspector,
|
|
131
109
|
openDevTools: !args.includes("--no-devtools"),
|
|
132
110
|
smoke: args.includes("--smoke"),
|
|
133
111
|
devDataDirectory,
|
|
@@ -194,451 +172,233 @@ function resolvePluginRendererFile(url) {
|
|
|
194
172
|
});
|
|
195
173
|
}
|
|
196
174
|
|
|
197
|
-
|
|
175
|
+
function activatePluginMainEntry() {
|
|
198
176
|
if (!configuration.manifest.mainEntry) {
|
|
199
177
|
return;
|
|
200
178
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
let
|
|
179
|
+
const mainEntryPath = resolvePluginMainEntry(configuration.manifest.mainEntry);
|
|
180
|
+
const pluginRequire = createRequire(mainEntryPath);
|
|
181
|
+
const loaded = pluginRequire(mainEntryPath);
|
|
182
|
+
if (!loaded || typeof loaded !== "object") {
|
|
183
|
+
throw new Error("Plugin main entry must export an object.");
|
|
184
|
+
}
|
|
185
|
+
pluginModule = loaded;
|
|
186
|
+
let activationStarted = false;
|
|
209
187
|
try {
|
|
210
|
-
|
|
188
|
+
const activate = loaded.activate || (loaded.default && loaded.default.activate);
|
|
189
|
+
if (activate) {
|
|
190
|
+
if (typeof activate !== "function") {
|
|
191
|
+
throw new Error("Plugin activate export must be a function.");
|
|
192
|
+
}
|
|
193
|
+
if (isAsyncFunction(activate)) {
|
|
194
|
+
throw new Error("Plugin activate must complete synchronously in Framework API 3.0.0.");
|
|
195
|
+
}
|
|
196
|
+
activationStarted = true;
|
|
197
|
+
const result = activate(createActivationContext());
|
|
198
|
+
if (isPromiseLike(result)) {
|
|
199
|
+
observeRejectedLifecyclePromise(result, "activate");
|
|
200
|
+
throw new Error("Plugin activate must complete synchronously in Framework API 3.0.0.");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
pluginActivated = true;
|
|
204
|
+
process.stdout.write(
|
|
205
|
+
"[nexus-plugin-dev] activated " + configuration.manifest.pluginId
|
|
206
|
+
+ " directly in the Electron main process with " + String(rendererOperations.size) + " renderer operation(s).\n"
|
|
207
|
+
);
|
|
211
208
|
} catch (error) {
|
|
212
|
-
|
|
209
|
+
if (activationStarted) {
|
|
210
|
+
runActivationCleanup(loaded);
|
|
211
|
+
}
|
|
212
|
+
rendererOperations.clear();
|
|
213
|
+
pluginModule = undefined;
|
|
213
214
|
throw error;
|
|
214
215
|
}
|
|
215
|
-
process.stdout.write(
|
|
216
|
-
"[nexus-plugin-dev] activated " + configuration.manifest.pluginId
|
|
217
|
-
+ " with " + String(ready.operations.length) + " renderer operation(s).\n"
|
|
218
|
-
);
|
|
219
216
|
}
|
|
220
217
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
this.child = undefined;
|
|
225
|
-
this.pending = new Map();
|
|
226
|
-
this.startPromise = undefined;
|
|
227
|
-
this.startResolve = undefined;
|
|
228
|
-
this.startReject = undefined;
|
|
229
|
-
this.startTimer = undefined;
|
|
230
|
-
this.shutdownPromise = undefined;
|
|
231
|
-
this.shutdownResolve = undefined;
|
|
232
|
-
this.shutdownTimer = undefined;
|
|
233
|
-
this.initializationRequestId = undefined;
|
|
234
|
-
this.requestSequence = 0;
|
|
235
|
-
this.intentionalStop = false;
|
|
236
|
-
this.ready = false;
|
|
218
|
+
function resolvePluginMainEntry(relativePath) {
|
|
219
|
+
if (path.isAbsolute(relativePath)) {
|
|
220
|
+
throw new Error("Plugin mainEntry must be relative to the plugin package.");
|
|
237
221
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return this.startPromise;
|
|
242
|
-
}
|
|
243
|
-
const startPromise = new Promise((resolve, reject) => {
|
|
244
|
-
this.startResolve = resolve;
|
|
245
|
-
this.startReject = reject;
|
|
246
|
-
});
|
|
247
|
-
this.startPromise = startPromise;
|
|
248
|
-
this.intentionalStop = false;
|
|
249
|
-
this.ready = false;
|
|
250
|
-
try {
|
|
251
|
-
this.child = utilityProcess.fork(path.join(__dirname, "plugin-host.js"), [], {
|
|
252
|
-
cwd: this.configuration.packageDirectory,
|
|
253
|
-
serviceName: "nexus-plugin-dev-" + this.configuration.manifest.pluginId,
|
|
254
|
-
stdio: this.configuration.workerInspector ? "inherit" : "ignore",
|
|
255
|
-
execArgv: this.configuration.workerInspector ? [this.configuration.workerInspector.execArg] : []
|
|
256
|
-
});
|
|
257
|
-
this.child.on("message", (message) => { void this.handleMessage(readWorkerMessage(message)); });
|
|
258
|
-
this.child.on("exit", (code) => this.handleExit(code));
|
|
259
|
-
if (!this.configuration.workerInspector || !this.configuration.workerInspector.breakOnStart) {
|
|
260
|
-
this.startTimer = setTimeout(() => {
|
|
261
|
-
this.failStart(new Error("Plugin worker activation timed out."));
|
|
262
|
-
this.kill();
|
|
263
|
-
}, 15_000);
|
|
264
|
-
}
|
|
265
|
-
const initializationRequestId = this.nextRequestId("initialize");
|
|
266
|
-
this.initializationRequestId = initializationRequestId;
|
|
267
|
-
this.post({
|
|
268
|
-
protocolVersion: workerProtocolVersion,
|
|
269
|
-
type: "initialize",
|
|
270
|
-
requestId: initializationRequestId,
|
|
271
|
-
payload: {
|
|
272
|
-
pluginId: this.configuration.manifest.pluginId,
|
|
273
|
-
pluginDirectory: this.configuration.packageDirectory,
|
|
274
|
-
pluginDataDirectory: this.configuration.pluginDataDirectory,
|
|
275
|
-
manifest: sanitizeJsonValue(this.configuration.manifest),
|
|
276
|
-
hostVersion: this.configuration.hostVersion,
|
|
277
|
-
hostApiVersion: sdk.PLUGIN_FRAMEWORK_VERSION
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
} catch (error) {
|
|
281
|
-
this.failStart(error);
|
|
282
|
-
}
|
|
283
|
-
return startPromise;
|
|
222
|
+
const resolvedPath = path.resolve(configuration.packageDirectory, relativePath);
|
|
223
|
+
if (!isPathInside(configuration.packageDirectory, resolvedPath)) {
|
|
224
|
+
throw new Error("Plugin mainEntry resolves outside the plugin package.");
|
|
284
225
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (!this.child || !this.ready) {
|
|
288
|
-
return Promise.resolve(fail("PluginWorkerNotReady", "Plugin worker is not ready."));
|
|
289
|
-
}
|
|
290
|
-
const requestId = this.nextRequestId("invoke");
|
|
291
|
-
return new Promise((resolve) => {
|
|
292
|
-
const pending = {
|
|
293
|
-
kind: "invoke",
|
|
294
|
-
resolve,
|
|
295
|
-
timer: setTimeout(() => {
|
|
296
|
-
if (this.pending.get(requestId) !== pending) {
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
this.pending.delete(requestId);
|
|
300
|
-
pending.timer = undefined;
|
|
301
|
-
this.ready = false;
|
|
302
|
-
resolve(fail("PluginOperationTimeout", "Plugin worker operation timed out.", {
|
|
303
|
-
pluginId: this.configuration.manifest.pluginId,
|
|
304
|
-
operation: payload.operation,
|
|
305
|
-
timeoutMs: workerOperationTimeoutMs
|
|
306
|
-
}));
|
|
307
|
-
this.kill();
|
|
308
|
-
}, workerOperationTimeoutMs)
|
|
309
|
-
};
|
|
310
|
-
this.pending.set(requestId, pending);
|
|
311
|
-
try {
|
|
312
|
-
this.post({ protocolVersion: workerProtocolVersion, type: "invoke", requestId, payload });
|
|
313
|
-
} catch (error) {
|
|
314
|
-
this.pending.delete(requestId);
|
|
315
|
-
clearPendingTimer(pending);
|
|
316
|
-
resolve(fail("PluginWorkerExited", "Plugin worker request could not be sent.", errorMessage(error)));
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
invokeElectronCallback(callbackId, args) {
|
|
322
|
-
if (!this.child) {
|
|
323
|
-
return Promise.resolve(fail("PluginProcessNotReady", "Plugin worker is not available for Electron callback."));
|
|
324
|
-
}
|
|
325
|
-
const requestId = this.nextRequestId("electron-callback");
|
|
326
|
-
return new Promise((resolve) => {
|
|
327
|
-
const pending = {
|
|
328
|
-
kind: "electronCallback",
|
|
329
|
-
resolve,
|
|
330
|
-
timer: setTimeout(() => {
|
|
331
|
-
if (this.pending.get(requestId) !== pending) {
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
this.pending.delete(requestId);
|
|
335
|
-
pending.timer = undefined;
|
|
336
|
-
resolve(fail("PluginOperationTimeout", "Electron facade callback timed out.", {
|
|
337
|
-
pluginId: this.configuration.manifest.pluginId,
|
|
338
|
-
callbackId,
|
|
339
|
-
timeoutMs: workerOperationTimeoutMs
|
|
340
|
-
}));
|
|
341
|
-
}, workerOperationTimeoutMs)
|
|
342
|
-
};
|
|
343
|
-
this.pending.set(requestId, pending);
|
|
344
|
-
try {
|
|
345
|
-
this.post({
|
|
346
|
-
protocolVersion: workerProtocolVersion,
|
|
347
|
-
type: "electronCallback",
|
|
348
|
-
requestId,
|
|
349
|
-
payload: { callbackId, args }
|
|
350
|
-
});
|
|
351
|
-
} catch (error) {
|
|
352
|
-
this.pending.delete(requestId);
|
|
353
|
-
clearPendingTimer(pending);
|
|
354
|
-
resolve(fail("PluginProcessExited", "Electron facade callback could not be sent.", errorMessage(error)));
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
emitElectronEvent(callbackId, args) {
|
|
360
|
-
if (!this.child) {
|
|
361
|
-
return false;
|
|
362
|
-
}
|
|
363
|
-
try {
|
|
364
|
-
this.post({
|
|
365
|
-
protocolVersion: workerProtocolVersion,
|
|
366
|
-
type: "electronEvent",
|
|
367
|
-
payload: { callbackId, args }
|
|
368
|
-
});
|
|
369
|
-
return true;
|
|
370
|
-
} catch {
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
226
|
+
if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isFile() || fs.lstatSync(resolvedPath).isSymbolicLink()) {
|
|
227
|
+
throw new Error("Plugin mainEntry was not found or is not a regular file: " + relativePath);
|
|
373
228
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return Promise.resolve(ok(true));
|
|
379
|
-
}
|
|
380
|
-
if (this.shutdownPromise) {
|
|
381
|
-
return this.shutdownPromise;
|
|
382
|
-
}
|
|
383
|
-
const requestId = this.nextRequestId("shutdown");
|
|
384
|
-
this.shutdownPromise = new Promise((resolve) => {
|
|
385
|
-
this.shutdownResolve = resolve;
|
|
386
|
-
this.pending.set(requestId, { kind: "shutdown", resolve });
|
|
387
|
-
this.shutdownTimer = setTimeout(() => {
|
|
388
|
-
this.pending.delete(requestId);
|
|
389
|
-
this.shutdownResolve = undefined;
|
|
390
|
-
this.shutdownPromise = undefined;
|
|
391
|
-
const result = fail("PluginWorkerShutdownTimeout", "Plugin worker shutdown timed out.");
|
|
392
|
-
resolve(result);
|
|
393
|
-
this.kill();
|
|
394
|
-
}, 5000);
|
|
395
|
-
try {
|
|
396
|
-
this.post({ protocolVersion: workerProtocolVersion, type: "shutdown", requestId, payload: { reason: reason || "quit" } });
|
|
397
|
-
} catch (error) {
|
|
398
|
-
clearTimeout(this.shutdownTimer);
|
|
399
|
-
this.shutdownTimer = undefined;
|
|
400
|
-
this.pending.delete(requestId);
|
|
401
|
-
this.shutdownPromise = undefined;
|
|
402
|
-
resolve(fail("PluginWorkerExited", "Plugin worker shutdown could not be sent.", errorMessage(error)));
|
|
403
|
-
this.kill();
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
return this.shutdownPromise;
|
|
229
|
+
const realRoot = fs.realpathSync(configuration.packageDirectory);
|
|
230
|
+
const realPath = fs.realpathSync(resolvedPath);
|
|
231
|
+
if (!isPathInside(realRoot, realPath)) {
|
|
232
|
+
throw new Error("Plugin mainEntry resolves outside the plugin package.");
|
|
407
233
|
}
|
|
234
|
+
return realPath;
|
|
235
|
+
}
|
|
408
236
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
if (message.type === "ready") {
|
|
415
|
-
if (!this.startPromise
|
|
416
|
-
|| this.ready
|
|
417
|
-
|| message.requestId !== this.initializationRequestId
|
|
418
|
-
|| !isRecord(message.payload)
|
|
419
|
-
|| !Array.isArray(message.payload.operations)
|
|
420
|
-
|| !message.payload.operations.every(isSafeOperationName)
|
|
421
|
-
|| new Set(message.payload.operations).size !== message.payload.operations.length
|
|
422
|
-
|| typeof message.payload.activatedAtUtc !== "string"
|
|
423
|
-
|| !message.payload.activatedAtUtc) {
|
|
424
|
-
this.handleProtocolViolation("Plugin worker ready response did not match initialization.");
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
clearTimeout(this.startTimer);
|
|
428
|
-
this.startTimer = undefined;
|
|
429
|
-
this.initializationRequestId = undefined;
|
|
430
|
-
this.ready = true;
|
|
431
|
-
this.startResolve?.(message.payload);
|
|
432
|
-
this.startResolve = undefined;
|
|
433
|
-
this.startReject = undefined;
|
|
434
|
-
this.startPromise = undefined;
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
if (message.type === "hostRequest") {
|
|
438
|
-
if (!isWorkerRequestId(message.requestId)) {
|
|
439
|
-
this.handleProtocolViolation("Plugin worker Host API request id is invalid.");
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
const result = normalizeRpcResult(await handlePluginHostRequest(this.configuration, message.payload));
|
|
443
|
-
try {
|
|
444
|
-
this.post({ protocolVersion: workerProtocolVersion, type: "hostResult", requestId: message.requestId, result });
|
|
445
|
-
} catch {
|
|
446
|
-
// The worker may have exited while a Host API request was running.
|
|
447
|
-
}
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
if (message.type === "result") {
|
|
451
|
-
if (!isWorkerRequestId(message.requestId) || !isNexusResult(message.result)) {
|
|
452
|
-
this.handleProtocolViolation("Plugin worker result is invalid.");
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
const pending = this.pending.get(message.requestId);
|
|
456
|
-
if (!pending) {
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
if (pending.kind === "electronCallback") {
|
|
460
|
-
this.handleProtocolViolation("Plugin worker used a normal result for an Electron callback.");
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
this.pending.delete(message.requestId);
|
|
464
|
-
clearPendingTimer(pending);
|
|
465
|
-
if (pending.kind === "shutdown") {
|
|
466
|
-
clearTimeout(this.shutdownTimer);
|
|
467
|
-
this.shutdownTimer = undefined;
|
|
468
|
-
this.shutdownResolve = undefined;
|
|
469
|
-
this.shutdownPromise = undefined;
|
|
470
|
-
pending.resolve(message.result);
|
|
471
|
-
this.kill();
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
pending.resolve(message.result);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
if (message.type === "electronCallbackResult") {
|
|
478
|
-
if (!isWorkerRequestId(message.requestId) || !isNexusResult(message.result)) {
|
|
479
|
-
this.handleProtocolViolation("Plugin worker Electron callback result is invalid.");
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
const pending = this.pending.get(message.requestId);
|
|
483
|
-
if (!pending) {
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
if (pending.kind !== "electronCallback") {
|
|
487
|
-
this.handleProtocolViolation("Plugin worker used an Electron callback result for another request.");
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
this.pending.delete(message.requestId);
|
|
491
|
-
clearPendingTimer(pending);
|
|
492
|
-
pending.resolve(message.result);
|
|
237
|
+
function runActivationCleanup(loaded) {
|
|
238
|
+
try {
|
|
239
|
+
const deactivate = loaded.deactivate || (loaded.default && loaded.default.deactivate);
|
|
240
|
+
if (!deactivate) {
|
|
493
241
|
return;
|
|
494
242
|
}
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
if (this.startPromise) {
|
|
498
|
-
this.failStart(error);
|
|
499
|
-
} else {
|
|
500
|
-
process.stderr.write("[nexus-plugin-dev] plugin worker failed: " + error.message + "\n");
|
|
501
|
-
}
|
|
502
|
-
this.kill();
|
|
243
|
+
if (typeof deactivate !== "function" || isAsyncFunction(deactivate)) {
|
|
244
|
+
process.stderr.write("[nexus-plugin-dev] activation cleanup must complete synchronously.\n");
|
|
503
245
|
return;
|
|
504
246
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
electronFacadeBroker.disposePlugin(this.configuration.manifest.pluginId);
|
|
510
|
-
this.child = undefined;
|
|
511
|
-
this.ready = false;
|
|
512
|
-
this.initializationRequestId = undefined;
|
|
513
|
-
clearTimeout(this.startTimer);
|
|
514
|
-
this.startTimer = undefined;
|
|
515
|
-
clearTimeout(this.shutdownTimer);
|
|
516
|
-
this.shutdownTimer = undefined;
|
|
517
|
-
this.shutdownPromise = undefined;
|
|
518
|
-
this.shutdownResolve = undefined;
|
|
519
|
-
const result = fail("PluginWorkerExited", "Plugin worker process exited.", { code });
|
|
520
|
-
if (this.startPromise) {
|
|
521
|
-
this.failStart(new Error(result.error.message));
|
|
522
|
-
}
|
|
523
|
-
for (const pending of this.pending.values()) {
|
|
524
|
-
clearPendingTimer(pending);
|
|
525
|
-
pending.resolve(result);
|
|
526
|
-
}
|
|
527
|
-
this.pending.clear();
|
|
528
|
-
if (!this.intentionalStop) {
|
|
529
|
-
process.stderr.write("[nexus-plugin-dev] plugin worker exited with code " + String(code) + ".\n");
|
|
247
|
+
const result = deactivate("reload");
|
|
248
|
+
if (isPromiseLike(result)) {
|
|
249
|
+
observeRejectedLifecyclePromise(result, "activation cleanup");
|
|
250
|
+
process.stderr.write("[nexus-plugin-dev] activation cleanup must complete synchronously.\n");
|
|
530
251
|
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
process.stderr.write("[nexus-plugin-dev] activation cleanup failed: " + errorMessage(error) + "\n");
|
|
531
254
|
}
|
|
255
|
+
}
|
|
532
256
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
this.initializationRequestId = undefined;
|
|
537
|
-
this.startReject?.(error instanceof Error ? error : new Error(String(error)));
|
|
538
|
-
this.startResolve = undefined;
|
|
539
|
-
this.startReject = undefined;
|
|
540
|
-
this.startPromise = undefined;
|
|
257
|
+
function deactivatePluginMainEntry(reason) {
|
|
258
|
+
if (!pluginModule || pluginDeactivated) {
|
|
259
|
+
return;
|
|
541
260
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const
|
|
545
|
-
if (
|
|
546
|
-
|
|
547
|
-
} else {
|
|
548
|
-
process.stderr.write("[nexus-plugin-dev] " + message + "\n");
|
|
261
|
+
pluginDeactivated = true;
|
|
262
|
+
try {
|
|
263
|
+
const deactivate = pluginModule.deactivate || (pluginModule.default && pluginModule.default.deactivate);
|
|
264
|
+
if (!deactivate) {
|
|
265
|
+
return;
|
|
549
266
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
post(message) {
|
|
554
|
-
if (!this.child) {
|
|
555
|
-
throw new Error("Plugin worker process has exited.");
|
|
267
|
+
if (typeof deactivate !== "function" || isAsyncFunction(deactivate)) {
|
|
268
|
+
process.stderr.write("[nexus-plugin-dev] plugin deactivate must complete synchronously in Framework API 3.0.0.\n");
|
|
269
|
+
return;
|
|
556
270
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (this.child) {
|
|
562
|
-
this.child.kill();
|
|
271
|
+
const result = deactivate(reason);
|
|
272
|
+
if (isPromiseLike(result)) {
|
|
273
|
+
observeRejectedLifecyclePromise(result, "deactivate");
|
|
274
|
+
process.stderr.write("[nexus-plugin-dev] plugin deactivate must complete synchronously in Framework API 3.0.0.\n");
|
|
563
275
|
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function clearPendingTimer(pending) {
|
|
573
|
-
if (pending && pending.timer) {
|
|
574
|
-
clearTimeout(pending.timer);
|
|
575
|
-
pending.timer = undefined;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
process.stderr.write("[nexus-plugin-dev] plugin deactivation failed: " + errorMessage(error) + "\n");
|
|
278
|
+
} finally {
|
|
279
|
+
pluginActivated = false;
|
|
280
|
+
rendererOperations.clear();
|
|
576
281
|
}
|
|
577
282
|
}
|
|
578
283
|
|
|
579
|
-
function
|
|
580
|
-
|
|
284
|
+
function observeRejectedLifecyclePromise(value, lifecycle) {
|
|
285
|
+
void Promise.resolve(value).catch(function reportLifecycleRejection(error) {
|
|
286
|
+
process.stderr.write("[nexus-plugin-dev] plugin " + lifecycle + " returned a rejected Promise: " + errorMessage(error) + "\n");
|
|
287
|
+
});
|
|
581
288
|
}
|
|
582
289
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
290
|
+
function createActivationContext() {
|
|
291
|
+
const manifest = configuration.manifest;
|
|
292
|
+
const unsupported = function createUnsupportedApi(permission) {
|
|
293
|
+
const summary = createPermissionSummary(permission);
|
|
294
|
+
const target = {
|
|
295
|
+
permission,
|
|
296
|
+
supportState: summary.supportState,
|
|
297
|
+
describe: function describe() {
|
|
298
|
+
return ok(summary);
|
|
299
|
+
},
|
|
300
|
+
unsupported: function unsupported(operation) {
|
|
301
|
+
return unsupportedCapability(manifest, permission, operation);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
return new Proxy(target, {
|
|
305
|
+
get: function getUnsupported(apiTarget, property) {
|
|
306
|
+
if (property in apiTarget) {
|
|
307
|
+
return Reflect.get(apiTarget, property);
|
|
308
|
+
}
|
|
309
|
+
if (typeof property === "symbol" || property === "then") {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
return function reportUnsupported() {
|
|
313
|
+
return apiTarget.unsupported(property);
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
};
|
|
318
|
+
return {
|
|
319
|
+
pluginId: manifest.pluginId,
|
|
320
|
+
pluginDirectory: configuration.packageDirectory,
|
|
321
|
+
manifest,
|
|
322
|
+
host: {
|
|
323
|
+
hostVersion: configuration.hostVersion,
|
|
324
|
+
hostApiVersion: sdk.PLUGIN_FRAMEWORK_VERSION,
|
|
325
|
+
pluginId: manifest.pluginId,
|
|
326
|
+
pluginDataDirectory: configuration.pluginDataDirectory,
|
|
327
|
+
hasPermission: function hasPermission(permission) {
|
|
328
|
+
return manifest.permissions.includes(permission);
|
|
329
|
+
},
|
|
330
|
+
registerRendererMethod: function registerRendererMethod(name, handler) {
|
|
331
|
+
if (!isSafeOperationName(name) || typeof handler !== "function") {
|
|
332
|
+
return fail("InvalidRequest", "Plugin renderer operation registration is invalid.");
|
|
333
|
+
}
|
|
334
|
+
if (rendererOperations.has(name)) {
|
|
335
|
+
return fail("InvalidRequest", "Plugin renderer operation is already registered.", {
|
|
336
|
+
pluginId: manifest.pluginId,
|
|
337
|
+
operation: name
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
rendererOperations.set(name, handler);
|
|
341
|
+
return ok(true);
|
|
342
|
+
},
|
|
343
|
+
getCapabilityStatus: function getCapabilityStatus(permission) {
|
|
344
|
+
return isPluginPermission(permission)
|
|
345
|
+
? ok(createPermissionSummary(permission))
|
|
346
|
+
: fail("InvalidRequest", "Plugin capability name is invalid.", { pluginId: manifest.pluginId, permission });
|
|
347
|
+
},
|
|
348
|
+
unsupportedCapability: function reportUnsupported(permission, operation) {
|
|
349
|
+
return unsupportedCapability(manifest, permission, operation);
|
|
350
|
+
},
|
|
351
|
+
log: function log(level, message, exception) {
|
|
352
|
+
if (!manifest.permissions.includes("businessLog")) {
|
|
353
|
+
return fail("PluginPermissionDenied", "Plugin does not declare businessLog permission.", { pluginId: manifest.pluginId });
|
|
354
|
+
}
|
|
355
|
+
if ((level !== "info" && level !== "warn" && level !== "error") || typeof message !== "string" || !message.trim()) {
|
|
356
|
+
return fail("InvalidRequest", "Plugin log request is invalid.", { pluginId: manifest.pluginId });
|
|
357
|
+
}
|
|
358
|
+
return ok(appendLog(level, "plugin:" + manifest.pluginId, message.trim(), typeof exception === "string" ? exception : undefined));
|
|
359
|
+
},
|
|
360
|
+
getSettings: function getSettings() {
|
|
361
|
+
return manifest.permissions.includes("settings")
|
|
362
|
+
? ok(loadSettings())
|
|
363
|
+
: fail("PluginPermissionDenied", "Plugin does not declare settings permission.", { pluginId: manifest.pluginId });
|
|
364
|
+
},
|
|
365
|
+
updateSettings: function updateSettings(values) {
|
|
366
|
+
if (!manifest.permissions.includes("settings")) {
|
|
367
|
+
return fail("PluginPermissionDenied", "Plugin does not declare settings permission.", { pluginId: manifest.pluginId });
|
|
368
|
+
}
|
|
369
|
+
if (!isRecord(values)) {
|
|
370
|
+
return fail("InvalidRequest", "Plugin settings values must be an object.");
|
|
371
|
+
}
|
|
372
|
+
return ok(saveSettings(values));
|
|
373
|
+
},
|
|
374
|
+
filesystem: unsupported("filesystem"),
|
|
375
|
+
udp: unsupported("udp"),
|
|
376
|
+
serial: unsupported("serial"),
|
|
377
|
+
dialog: unsupported("dialog"),
|
|
378
|
+
externalProcess: unsupported("externalProcess")
|
|
620
379
|
}
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
return fail("InvalidRequest", "Plugin Host API method is invalid.", { method: request.method });
|
|
380
|
+
};
|
|
624
381
|
}
|
|
625
382
|
|
|
626
383
|
function unsupportedCapability(manifest, permission, operation) {
|
|
627
384
|
if (!isPluginPermission(permission)) {
|
|
628
|
-
return fail("InvalidRequest", "Plugin capability name is invalid.", { permission });
|
|
385
|
+
return fail("InvalidRequest", "Plugin capability name is invalid.", { pluginId: manifest.pluginId, permission });
|
|
629
386
|
}
|
|
630
387
|
if (!manifest.permissions.includes(permission)) {
|
|
631
|
-
return fail("PluginPermissionDenied", "Plugin does not declare " + permission + " permission.", {
|
|
388
|
+
return fail("PluginPermissionDenied", "Plugin does not declare " + permission + " permission.", {
|
|
389
|
+
pluginId: manifest.pluginId,
|
|
390
|
+
permission
|
|
391
|
+
});
|
|
632
392
|
}
|
|
633
393
|
const summary = createPermissionSummary(permission);
|
|
634
394
|
return fail(
|
|
635
395
|
"PluginCapabilityUnsupported",
|
|
636
|
-
"
|
|
396
|
+
"This Host API capability is not available; use the native Electron or Node API from mainEntry.",
|
|
637
397
|
{
|
|
398
|
+
pluginId: manifest.pluginId,
|
|
638
399
|
permission,
|
|
639
400
|
operation: typeof operation === "string" ? operation : undefined,
|
|
640
|
-
supportState: summary.supportState
|
|
641
|
-
statusMessage: summary.statusMessage
|
|
401
|
+
supportState: summary.supportState
|
|
642
402
|
}
|
|
643
403
|
);
|
|
644
404
|
}
|
|
@@ -681,7 +441,7 @@ async function verifyRendererContextSmoke() {
|
|
|
681
441
|
throw new Error("Plugin renderer frame was not found.");
|
|
682
442
|
}
|
|
683
443
|
if (configuration.manifest.hostApiVersion !== sdk.PLUGIN_FRAMEWORK_VERSION
|
|
684
|
-
|| (configuration.manifest.mainEntry && !
|
|
444
|
+
|| (configuration.manifest.mainEntry && !pluginActivated)
|
|
685
445
|
|| configuration.manifest.minHostVersion === undefined) {
|
|
686
446
|
throw new Error("Development Host API version contract is inconsistent.");
|
|
687
447
|
}
|
|
@@ -817,15 +577,32 @@ async function invokeMain(params) {
|
|
|
817
577
|
if (!isSafeOperationName(params.operation)) {
|
|
818
578
|
return fail("InvalidRequest", "Plugin renderer operation name is invalid.", { operation: params.operation });
|
|
819
579
|
}
|
|
820
|
-
if (!
|
|
580
|
+
if (!pluginActivated) {
|
|
821
581
|
return fail("PluginCapabilityUnsupported", "Plugin mainEntry is not available in this debug target.", { operation: params.operation });
|
|
822
582
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
583
|
+
const handler = rendererOperations.get(params.operation);
|
|
584
|
+
if (!handler) {
|
|
585
|
+
return fail("NotFound", "Plugin renderer operation was not registered.", {
|
|
586
|
+
pluginId: configuration.manifest.pluginId,
|
|
587
|
+
operation: params.operation
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
try {
|
|
591
|
+
const value = await handler({
|
|
592
|
+
pluginId: configuration.manifest.pluginId,
|
|
593
|
+
surface: configuration.surface,
|
|
594
|
+
contributionId: configuration.contributionId,
|
|
595
|
+
operation: params.operation,
|
|
596
|
+
input: isRecord(params.input) ? decodeOperationValue(params.input) : undefined
|
|
597
|
+
});
|
|
598
|
+
return normalizeOperationResult(value);
|
|
599
|
+
} catch (error) {
|
|
600
|
+
return fail("PluginMainOperationFailed", "Plugin renderer operation failed.", {
|
|
601
|
+
pluginId: configuration.manifest.pluginId,
|
|
602
|
+
operation: params.operation,
|
|
603
|
+
error: errorMessage(error)
|
|
604
|
+
});
|
|
605
|
+
}
|
|
829
606
|
}
|
|
830
607
|
|
|
831
608
|
function createWindow() {
|
|
@@ -905,12 +682,10 @@ function saveSettings(values) {
|
|
|
905
682
|
const document = {
|
|
906
683
|
schemaVersion: sdk.PLUGIN_MANIFEST_SCHEMA_VERSION,
|
|
907
684
|
pluginId: configuration.manifest.pluginId,
|
|
908
|
-
values:
|
|
685
|
+
values: { ...existing.values, ...sanitizeJsonObject(values) }
|
|
909
686
|
};
|
|
910
687
|
const filePath = settingsFilePath();
|
|
911
|
-
|
|
912
|
-
fs.writeFileSync(temporaryPath, JSON.stringify(document, null, 2) + "\n", "utf8");
|
|
913
|
-
fs.renameSync(temporaryPath, filePath);
|
|
688
|
+
fs.writeFileSync(filePath, JSON.stringify(document, null, 2) + "\n", "utf8");
|
|
914
689
|
return document;
|
|
915
690
|
}
|
|
916
691
|
|
|
@@ -973,37 +748,131 @@ function isSafeOperationName(value) {
|
|
|
973
748
|
});
|
|
974
749
|
}
|
|
975
750
|
|
|
976
|
-
function
|
|
977
|
-
if (
|
|
978
|
-
return
|
|
751
|
+
function normalizeOperationResult(value) {
|
|
752
|
+
if (isNexusResult(value)) {
|
|
753
|
+
return value.ok
|
|
754
|
+
? ok(encodeOperationValue(value.value))
|
|
755
|
+
: fail(value.error.code, value.error.message, encodeOperationValue(value.error.details));
|
|
979
756
|
}
|
|
980
|
-
return value
|
|
981
|
-
? Object.prototype.hasOwnProperty.call(value, "value")
|
|
982
|
-
: isRecord(value.error) && typeof value.error.code === "string" && typeof value.error.message === "string";
|
|
757
|
+
return ok(encodeOperationValue(value));
|
|
983
758
|
}
|
|
984
759
|
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
|
|
760
|
+
function isNexusResult(value) {
|
|
761
|
+
return isRecord(value)
|
|
762
|
+
&& typeof value.ok === "boolean"
|
|
763
|
+
&& (value.ok
|
|
764
|
+
? Object.prototype.hasOwnProperty.call(value, "value")
|
|
765
|
+
: isRecord(value.error) && typeof value.error.code === "string" && typeof value.error.message === "string");
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function encodeOperationValue(value, seen) {
|
|
769
|
+
const visited = seen || new WeakSet();
|
|
770
|
+
if (value === undefined) {
|
|
771
|
+
return { __nexusWire: "undefined" };
|
|
988
772
|
}
|
|
989
|
-
if (
|
|
990
|
-
return
|
|
773
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
774
|
+
return value;
|
|
991
775
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
776
|
+
if (typeof value === "bigint") {
|
|
777
|
+
return { __nexusWire: "bigint", value: value.toString(10) };
|
|
778
|
+
}
|
|
779
|
+
if (typeof value === "number") {
|
|
780
|
+
return Number.isFinite(value) ? value : null;
|
|
781
|
+
}
|
|
782
|
+
if (typeof value !== "object" || visited.has(value)) {
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
if (value instanceof Date) {
|
|
786
|
+
return { __nexusWire: "date", value: value.toISOString() };
|
|
787
|
+
}
|
|
788
|
+
if (value instanceof Error) {
|
|
789
|
+
return {
|
|
790
|
+
__nexusWire: "error",
|
|
791
|
+
name: value.name,
|
|
792
|
+
message: value.message,
|
|
793
|
+
...(value.stack ? { stack: value.stack } : {})
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
if (Buffer.isBuffer(value)) {
|
|
797
|
+
return { __nexusWire: "buffer", value: value.toString("hex") };
|
|
798
|
+
}
|
|
799
|
+
if (!Array.isArray(value) && !isPlainObject(value)) {
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
visited.add(value);
|
|
803
|
+
try {
|
|
804
|
+
if (Array.isArray(value)) {
|
|
805
|
+
return value.map(function encodeItem(item) {
|
|
806
|
+
return encodeOperationValue(item, visited);
|
|
807
|
+
});
|
|
998
808
|
}
|
|
999
|
-
|
|
809
|
+
const encoded = Object.fromEntries(Object.entries(value).map(function encodeEntry(entry) {
|
|
810
|
+
return [entry[0], encodeOperationValue(entry[1], visited)];
|
|
811
|
+
}));
|
|
812
|
+
return Object.prototype.hasOwnProperty.call(value, "__nexusWire")
|
|
813
|
+
? { __nexusWire: "object", value: encoded }
|
|
814
|
+
: encoded;
|
|
815
|
+
} finally {
|
|
816
|
+
visited.delete(value);
|
|
817
|
+
}
|
|
1000
818
|
}
|
|
1001
819
|
|
|
1002
|
-
function
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
820
|
+
function decodeOperationValue(value, seen) {
|
|
821
|
+
if (value === null || typeof value !== "object") {
|
|
822
|
+
return value;
|
|
823
|
+
}
|
|
824
|
+
const visited = seen || new WeakSet();
|
|
825
|
+
if (visited.has(value)) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
if (value.__nexusWire === "undefined") {
|
|
829
|
+
return undefined;
|
|
830
|
+
}
|
|
831
|
+
if (value.__nexusWire === "bigint" && typeof value.value === "string") {
|
|
832
|
+
try {
|
|
833
|
+
return BigInt(value.value);
|
|
834
|
+
} catch {
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
if (value.__nexusWire === "date" && typeof value.value === "string") {
|
|
839
|
+
const date = new Date(value.value);
|
|
840
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
841
|
+
}
|
|
842
|
+
if (value.__nexusWire === "buffer" && typeof value.value === "string") {
|
|
843
|
+
return value.value.length % 2 === 0 && /^[0-9a-f]*$/i.test(value.value)
|
|
844
|
+
? Buffer.from(value.value, "hex")
|
|
845
|
+
: null;
|
|
846
|
+
}
|
|
847
|
+
if (value.__nexusWire === "error" && typeof value.message === "string") {
|
|
848
|
+
const error = new Error(value.message);
|
|
849
|
+
if (typeof value.name === "string") {
|
|
850
|
+
error.name = value.name;
|
|
851
|
+
}
|
|
852
|
+
if (typeof value.stack === "string") {
|
|
853
|
+
error.stack = value.stack;
|
|
854
|
+
}
|
|
855
|
+
return error;
|
|
856
|
+
}
|
|
857
|
+
if (value.__nexusWire === "object" && isRecord(value.value)) {
|
|
858
|
+
return decodeOperationValue(value.value, visited);
|
|
859
|
+
}
|
|
860
|
+
visited.add(value);
|
|
861
|
+
try {
|
|
862
|
+
if (Array.isArray(value)) {
|
|
863
|
+
return value.map(function decodeItem(item) {
|
|
864
|
+
return decodeOperationValue(item, visited);
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
if (!isPlainObject(value)) {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
return Object.fromEntries(Object.entries(value).map(function decodeEntry(entry) {
|
|
871
|
+
return [entry[0], decodeOperationValue(entry[1], visited)];
|
|
872
|
+
}));
|
|
873
|
+
} finally {
|
|
874
|
+
visited.delete(value);
|
|
875
|
+
}
|
|
1007
876
|
}
|
|
1008
877
|
|
|
1009
878
|
function isPluginPermission(value) {
|
|
@@ -1011,9 +880,6 @@ function isPluginPermission(value) {
|
|
|
1011
880
|
}
|
|
1012
881
|
|
|
1013
882
|
function permissionSupportState(permission) {
|
|
1014
|
-
if (hostControlPermissions.has(permission)) {
|
|
1015
|
-
return "hostControl";
|
|
1016
|
-
}
|
|
1017
883
|
if (implementedPermissions.has(permission)) {
|
|
1018
884
|
return "implemented";
|
|
1019
885
|
}
|
|
@@ -1027,8 +893,6 @@ function createPermissionSummary(permission) {
|
|
|
1027
893
|
const supportState = permissionSupportState(permission);
|
|
1028
894
|
const statusMessage = supportState === "implemented"
|
|
1029
895
|
? "Available to trusted mainEntry Host API."
|
|
1030
|
-
: supportState === "hostControl"
|
|
1031
|
-
? "Grants direct control of Nexus Electron host objects; global changes can affect the application and may not be reversible."
|
|
1032
896
|
: supportState === "routeGuard"
|
|
1033
897
|
? "Declared for renderer route guards; not exposed to trusted mainEntry Host API."
|
|
1034
898
|
: "Reserved for a future Host API; trusted mainEntry calls return PluginCapabilityUnsupported.";
|
|
@@ -1061,6 +925,22 @@ function isRecord(value) {
|
|
|
1061
925
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1062
926
|
}
|
|
1063
927
|
|
|
928
|
+
function isPlainObject(value) {
|
|
929
|
+
if (!isRecord(value)) {
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
const prototype = Object.getPrototypeOf(value);
|
|
933
|
+
return prototype === Object.prototype || prototype === null;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function isPromiseLike(value) {
|
|
937
|
+
return isRecord(value) && typeof value.then === "function";
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function isAsyncFunction(value) {
|
|
941
|
+
return typeof value === "function" && value.constructor && value.constructor.name === "AsyncFunction";
|
|
942
|
+
}
|
|
943
|
+
|
|
1064
944
|
function isLogLevel(value) {
|
|
1065
945
|
return value === "debug" || value === "info" || value === "warn" || value === "error";
|
|
1066
946
|
}
|
|
@@ -1109,28 +989,6 @@ function isStrictVersion(value) {
|
|
|
1109
989
|
}));
|
|
1110
990
|
}
|
|
1111
991
|
|
|
1112
|
-
function parseWorkerInspector(args) {
|
|
1113
|
-
const prefix = "--worker-inspector=";
|
|
1114
|
-
const matches = args.filter(function isWorkerInspectorArgument(value) {
|
|
1115
|
-
return value.startsWith(prefix);
|
|
1116
|
-
});
|
|
1117
|
-
if (matches.length > 1) {
|
|
1118
|
-
throw new Error("--worker-inspector must not be repeated.");
|
|
1119
|
-
}
|
|
1120
|
-
if (matches.length === 0) {
|
|
1121
|
-
return undefined;
|
|
1122
|
-
}
|
|
1123
|
-
const match = /^(inspect|inspect-brk):([0-9]+)$/.exec(matches[0].slice(prefix.length));
|
|
1124
|
-
const port = match ? Number(match[2]) : 0;
|
|
1125
|
-
if (!match || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
1126
|
-
throw new Error("--worker-inspector must use inspect:<port> or inspect-brk:<port>.");
|
|
1127
|
-
}
|
|
1128
|
-
return {
|
|
1129
|
-
execArg: "--" + match[1] + "=" + String(port),
|
|
1130
|
-
breakOnStart: match[1] === "inspect-brk"
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
992
|
function requiredArgument(args, name) {
|
|
1135
993
|
const prefix = name + "=";
|
|
1136
994
|
const argument = args.find(function findArgument(value) {
|