@aiot-nexus/plugin-sdk 1.0.1 → 1.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.
@@ -0,0 +1,569 @@
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.shouldSuppressNativeBuildEvent = exports.isRetryablePluginDeploymentError = exports.createNexusHostArguments = exports.mergePluginSourceChange = exports.manifestChangeRequiresRestart = exports.classifyPluginSourceChange = exports.runPluginNexusDev = void 0;
27
+ const child_process_1 = require("child_process");
28
+ const fs = __importStar(require("fs"));
29
+ const path = __importStar(require("path"));
30
+ const dev_1 = require("./dev");
31
+ const nexus_dev_control_1 = require("./nexus-dev-control");
32
+ const nexus_dev_deployment_1 = require("./nexus-dev-deployment");
33
+ async function runPluginNexusDev(options, io) {
34
+ return new PluginNexusDevSession(options, io).run();
35
+ }
36
+ exports.runPluginNexusDev = runPluginNexusDev;
37
+ class PluginNexusDevSession {
38
+ constructor(options, io) {
39
+ this.options = options;
40
+ this.io = io;
41
+ this.control = new nexus_dev_control_1.NexusDevControlServer();
42
+ this.deployedPluginDirectory = "";
43
+ this.expectedExits = new Set();
44
+ this.provisionalChildren = new Set();
45
+ this.restarting = false;
46
+ this.stopping = false;
47
+ this.terminalSettled = false;
48
+ this.terminalPromise = new Promise((resolve) => {
49
+ this.terminalResolve = resolve;
50
+ });
51
+ this.handleSignal = () => {
52
+ if (this.stopping) {
53
+ return;
54
+ }
55
+ this.stopping = true;
56
+ this.watcher?.close();
57
+ void (async () => {
58
+ await this.watcher?.drain();
59
+ await this.stopCurrentHost();
60
+ this.settleTerminal(0);
61
+ })();
62
+ };
63
+ this.projectDirectory = path.resolve(options.projectDirectory);
64
+ this.nexusExecutable = (0, nexus_dev_deployment_1.resolveNexusExecutable)(options.hostPath);
65
+ }
66
+ async run() {
67
+ this.io.stdout("Building plugin for the packaged Nexus host: " + this.projectDirectory);
68
+ (0, dev_1.buildPluginForDevelopment)(this.projectDirectory);
69
+ const target = (0, dev_1.preparePluginDebugTarget)({ ...this.options, projectDirectory: this.projectDirectory });
70
+ const deployment = (0, nexus_dev_deployment_1.beginPluginPackageDeployment)(target.packageDirectory, this.nexusExecutable, target.pluginId);
71
+ deployment.commit();
72
+ this.target = target;
73
+ this.deployedPluginDirectory = path.join(path.dirname(this.nexusExecutable), "Plugins", target.pluginId);
74
+ this.io.stdout("Deployed plugin package: " + this.deployedPluginDirectory
75
+ + "\nContribution: " + target.surface + ":" + target.contributionId);
76
+ try {
77
+ await this.control.start();
78
+ this.disposeDisconnectHandler = this.control.setDisconnectHandler((error) => {
79
+ if (!this.stopping && !this.restarting) {
80
+ this.io.stderr("Packaged Nexus plugin development control disconnected: " + error.message);
81
+ this.settleTerminal(1);
82
+ }
83
+ });
84
+ this.installSignalHandlers();
85
+ await this.launchHost(target);
86
+ if (this.stopping) {
87
+ return await this.terminalPromise;
88
+ }
89
+ this.watcher = new PluginSourceWatcher(this.projectDirectory, async (kind) => {
90
+ const currentTarget = this.requireTarget();
91
+ await applyPluginSourceChange(kind, this.options, currentTarget, this.deployedPluginDirectory, this.control, this.io, () => this.restartHost(), () => this.runBuild(() => (0, dev_1.buildPluginRendererForDevelopment)(currentTarget.projectDirectory)));
92
+ }, this.io);
93
+ this.watcher.start();
94
+ this.io.stdout("Watching plugin source changes. Press Ctrl+C to stop.");
95
+ return await this.terminalPromise;
96
+ }
97
+ finally {
98
+ this.stopping = true;
99
+ this.watcher?.close();
100
+ await this.watcher?.drain();
101
+ this.removeSignalHandlers();
102
+ this.disposeDisconnectHandler?.();
103
+ this.disposeDisconnectHandler = undefined;
104
+ await this.stopCurrentHost();
105
+ await this.control.close();
106
+ }
107
+ }
108
+ async restartHost() {
109
+ const previousTarget = this.requireTarget();
110
+ this.io.stdout("Building complete plugin package before restarting Nexus...");
111
+ this.runBuild(() => (0, dev_1.buildPluginForDevelopment)(this.projectDirectory));
112
+ const updatedTarget = (0, dev_1.preparePluginDebugTarget)({
113
+ projectDirectory: this.projectDirectory,
114
+ surface: previousTarget.surface,
115
+ contributionId: previousTarget.contributionId,
116
+ language: this.options.language,
117
+ themeMode: this.options.themeMode
118
+ });
119
+ if (updatedTarget.pluginId !== previousTarget.pluginId) {
120
+ throw new dev_1.PluginDevError("Plugin id cannot change during a Nexus development session.");
121
+ }
122
+ this.restarting = true;
123
+ let transaction;
124
+ let transactionCommitted = false;
125
+ try {
126
+ await this.shutdownHostForRestart();
127
+ transaction = await retryFileOperation(() => (0, nexus_dev_deployment_1.beginPluginPackageDeployment)(updatedTarget.packageDirectory, this.nexusExecutable, updatedTarget.pluginId), "replace the plugin package");
128
+ try {
129
+ const updatedChild = await this.launchHost(updatedTarget, true);
130
+ await this.ensureProvisionalHostIsStable(updatedChild);
131
+ this.acceptProvisionalHost(updatedChild);
132
+ }
133
+ catch (error) {
134
+ await this.stopCurrentHost();
135
+ await retryFileOperation(() => transaction?.rollback(), "roll back the plugin package");
136
+ const previousChild = await this.launchHost(previousTarget, true);
137
+ await this.ensureProvisionalHostIsStable(previousChild);
138
+ this.acceptProvisionalHost(previousChild);
139
+ throw new dev_1.PluginDevError("Updated Nexus host failed to start; the previous plugin package was restored. " + errorMessage(error));
140
+ }
141
+ transactionCommitted = true;
142
+ this.restarting = false;
143
+ try {
144
+ await retryFileOperation(() => transaction?.commit(), "remove the previous plugin package backup");
145
+ }
146
+ catch (error) {
147
+ this.io.stderr("Updated plugin is active, but its previous package backup could not be removed: " + errorMessage(error));
148
+ }
149
+ this.target = updatedTarget;
150
+ this.io.stdout("Packaged Nexus restarted for plugin mainEntry update. PID: " + String(this.child?.pid || "unknown"));
151
+ }
152
+ catch (error) {
153
+ if (!this.child) {
154
+ if (transaction && !transactionCommitted) {
155
+ await retryFileOperation(() => transaction?.rollback(), "roll back the plugin package");
156
+ }
157
+ const previousChild = await this.launchHost(previousTarget, true);
158
+ await this.ensureProvisionalHostIsStable(previousChild);
159
+ this.acceptProvisionalHost(previousChild);
160
+ }
161
+ throw error;
162
+ }
163
+ finally {
164
+ this.restarting = false;
165
+ }
166
+ }
167
+ async launchHost(target, provisional = false) {
168
+ const hostArgs = createNexusHostArguments(this.control, target, this.options);
169
+ this.io.stdout("Starting packaged Nexus host: " + this.nexusExecutable);
170
+ const child = (0, child_process_1.spawn)(this.nexusExecutable, hostArgs, {
171
+ cwd: path.dirname(this.nexusExecutable),
172
+ env: process.env,
173
+ stdio: "inherit",
174
+ windowsHide: false
175
+ });
176
+ this.child = child;
177
+ if (provisional) {
178
+ this.provisionalChildren.add(child);
179
+ }
180
+ const hostExit = waitForHostExit(child);
181
+ this.childExit = hostExit;
182
+ hostExit.then((result) => this.handleHostExit(child, result), (error) => {
183
+ if (!this.restarting && !this.stopping) {
184
+ this.io.stderr(errorMessage(error));
185
+ this.settleTerminal(1);
186
+ }
187
+ });
188
+ await Promise.race([
189
+ this.control.waitForAuthentication(this.options.inspectorMode === "inspect-brk" ? 0 : 15000),
190
+ hostExit.then((result) => {
191
+ if (this.stopping) {
192
+ return;
193
+ }
194
+ throw new dev_1.PluginDevError(formatEarlyHostExit(result));
195
+ })
196
+ ]);
197
+ if (this.stopping) {
198
+ return child;
199
+ }
200
+ this.io.stdout("Packaged Nexus plugin development control connected. PID: " + String(child.pid || "unknown"));
201
+ return child;
202
+ }
203
+ handleHostExit(child, result) {
204
+ if (this.child === child) {
205
+ this.child = undefined;
206
+ this.childExit = undefined;
207
+ }
208
+ const provisional = this.provisionalChildren.delete(child);
209
+ if (this.expectedExits.delete(child) || provisional || this.stopping) {
210
+ return;
211
+ }
212
+ const exitCode = result.status === null ? 1 : result.status;
213
+ this.io.stderr("Packaged Nexus host exited unexpectedly"
214
+ + (result.status === null ? "" : " with code " + String(result.status))
215
+ + (result.signal ? " (" + result.signal + ")" : "") + ".");
216
+ this.settleTerminal(exitCode === 0 ? 1 : exitCode);
217
+ }
218
+ async shutdownHostForRestart() {
219
+ const child = this.child;
220
+ const hostExit = this.childExit;
221
+ if (!child || !hostExit) {
222
+ throw new dev_1.PluginDevError("Packaged Nexus host is not running.");
223
+ }
224
+ this.expectedExits.add(child);
225
+ try {
226
+ await this.control.requestShutdown();
227
+ }
228
+ catch (error) {
229
+ this.expectedExits.delete(child);
230
+ throw error;
231
+ }
232
+ try {
233
+ await promiseWithTimeout(hostExit, 10000, "Nexus did not exit after accepting the plugin development restart.");
234
+ }
235
+ catch (error) {
236
+ child.kill();
237
+ await promiseWithTimeout(hostExit, 5000, "Nexus could not be stopped for the plugin development restart.");
238
+ this.io.stderr("Nexus required forced termination before plugin replacement: " + errorMessage(error));
239
+ }
240
+ await this.control.waitForDisconnection();
241
+ }
242
+ async stopCurrentHost() {
243
+ const child = this.child;
244
+ const hostExit = this.childExit;
245
+ if (!child || !hostExit) {
246
+ return;
247
+ }
248
+ this.expectedExits.add(child);
249
+ try {
250
+ await this.control.requestShutdown();
251
+ await promiseWithTimeout(hostExit, 5000, "Nexus did not exit during plugin development shutdown.");
252
+ }
253
+ catch {
254
+ child.kill();
255
+ try {
256
+ await promiseWithTimeout(hostExit, 3000, "Nexus did not terminate after plugin development shutdown.");
257
+ }
258
+ catch {
259
+ // The CLI is already stopping; do not start another host or a restart loop.
260
+ }
261
+ }
262
+ }
263
+ async ensureProvisionalHostIsStable(child) {
264
+ await new Promise((resolve) => setTimeout(resolve, 250));
265
+ if (this.child !== child || child.exitCode !== null || child.signalCode !== null || !this.control.isConnected()) {
266
+ throw new dev_1.PluginDevError("Restarted Nexus host did not remain connected after authentication.");
267
+ }
268
+ }
269
+ acceptProvisionalHost(child) {
270
+ if (this.child !== child || !this.control.isConnected()) {
271
+ throw new dev_1.PluginDevError("Restarted Nexus host disconnected before activation completed.");
272
+ }
273
+ this.provisionalChildren.delete(child);
274
+ }
275
+ runBuild(operation) {
276
+ return this.watcher ? this.watcher.runBuild(operation) : operation();
277
+ }
278
+ installSignalHandlers() {
279
+ process.on("SIGINT", this.handleSignal);
280
+ process.on("SIGTERM", this.handleSignal);
281
+ }
282
+ removeSignalHandlers() {
283
+ process.removeListener("SIGINT", this.handleSignal);
284
+ process.removeListener("SIGTERM", this.handleSignal);
285
+ }
286
+ requireTarget() {
287
+ if (!this.target) {
288
+ throw new dev_1.PluginDevError("Plugin development target is not ready.");
289
+ }
290
+ return this.target;
291
+ }
292
+ settleTerminal(exitCode) {
293
+ if (this.terminalSettled) {
294
+ return;
295
+ }
296
+ this.terminalSettled = true;
297
+ this.terminalResolve?.(exitCode);
298
+ this.terminalResolve = undefined;
299
+ }
300
+ }
301
+ function classifyPluginSourceChange(projectDirectory, changedPath) {
302
+ const relativePath = path.relative(path.resolve(projectDirectory), path.resolve(changedPath)).replace(/\\/g, "/");
303
+ if (relativePath.startsWith("../") || relativePath === ".." || path.isAbsolute(relativePath)) {
304
+ return undefined;
305
+ }
306
+ if (relativePath === "src/manifest.json") {
307
+ return "manifest";
308
+ }
309
+ if (relativePath.startsWith("src/renderer/") || relativePath === "tsconfig.renderer.json") {
310
+ return "renderer";
311
+ }
312
+ if (relativePath.toLowerCase().endsWith(".node") || relativePath === "binding.gyp") {
313
+ return "restart";
314
+ }
315
+ if (relativePath.startsWith("src/")
316
+ || relativePath.startsWith("scripts/")
317
+ || relativePath === "package.json"
318
+ || relativePath === "package-lock.json"
319
+ || relativePath === "npm-shrinkwrap.json"
320
+ || relativePath === "tsconfig.main.json") {
321
+ return "restart";
322
+ }
323
+ return undefined;
324
+ }
325
+ exports.classifyPluginSourceChange = classifyPluginSourceChange;
326
+ function manifestChangeRequiresRestart(previous, next) {
327
+ const restartSensitiveFields = [
328
+ "schemaVersion",
329
+ "pluginId",
330
+ "pluginVersion",
331
+ "hostApiVersion",
332
+ "minHostVersion",
333
+ "permissions",
334
+ "mainEntry",
335
+ "deferred"
336
+ ];
337
+ return restartSensitiveFields.some((field) => stableJson(previous[field]) !== stableJson(next[field]));
338
+ }
339
+ exports.manifestChangeRequiresRestart = manifestChangeRequiresRestart;
340
+ function mergePluginSourceChange(current, incoming) {
341
+ const priority = { renderer: 1, manifest: 2, restart: 3 };
342
+ return !current || priority[incoming] > priority[current] ? incoming : current;
343
+ }
344
+ exports.mergePluginSourceChange = mergePluginSourceChange;
345
+ async function applyPluginSourceChange(kind, options, initialTarget, deployedPluginDirectory, control, io, restartHost, buildRenderer) {
346
+ if (kind === "restart") {
347
+ await restartHost();
348
+ return;
349
+ }
350
+ try {
351
+ io.stdout("Building plugin renderer update...");
352
+ const packageKind = buildRenderer();
353
+ const updatedTarget = (0, dev_1.preparePluginBrowserDebugTarget)({
354
+ projectDirectory: initialTarget.projectDirectory,
355
+ surface: initialTarget.surface,
356
+ contributionId: initialTarget.contributionId,
357
+ language: options.language,
358
+ themeMode: options.themeMode
359
+ }, packageKind);
360
+ if (updatedTarget.pluginId !== initialTarget.pluginId) {
361
+ throw new dev_1.PluginDevError("Plugin id cannot change during a Nexus development session.");
362
+ }
363
+ const rendererDirectory = path.join(updatedTarget.packageDirectory, "renderer");
364
+ if (kind === "manifest") {
365
+ const previousManifest = readManifest(path.join(deployedPluginDirectory, "manifest.json"));
366
+ if (manifestChangeRequiresRestart(previousManifest, updatedTarget.manifest)) {
367
+ await restartHost();
368
+ return;
369
+ }
370
+ const transaction = (0, nexus_dev_deployment_1.beginManifestAndRendererDeployment)(path.join(updatedTarget.packageDirectory, "manifest.json"), rendererDirectory, deployedPluginDirectory);
371
+ await finishUpdateTransaction(transaction, "manifestUpdated", initialTarget.pluginId, control, io);
372
+ return;
373
+ }
374
+ const transaction = (0, nexus_dev_deployment_1.beginRendererDeployment)(rendererDirectory, deployedPluginDirectory);
375
+ await finishUpdateTransaction(transaction, "rendererUpdated", initialTarget.pluginId, control, io);
376
+ }
377
+ catch (error) {
378
+ io.stderr("Plugin update failed; the previous deployed version remains active: " + errorMessage(error));
379
+ }
380
+ }
381
+ async function finishUpdateTransaction(transaction, updateType, pluginId, control, io) {
382
+ let result;
383
+ try {
384
+ result = await control.sendUpdate(updateType, pluginId);
385
+ }
386
+ catch (error) {
387
+ transaction.rollback();
388
+ throw error;
389
+ }
390
+ if (!result.ok) {
391
+ transaction.rollback();
392
+ if (updateType === "manifestUpdated") {
393
+ const rollbackResult = await control.sendUpdate("manifestUpdated", pluginId);
394
+ if (!rollbackResult.ok) {
395
+ throw new dev_1.PluginDevError(result.message + " Rollback scan also failed: " + rollbackResult.message);
396
+ }
397
+ }
398
+ throw new dev_1.PluginDevError(result.message);
399
+ }
400
+ transaction.commit();
401
+ io.stdout((updateType === "rendererUpdated" ? "Plugin renderer reloaded." : "Plugin manifest rescanned.")
402
+ + (result.requiresRestart ? " Existing mainEntry state will remain active until the next host restart." : ""));
403
+ }
404
+ function createNexusHostArguments(control, target, options) {
405
+ const args = [];
406
+ if (options.inspectorMode) {
407
+ args.push("--" + options.inspectorMode + "=" + String(options.inspectorPort || 9229));
408
+ }
409
+ args.push("--plugin-dev", "--plugin-dev-control=" + control.pipePath, "--plugin-dev-token=" + control.token, "--page=" + resolveContributionPage(target));
410
+ if (target.surface !== "business") {
411
+ args.push("--page-param=nexusPluginDevContribution=" + target.contributionId);
412
+ }
413
+ if (!options.openDevTools) {
414
+ args.push("--plugin-dev-no-devtools");
415
+ }
416
+ return args;
417
+ }
418
+ exports.createNexusHostArguments = createNexusHostArguments;
419
+ function resolveContributionPage(target) {
420
+ if (target.surface === "tool") {
421
+ return "toolbox";
422
+ }
423
+ if (target.surface === "settings") {
424
+ return "settings";
425
+ }
426
+ const entry = target.manifest.businessEntries?.find((item) => {
427
+ return item.component === "PluginFrame"
428
+ && (item.businessId === target.contributionId || item.pageRoute === target.contributionId);
429
+ });
430
+ return entry?.pageRoute || entry?.businessId || target.contributionId;
431
+ }
432
+ function waitForHostExit(child) {
433
+ return new Promise((resolve, reject) => {
434
+ child.once("error", (error) => reject(new dev_1.PluginDevError("Packaged Nexus host could not start: " + error.message)));
435
+ child.once("exit", (status, signal) => resolve({ status, signal }));
436
+ });
437
+ }
438
+ function formatEarlyHostExit(result) {
439
+ return "Packaged Nexus host exited before plugin development control authentication"
440
+ + (result.status === null ? "" : " with code " + String(result.status))
441
+ + (result.signal ? " (" + result.signal + ")" : "") + ".";
442
+ }
443
+ class PluginSourceWatcher {
444
+ constructor(projectDirectory, applyChange, io) {
445
+ this.projectDirectory = projectDirectory;
446
+ this.applyChange = applyChange;
447
+ this.io = io;
448
+ this.queue = Promise.resolve();
449
+ this.closed = false;
450
+ this.suppressNativeEvents = false;
451
+ this.ignoreNativeEventsUntil = 0;
452
+ }
453
+ start() {
454
+ this.watcher = fs.watch(this.projectDirectory, { recursive: true }, (_eventType, fileName) => {
455
+ if (!fileName || this.closed) {
456
+ return;
457
+ }
458
+ const changedPath = path.join(this.projectDirectory, fileName.toString());
459
+ if (shouldSuppressNativeBuildEvent(changedPath, this.suppressNativeEvents, this.ignoreNativeEventsUntil)) {
460
+ return;
461
+ }
462
+ const kind = classifyPluginSourceChange(this.projectDirectory, changedPath);
463
+ if (!kind) {
464
+ return;
465
+ }
466
+ this.pendingKind = mergePluginSourceChange(this.pendingKind, kind);
467
+ if (this.timer) {
468
+ clearTimeout(this.timer);
469
+ }
470
+ this.timer = setTimeout(() => this.flush(), 180);
471
+ });
472
+ this.watcher.on("error", (error) => this.io.stderr("Plugin source watcher failed: " + error.message));
473
+ }
474
+ close() {
475
+ this.closed = true;
476
+ if (this.timer) {
477
+ clearTimeout(this.timer);
478
+ this.timer = undefined;
479
+ }
480
+ this.watcher?.close();
481
+ this.watcher = undefined;
482
+ }
483
+ drain() {
484
+ return this.queue;
485
+ }
486
+ runBuild(operation) {
487
+ this.suppressNativeEvents = true;
488
+ try {
489
+ return operation();
490
+ }
491
+ finally {
492
+ this.suppressNativeEvents = false;
493
+ this.ignoreNativeEventsUntil = Date.now() + 750;
494
+ }
495
+ }
496
+ flush() {
497
+ this.timer = undefined;
498
+ const kind = this.pendingKind;
499
+ this.pendingKind = undefined;
500
+ if (!kind || this.closed) {
501
+ return;
502
+ }
503
+ this.queue = this.queue
504
+ .then(() => this.applyChange(kind))
505
+ .catch((error) => this.io.stderr("Plugin update queue failed: " + errorMessage(error)));
506
+ }
507
+ }
508
+ function readManifest(filePath) {
509
+ try {
510
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
511
+ }
512
+ catch (error) {
513
+ throw new dev_1.PluginDevError("Deployed plugin manifest is invalid: " + errorMessage(error));
514
+ }
515
+ }
516
+ function stableJson(value) {
517
+ if (Array.isArray(value)) {
518
+ return "[" + value.map(stableJson).join(",") + "]";
519
+ }
520
+ if (value && typeof value === "object") {
521
+ return "{" + Object.keys(value).sort().map((key) => {
522
+ return JSON.stringify(key) + ":" + stableJson(value[key]);
523
+ }).join(",") + "}";
524
+ }
525
+ return JSON.stringify(value);
526
+ }
527
+ function errorMessage(error) {
528
+ return error instanceof Error ? error.message : String(error);
529
+ }
530
+ function isRetryablePluginDeploymentError(error) {
531
+ const value = errorMessage(error).toUpperCase();
532
+ return value.includes("EBUSY") || value.includes("EPERM") || value.includes("EACCES") || value.includes("FILE IS BEING USED");
533
+ }
534
+ exports.isRetryablePluginDeploymentError = isRetryablePluginDeploymentError;
535
+ function shouldSuppressNativeBuildEvent(changedPath, buildRunning, ignoreUntil, now = Date.now()) {
536
+ return changedPath.toLowerCase().endsWith(".node") && (buildRunning || now <= ignoreUntil);
537
+ }
538
+ exports.shouldSuppressNativeBuildEvent = shouldSuppressNativeBuildEvent;
539
+ async function retryFileOperation(operation, label) {
540
+ const delays = [0, 75, 150, 300, 600, 900, 1200];
541
+ let lastError;
542
+ for (const delay of delays) {
543
+ if (delay > 0) {
544
+ await new Promise((resolve) => setTimeout(resolve, delay));
545
+ }
546
+ try {
547
+ return operation();
548
+ }
549
+ catch (error) {
550
+ lastError = error;
551
+ if (!isRetryablePluginDeploymentError(error)) {
552
+ throw error;
553
+ }
554
+ }
555
+ }
556
+ throw new dev_1.PluginDevError("Could not " + label + " after waiting for Nexus file locks: " + errorMessage(lastError));
557
+ }
558
+ function promiseWithTimeout(promise, timeoutMs, message) {
559
+ return new Promise((resolve, reject) => {
560
+ const timer = setTimeout(() => reject(new dev_1.PluginDevError(message)), timeoutMs);
561
+ promise.then((value) => {
562
+ clearTimeout(timer);
563
+ resolve(value);
564
+ }, (error) => {
565
+ clearTimeout(timer);
566
+ reject(error);
567
+ });
568
+ });
569
+ }
@@ -199,7 +199,10 @@ function vendorSdkArchive(pluginDirectory, sourcePath) {
199
199
  function createPackageJson(options, sdkSource) {
200
200
  const scripts = {
201
201
  build: "node scripts/build.js && npm run validate",
202
+ "build:web": "node scripts/build.js --renderer-only",
202
203
  dev: "nexus-plugin dev .",
204
+ "dev:web": "nexus-plugin dev:web .",
205
+ "dev:nexus": "nexus-plugin dev:nexus .",
203
206
  "dev:debug": "nexus-plugin dev . --inspect-brk=9229",
204
207
  typecheck: "tsc -p tsconfig.main.json --noEmit && tsc -p tsconfig.renderer.json --noEmit",
205
208
  validate: "nexus-plugin validate dist/" + options.pluginId
@@ -6,4 +6,7 @@ export interface PluginPackageValidationResult {
6
6
  manifest?: PluginManifest;
7
7
  errors: string[];
8
8
  }
9
- export declare function validatePluginPackage(packageDirectory: string): PluginPackageValidationResult;
9
+ export interface PluginPackageValidationOptions {
10
+ target?: "package" | "renderer";
11
+ }
12
+ export declare function validatePluginPackage(packageDirectory: string, options?: PluginPackageValidationOptions): PluginPackageValidationResult;
@@ -34,17 +34,17 @@ const plugin_manifest_schema_json_1 = __importDefault(require("../.generated/plu
34
34
  const plugin_framework_version_1 = require("../.generated/plugin-framework-version");
35
35
  const manifestValidator = new _2020_1.default({ allErrors: true, strict: true })
36
36
  .compile(plugin_manifest_schema_json_1.default);
37
- function validatePluginPackage(packageDirectory) {
37
+ function validatePluginPackage(packageDirectory, options = {}) {
38
38
  const resolvedDirectory = path.resolve(packageDirectory);
39
39
  try {
40
- return validateResolvedPluginPackage(resolvedDirectory);
40
+ return validateResolvedPluginPackage(resolvedDirectory, options);
41
41
  }
42
42
  catch (error) {
43
43
  return invalid(resolvedDirectory, "Plugin package could not be inspected: " + errorMessage(error));
44
44
  }
45
45
  }
46
46
  exports.validatePluginPackage = validatePluginPackage;
47
- function validateResolvedPluginPackage(resolvedDirectory) {
47
+ function validateResolvedPluginPackage(resolvedDirectory, options) {
48
48
  const errors = [];
49
49
  if (!fs.existsSync(resolvedDirectory)) {
50
50
  return invalid(resolvedDirectory, "Plugin package directory does not exist.");
@@ -88,7 +88,7 @@ function validateResolvedPluginPackage(resolvedDirectory) {
88
88
  if (!(0, plugin_framework_version_1.parsePluginCompatibleVersion)(manifest.minHostVersion)) {
89
89
  errors.push("minHostVersion contains an unsupported numeric version.");
90
90
  }
91
- validateEntry(resolvedDirectory, "mainEntry", manifest.mainEntry, errors);
91
+ validateEntry(resolvedDirectory, "mainEntry", manifest.mainEntry, errors, options.target !== "renderer");
92
92
  validateEntry(resolvedDirectory, "rendererEntry", manifest.rendererEntry, errors);
93
93
  if (manifest.rendererEntry && path.extname(manifest.rendererEntry).toLowerCase() !== ".html") {
94
94
  errors.push("rendererEntry must point to an HTML file.");
@@ -104,7 +104,7 @@ function validateResolvedPluginPackage(resolvedDirectory) {
104
104
  errors
105
105
  };
106
106
  }
107
- function validateEntry(rootDirectory, fieldName, relativePath, errors) {
107
+ function validateEntry(rootDirectory, fieldName, relativePath, errors, requireExistingFile = true) {
108
108
  if (!relativePath) {
109
109
  return;
110
110
  }
@@ -118,7 +118,9 @@ function validateEntry(rootDirectory, fieldName, relativePath, errors) {
118
118
  return;
119
119
  }
120
120
  if (!fs.existsSync(entryPath)) {
121
- errors.push(fieldName + " does not exist: " + relativePath);
121
+ if (requireExistingFile) {
122
+ errors.push(fieldName + " does not exist: " + relativePath);
123
+ }
122
124
  return;
123
125
  }
124
126
  const entryStat = fs.lstatSync(entryPath);
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ import * as http from "http";
3
+ import { type PluginDebugSelection, type PluginDebugTarget, type PluginDevIo } from "./dev";
4
+ export interface PluginWebDevOptions extends PluginDebugSelection {
5
+ build: boolean;
6
+ openBrowser: boolean;
7
+ port: number;
8
+ }
9
+ export declare function runPluginWebDev(options: PluginWebDevOptions, io: PluginDevIo): number;
10
+ export declare function createPluginWebDevServer(target: PluginDebugTarget, io: PluginDevIo): http.Server;
11
+ export declare function pluginWebDevServerUrl(server: http.Server): string;