@aiot-nexus/plugin-sdk 2.1.1 → 3.1.0
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 +197 -90
- package/dev-host/main.js +366 -505
- 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 +3 -2
- 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 +81 -6
- package/dist/src/cli/update-notice.js +1 -1
- 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 +3 -3
- 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/scaffold.js +6 -5
- package/dist/src/sdk-info.d.ts +17 -0
- package/dist/src/sdk-info.js +23 -0
- package/dist/src/types.d.ts +15 -98
- package/dist/src/upgrade.d.ts +44 -0
- package/dist/src/upgrade.js +1189 -0
- package/dist/src/validation.js +9 -0
- package/package.json +2 -2
- package/scripts/sdk-update-check.js +1 -1
- package/templates/minimal/.vscode/launch.json +1 -1
- package/templates/minimal/README.md.tmpl +18 -17
- package/templates/minimal/src/main.ts.tmpl +5 -4
- package/templates/sqlite/.vscode/launch.json +1 -1
- package/templates/sqlite/README.md.tmpl +15 -13
- 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.1.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.1.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.1.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.1.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
|
}
|
|
@@ -680,8 +440,11 @@ async function verifyRendererContextSmoke() {
|
|
|
680
440
|
if (!frame) {
|
|
681
441
|
throw new Error("Plugin renderer frame was not found.");
|
|
682
442
|
}
|
|
683
|
-
if (
|
|
684
|
-
|
|
443
|
+
if (!sdk.isPluginFrameworkVersionCompatible(
|
|
444
|
+
configuration.manifest.hostApiVersion,
|
|
445
|
+
sdk.PLUGIN_FRAMEWORK_VERSION
|
|
446
|
+
)
|
|
447
|
+
|| (configuration.manifest.mainEntry && !pluginActivated)
|
|
685
448
|
|| configuration.manifest.minHostVersion === undefined) {
|
|
686
449
|
throw new Error("Development Host API version contract is inconsistent.");
|
|
687
450
|
}
|
|
@@ -817,15 +580,32 @@ async function invokeMain(params) {
|
|
|
817
580
|
if (!isSafeOperationName(params.operation)) {
|
|
818
581
|
return fail("InvalidRequest", "Plugin renderer operation name is invalid.", { operation: params.operation });
|
|
819
582
|
}
|
|
820
|
-
if (!
|
|
583
|
+
if (!pluginActivated) {
|
|
821
584
|
return fail("PluginCapabilityUnsupported", "Plugin mainEntry is not available in this debug target.", { operation: params.operation });
|
|
822
585
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
586
|
+
const handler = rendererOperations.get(params.operation);
|
|
587
|
+
if (!handler) {
|
|
588
|
+
return fail("NotFound", "Plugin renderer operation was not registered.", {
|
|
589
|
+
pluginId: configuration.manifest.pluginId,
|
|
590
|
+
operation: params.operation
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
const value = await handler({
|
|
595
|
+
pluginId: configuration.manifest.pluginId,
|
|
596
|
+
surface: configuration.surface,
|
|
597
|
+
contributionId: configuration.contributionId,
|
|
598
|
+
operation: params.operation,
|
|
599
|
+
input: isRecord(params.input) ? decodeOperationValue(params.input) : undefined
|
|
600
|
+
});
|
|
601
|
+
return normalizeOperationResult(value);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
return fail("PluginMainOperationFailed", "Plugin renderer operation failed.", {
|
|
604
|
+
pluginId: configuration.manifest.pluginId,
|
|
605
|
+
operation: params.operation,
|
|
606
|
+
error: errorMessage(error)
|
|
607
|
+
});
|
|
608
|
+
}
|
|
829
609
|
}
|
|
830
610
|
|
|
831
611
|
function createWindow() {
|
|
@@ -905,12 +685,10 @@ function saveSettings(values) {
|
|
|
905
685
|
const document = {
|
|
906
686
|
schemaVersion: sdk.PLUGIN_MANIFEST_SCHEMA_VERSION,
|
|
907
687
|
pluginId: configuration.manifest.pluginId,
|
|
908
|
-
values:
|
|
688
|
+
values: { ...existing.values, ...sanitizeJsonObject(values) }
|
|
909
689
|
};
|
|
910
690
|
const filePath = settingsFilePath();
|
|
911
|
-
|
|
912
|
-
fs.writeFileSync(temporaryPath, JSON.stringify(document, null, 2) + "\n", "utf8");
|
|
913
|
-
fs.renameSync(temporaryPath, filePath);
|
|
691
|
+
fs.writeFileSync(filePath, JSON.stringify(document, null, 2) + "\n", "utf8");
|
|
914
692
|
return document;
|
|
915
693
|
}
|
|
916
694
|
|
|
@@ -973,37 +751,131 @@ function isSafeOperationName(value) {
|
|
|
973
751
|
});
|
|
974
752
|
}
|
|
975
753
|
|
|
976
|
-
function
|
|
977
|
-
if (
|
|
978
|
-
return
|
|
754
|
+
function normalizeOperationResult(value) {
|
|
755
|
+
if (isNexusResult(value)) {
|
|
756
|
+
return value.ok
|
|
757
|
+
? ok(encodeOperationValue(value.value))
|
|
758
|
+
: fail(value.error.code, value.error.message, encodeOperationValue(value.error.details));
|
|
979
759
|
}
|
|
980
|
-
return value
|
|
981
|
-
? Object.prototype.hasOwnProperty.call(value, "value")
|
|
982
|
-
: isRecord(value.error) && typeof value.error.code === "string" && typeof value.error.message === "string";
|
|
760
|
+
return ok(encodeOperationValue(value));
|
|
983
761
|
}
|
|
984
762
|
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
|
|
763
|
+
function isNexusResult(value) {
|
|
764
|
+
return isRecord(value)
|
|
765
|
+
&& typeof value.ok === "boolean"
|
|
766
|
+
&& (value.ok
|
|
767
|
+
? Object.prototype.hasOwnProperty.call(value, "value")
|
|
768
|
+
: isRecord(value.error) && typeof value.error.code === "string" && typeof value.error.message === "string");
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function encodeOperationValue(value, seen) {
|
|
772
|
+
const visited = seen || new WeakSet();
|
|
773
|
+
if (value === undefined) {
|
|
774
|
+
return { __nexusWire: "undefined" };
|
|
988
775
|
}
|
|
989
|
-
if (
|
|
990
|
-
return
|
|
776
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
777
|
+
return value;
|
|
991
778
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
779
|
+
if (typeof value === "bigint") {
|
|
780
|
+
return { __nexusWire: "bigint", value: value.toString(10) };
|
|
781
|
+
}
|
|
782
|
+
if (typeof value === "number") {
|
|
783
|
+
return Number.isFinite(value) ? value : null;
|
|
784
|
+
}
|
|
785
|
+
if (typeof value !== "object" || visited.has(value)) {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
if (value instanceof Date) {
|
|
789
|
+
return { __nexusWire: "date", value: value.toISOString() };
|
|
790
|
+
}
|
|
791
|
+
if (value instanceof Error) {
|
|
792
|
+
return {
|
|
793
|
+
__nexusWire: "error",
|
|
794
|
+
name: value.name,
|
|
795
|
+
message: value.message,
|
|
796
|
+
...(value.stack ? { stack: value.stack } : {})
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if (Buffer.isBuffer(value)) {
|
|
800
|
+
return { __nexusWire: "buffer", value: value.toString("hex") };
|
|
801
|
+
}
|
|
802
|
+
if (!Array.isArray(value) && !isPlainObject(value)) {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
visited.add(value);
|
|
806
|
+
try {
|
|
807
|
+
if (Array.isArray(value)) {
|
|
808
|
+
return value.map(function encodeItem(item) {
|
|
809
|
+
return encodeOperationValue(item, visited);
|
|
810
|
+
});
|
|
998
811
|
}
|
|
999
|
-
|
|
812
|
+
const encoded = Object.fromEntries(Object.entries(value).map(function encodeEntry(entry) {
|
|
813
|
+
return [entry[0], encodeOperationValue(entry[1], visited)];
|
|
814
|
+
}));
|
|
815
|
+
return Object.prototype.hasOwnProperty.call(value, "__nexusWire")
|
|
816
|
+
? { __nexusWire: "object", value: encoded }
|
|
817
|
+
: encoded;
|
|
818
|
+
} finally {
|
|
819
|
+
visited.delete(value);
|
|
820
|
+
}
|
|
1000
821
|
}
|
|
1001
822
|
|
|
1002
|
-
function
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
823
|
+
function decodeOperationValue(value, seen) {
|
|
824
|
+
if (value === null || typeof value !== "object") {
|
|
825
|
+
return value;
|
|
826
|
+
}
|
|
827
|
+
const visited = seen || new WeakSet();
|
|
828
|
+
if (visited.has(value)) {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
if (value.__nexusWire === "undefined") {
|
|
832
|
+
return undefined;
|
|
833
|
+
}
|
|
834
|
+
if (value.__nexusWire === "bigint" && typeof value.value === "string") {
|
|
835
|
+
try {
|
|
836
|
+
return BigInt(value.value);
|
|
837
|
+
} catch {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (value.__nexusWire === "date" && typeof value.value === "string") {
|
|
842
|
+
const date = new Date(value.value);
|
|
843
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
844
|
+
}
|
|
845
|
+
if (value.__nexusWire === "buffer" && typeof value.value === "string") {
|
|
846
|
+
return value.value.length % 2 === 0 && /^[0-9a-f]*$/i.test(value.value)
|
|
847
|
+
? Buffer.from(value.value, "hex")
|
|
848
|
+
: null;
|
|
849
|
+
}
|
|
850
|
+
if (value.__nexusWire === "error" && typeof value.message === "string") {
|
|
851
|
+
const error = new Error(value.message);
|
|
852
|
+
if (typeof value.name === "string") {
|
|
853
|
+
error.name = value.name;
|
|
854
|
+
}
|
|
855
|
+
if (typeof value.stack === "string") {
|
|
856
|
+
error.stack = value.stack;
|
|
857
|
+
}
|
|
858
|
+
return error;
|
|
859
|
+
}
|
|
860
|
+
if (value.__nexusWire === "object" && isRecord(value.value)) {
|
|
861
|
+
return decodeOperationValue(value.value, visited);
|
|
862
|
+
}
|
|
863
|
+
visited.add(value);
|
|
864
|
+
try {
|
|
865
|
+
if (Array.isArray(value)) {
|
|
866
|
+
return value.map(function decodeItem(item) {
|
|
867
|
+
return decodeOperationValue(item, visited);
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
if (!isPlainObject(value)) {
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
return Object.fromEntries(Object.entries(value).map(function decodeEntry(entry) {
|
|
874
|
+
return [entry[0], decodeOperationValue(entry[1], visited)];
|
|
875
|
+
}));
|
|
876
|
+
} finally {
|
|
877
|
+
visited.delete(value);
|
|
878
|
+
}
|
|
1007
879
|
}
|
|
1008
880
|
|
|
1009
881
|
function isPluginPermission(value) {
|
|
@@ -1011,9 +883,6 @@ function isPluginPermission(value) {
|
|
|
1011
883
|
}
|
|
1012
884
|
|
|
1013
885
|
function permissionSupportState(permission) {
|
|
1014
|
-
if (hostControlPermissions.has(permission)) {
|
|
1015
|
-
return "hostControl";
|
|
1016
|
-
}
|
|
1017
886
|
if (implementedPermissions.has(permission)) {
|
|
1018
887
|
return "implemented";
|
|
1019
888
|
}
|
|
@@ -1027,8 +896,6 @@ function createPermissionSummary(permission) {
|
|
|
1027
896
|
const supportState = permissionSupportState(permission);
|
|
1028
897
|
const statusMessage = supportState === "implemented"
|
|
1029
898
|
? "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
899
|
: supportState === "routeGuard"
|
|
1033
900
|
? "Declared for renderer route guards; not exposed to trusted mainEntry Host API."
|
|
1034
901
|
: "Reserved for a future Host API; trusted mainEntry calls return PluginCapabilityUnsupported.";
|
|
@@ -1061,6 +928,22 @@ function isRecord(value) {
|
|
|
1061
928
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1062
929
|
}
|
|
1063
930
|
|
|
931
|
+
function isPlainObject(value) {
|
|
932
|
+
if (!isRecord(value)) {
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
const prototype = Object.getPrototypeOf(value);
|
|
936
|
+
return prototype === Object.prototype || prototype === null;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function isPromiseLike(value) {
|
|
940
|
+
return isRecord(value) && typeof value.then === "function";
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function isAsyncFunction(value) {
|
|
944
|
+
return typeof value === "function" && value.constructor && value.constructor.name === "AsyncFunction";
|
|
945
|
+
}
|
|
946
|
+
|
|
1064
947
|
function isLogLevel(value) {
|
|
1065
948
|
return value === "debug" || value === "info" || value === "warn" || value === "error";
|
|
1066
949
|
}
|
|
@@ -1109,28 +992,6 @@ function isStrictVersion(value) {
|
|
|
1109
992
|
}));
|
|
1110
993
|
}
|
|
1111
994
|
|
|
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
995
|
function requiredArgument(args, name) {
|
|
1135
996
|
const prefix = name + "=";
|
|
1136
997
|
const argument = args.find(function findArgument(value) {
|