@aiot-nexus/plugin-sdk 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }
@@ -202,6 +202,7 @@ function createPackageJson(options, sdkSource) {
202
202
  "build:web": "node scripts/build.js --renderer-only",
203
203
  dev: "nexus-plugin dev .",
204
204
  "dev:web": "nexus-plugin dev:web .",
205
+ "dev:nexus": "nexus-plugin dev:nexus .",
205
206
  "dev:debug": "nexus-plugin dev . --inspect-brk=9229",
206
207
  typecheck: "tsc -p tsconfig.main.json --noEmit && tsc -p tsconfig.renderer.json --noEmit",
207
208
  validate: "nexus-plugin validate dist/" + options.pluginId
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-nexus/plugin-sdk",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "Types, PluginFrame client, and CLI for AIoT Nexus independent plugins.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -30,12 +30,14 @@
30
30
  "dist",
31
31
  "dev-host",
32
32
  "templates",
33
- "README.md"
33
+ "README.md",
34
+ "scripts/sdk-update-check.js"
34
35
  ],
35
36
  "scripts": {
36
37
  "build": "node scripts/build.js",
38
+ "postinstall": "node scripts/sdk-update-check.js --install",
37
39
  "verify:package": "node scripts/verify-package.js",
38
- "test": "npm run build && node tests/package.test.js && node tests/cli.test.js && node tests/dev.test.js && node tests/frame-client.test.js && node tests/web-dev.test.js && npm run verify:package",
40
+ "test": "npm run build && node tests/package.test.js && node tests/sdk-update-check.test.js && node tests/cli.test.js && node tests/cli-entry.test.js && node tests/update-notice.test.js && node tests/dev.test.js && node tests/nexus-dev.test.js && node tests/frame-client.test.js && node tests/web-dev.test.js && npm run verify:package",
39
41
  "prepack": "node scripts/prepack.js",
40
42
  "pack:sdk": "npm run build && npm run verify:package && node scripts/pack.js",
41
43
  "publish:sdk": "npm publish"