@elementspanel/epanel-plugin-instance 1.0.5 → 1.0.7

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.
@@ -9468,6 +9468,7 @@ Object.defineProperty(exports, "arrayUnique", ({ enumerable: true, get: function
9468
9468
  __exportStar(__webpack_require__("../common/src/plugin_contract.ts"), exports);
9469
9469
  __exportStar(__webpack_require__("../common/src/plugin_overrides.ts"), exports);
9470
9470
  __exportStar(__webpack_require__("../common/src/plugin_lifecycle.ts"), exports);
9471
+ __exportStar(__webpack_require__("../common/src/process_restart.ts"), exports);
9471
9472
  __exportStar(__webpack_require__("../common/src/plugin_revision.ts"), exports);
9472
9473
  __exportStar(__webpack_require__("../common/src/plugin_modules.ts"), exports);
9473
9474
  __exportStar(__webpack_require__("../common/src/plugin_watch.ts"), exports);
@@ -10385,6 +10386,39 @@ async function loadPluginModule(entry, directory, revision) {
10385
10386
 
10386
10387
  "use strict";
10387
10388
 
10389
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10390
+ if (k2 === undefined) k2 = k;
10391
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10392
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10393
+ desc = { enumerable: true, get: function() { return m[k]; } };
10394
+ }
10395
+ Object.defineProperty(o, k2, desc);
10396
+ }) : (function(o, m, k, k2) {
10397
+ if (k2 === undefined) k2 = k;
10398
+ o[k2] = m[k];
10399
+ }));
10400
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10401
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
10402
+ }) : function(o, v) {
10403
+ o["default"] = v;
10404
+ });
10405
+ var __importStar = (this && this.__importStar) || (function () {
10406
+ var ownKeys = function(o) {
10407
+ ownKeys = Object.getOwnPropertyNames || function (o) {
10408
+ var ar = [];
10409
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
10410
+ return ar;
10411
+ };
10412
+ return ownKeys(o);
10413
+ };
10414
+ return function (mod) {
10415
+ if (mod && mod.__esModule) return mod;
10416
+ var result = {};
10417
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
10418
+ __setModuleDefault(result, mod);
10419
+ return result;
10420
+ };
10421
+ })();
10388
10422
  var __importDefault = (this && this.__importDefault) || function (mod) {
10389
10423
  return (mod && mod.__esModule) ? mod : { "default": mod };
10390
10424
  };
@@ -10404,6 +10438,8 @@ const path_1 = __importDefault(__webpack_require__("path"));
10404
10438
  const module_1 = __webpack_require__("module");
10405
10439
  const plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
10406
10440
  const plugin_contract_1 = __webpack_require__("../common/src/plugin_contract.ts");
10441
+ // @ts-ignore Shared by the source runtime and the published npm launcher.
10442
+ const npmPlugins = __importStar(__webpack_require__("../../scripts/runtime/npm-plugins.cjs"));
10407
10443
  const PLUGIN_ID = /^[a-z][a-z0-9_-]{1,63}$/;
10408
10444
  const PACKAGE_NAME = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
10409
10445
  const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$/;
@@ -10445,11 +10481,20 @@ function currentProjectRoot(packageRoot = currentPackageRoot()) {
10445
10481
  return scope;
10446
10482
  }
10447
10483
  function npmPluginRoots(options = {}) {
10448
- var _a, _b;
10484
+ var _a, _b, _c;
10449
10485
  const packageRoot = path_1.default.resolve((_a = options.packageRoot) !== null && _a !== void 0 ? _a : currentPackageRoot());
10486
+ const projectRoot = path_1.default.resolve((_b = options.projectRoot) !== null && _b !== void 0 ? _b : currentProjectRoot(packageRoot));
10487
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
10488
+ const side = options.side ||
10489
+ stringValue((_c = objectValue(host === null || host === void 0 ? void 0 : host.elementspanel)) === null || _c === void 0 ? void 0 : _c.side) ||
10490
+ path_1.default.basename(packageRoot);
10450
10491
  return {
10451
10492
  packageRoot,
10452
- projectRoot: path_1.default.resolve((_b = options.projectRoot) !== null && _b !== void 0 ? _b : currentProjectRoot(packageRoot))
10493
+ projectRoot,
10494
+ dependencyRoot: path_1.default.dirname(npmPlugins.projectManifestFile(packageRoot, side, {
10495
+ projectRoot,
10496
+ runtimeDirectory: process.env.EPANEL_RUNTIME_ROOT
10497
+ }))
10453
10498
  };
10454
10499
  }
10455
10500
  function projectManifestFile(projectRoot) {
@@ -10480,15 +10525,10 @@ function pluginRecord(pluginId, name, source, packageRoot, directory, folder, ve
10480
10525
  function readPackageEntry(packageRoot, name) {
10481
10526
  // Read package boundaries directly: Node caches require.resolve() paths even
10482
10527
  // after npm moves an installation between nested and hoisted node_modules.
10483
- let manifestPath;
10484
- for (const root of [packageRoot, currentProjectRoot(packageRoot)]) {
10485
- const localRequire = (0, module_1.createRequire)(path_1.default.join(root, "package.json"));
10486
- manifestPath = (localRequire.resolve.paths(name) || [])
10487
- .map((directory) => path_1.default.join(directory, name, "package.json"))
10488
- .find((filename) => fs_extra_1.default.existsSync(filename));
10489
- if (manifestPath)
10490
- break;
10491
- }
10528
+ const localRequire = (0, module_1.createRequire)(path_1.default.join(packageRoot, "package.json"));
10529
+ let manifestPath = (localRequire.resolve.paths(name) || [])
10530
+ .map((directory) => path_1.default.join(directory, name, "package.json"))
10531
+ .find((filename) => fs_extra_1.default.existsSync(filename));
10492
10532
  if (!manifestPath)
10493
10533
  throw new Error(`Missing npm plugin package: ${name}.`);
10494
10534
  manifestPath = fs_extra_1.default.realpathSync(manifestPath);
@@ -10536,84 +10576,26 @@ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side)
10536
10576
  /**
10537
10577
  * Every npm plugin the runtime can load for a side, built-in and project-installed.
10538
10578
  *
10539
- * Built-in package names win over a project mapping for the same id. An explicit
10540
- * project dependency on that same package selects the user's installed version.
10541
- * A project package that does not describe the requested side is skipped instead of failing, which
10542
- * is what lets a two-sided project share one dependency list between sides.
10579
+ * Built-in package names reserve their plugin ids. The side manifest selects
10580
+ * the installed versions; a package without that side is skipped.
10543
10581
  */
10544
10582
  function listNpmPlugins(side, options = {}) {
10545
- var _a, _b;
10546
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10547
- const host = readJson(path_1.default.join(packageRoot, "package.json"));
10548
- if (!host)
10549
- throw new Error(`Missing runtime package metadata: ${packageRoot}`);
10550
- const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot)
10551
- ? null
10552
- : readProjectManifest(projectRoot);
10553
- const hostMappings = mappingsOf(host);
10554
- const projectMappings = { ...mappingsOf(project) };
10555
- for (const name of Object.keys(dependenciesOf(project))) {
10556
- if (!PACKAGE_NAME.test(name) || Object.values(projectMappings).includes(name))
10557
- continue;
10558
- try {
10559
- const entry = readPackageEntry(projectRoot, name);
10560
- const id = stringValue((_a = objectValue(entry.metadata.elementspanel)) === null || _a === void 0 ? void 0 : _a.id);
10561
- if (PLUGIN_ID.test(id) && projectMappings[id] === undefined)
10562
- projectMappings[id] = name;
10563
- }
10564
- catch (_c) {
10565
- /* npm may still be writing the package. */
10566
- }
10567
- }
10568
- const mappings = new Map();
10569
- for (const [pluginId, name] of Object.entries(projectMappings)) {
10570
- if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
10571
- continue;
10572
- mappings.set(pluginId, { name, source: "project" });
10573
- }
10574
- for (const [pluginId, name] of Object.entries(hostMappings)) {
10575
- if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
10576
- continue;
10577
- if (dependenciesOf(host)[name] === undefined)
10578
- continue;
10579
- mappings.set(pluginId, {
10580
- name,
10581
- source: dependenciesOf(project)[name] === undefined ? "bundled" : "project"
10582
- });
10583
- }
10584
- const hostDependencies = dependenciesOf(host);
10585
- const projectDependencies = dependenciesOf(project);
10586
- const folders = new Set();
10583
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots({ ...options, side });
10587
10584
  const records = [];
10588
- for (const [pluginId, mapping] of mappings) {
10589
- const declared = (_b = hostDependencies[mapping.name]) !== null && _b !== void 0 ? _b : projectDependencies[mapping.name];
10590
- if (declared === undefined) {
10591
- if (options.skipInvalid)
10592
- continue;
10593
- throw new Error(`Missing npm plugin dependency: ${mapping.name}. Run npm install in the project directory.`);
10594
- }
10595
- let entry;
10596
- try {
10597
- entry = readPackageEntry(mapping.source === "project" ? projectRoot : packageRoot, mapping.name);
10598
- }
10599
- catch (error) {
10600
- if (options.skipInvalid)
10601
- continue;
10602
- throw error;
10603
- }
10604
- const record = recordFromPackage(pluginId, mapping.name, mapping.source, entry.root, entry.metadata, side);
10585
+ for (const item of npmPlugins.resolveNpmPlugins(packageRoot, side, {
10586
+ projectRoot,
10587
+ runtimeDirectory: dependencyRoot,
10588
+ skipInvalid: options.skipInvalid
10589
+ })) {
10590
+ const metadata = readJson(path_1.default.join(item.packageRoot, "package.json"));
10591
+ if (!metadata)
10592
+ continue;
10593
+ const record = recordFromPackage(item.id, item.name, item.source, item.packageRoot, metadata, side);
10605
10594
  if (!record) {
10606
- if (options.skipInvalid || mapping.source === "project")
10607
- continue;
10608
- throw new Error(`Invalid ${side !== null && side !== void 0 ? side : "plugin"} entry in npm plugin ${mapping.name}.`);
10609
- }
10610
- const folder = record.folder;
10611
- if (folders.has(folder)) {
10612
10595
  if (options.skipInvalid)
10613
10596
  continue;
10614
- throw new Error(`Duplicate npm plugin asset directory: ${folder}.`);
10597
+ throw new Error(`Invalid ${side !== null && side !== void 0 ? side : "plugin"} entry in npm plugin ${item.name}.`);
10615
10598
  }
10616
- folders.add(folder);
10617
10599
  records.push(record);
10618
10600
  }
10619
10601
  return records;
@@ -10712,10 +10694,9 @@ async function acquireProjectLock(projectRoot) {
10712
10694
  }
10713
10695
  }
10714
10696
  }
10715
- function npmPluginOperationPending() {
10716
- const { packageRoot, projectRoot } = npmPluginRoots();
10717
- return (operations.size > 0 ||
10718
- [packageRoot, projectRoot].some((root) => fs_extra_1.default.existsSync(path_1.default.join(root, LOCK_NAME))));
10697
+ function npmPluginOperationPending(side) {
10698
+ const { dependencyRoot } = npmPluginRoots({ side });
10699
+ return operations.size > 0 || fs_extra_1.default.existsSync(path_1.default.join(dependencyRoot, LOCK_NAME));
10719
10700
  }
10720
10701
  function withProjectLock(projectRoot, operation) {
10721
10702
  var _a;
@@ -10751,19 +10732,16 @@ async function installNpmPlugin(options) {
10751
10732
  throw new Error(`Invalid plugin id: ${options.pluginId}`);
10752
10733
  if (!VERSION.test(version))
10753
10734
  throw new Error(`Invalid plugin version: ${version}`);
10754
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10755
- return withProjectLock(projectRoot, async () => {
10756
- var _a, _b;
10735
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots(options);
10736
+ return withProjectLock(dependencyRoot, async () => {
10737
+ var _a, _b, _c, _d, _e;
10757
10738
  const host = readJson(path_1.default.join(packageRoot, "package.json"));
10758
10739
  if (!host)
10759
10740
  throw new Error(`Missing runtime package metadata: ${packageRoot}`);
10760
- const hostId = (_a = Object.entries(mappingsOf(host)).find(([, value]) => value === name && dependenciesOf(host)[name] !== undefined)) === null || _a === void 0 ? void 0 : _a[0];
10761
- const targetRoot = hostId && dependenciesOf(readProjectManifest(projectRoot))[name] === undefined
10762
- ? packageRoot
10763
- : projectRoot;
10741
+ const hostId = (_a = Object.entries(mappingsOf(host)).find(([, value]) => value === name)) === null || _a === void 0 ? void 0 : _a[0];
10742
+ const targetRoot = dependencyRoot;
10764
10743
  if (options.pluginId &&
10765
10744
  mappingsOf(host)[options.pluginId] &&
10766
- dependenciesOf(host)[String(mappingsOf(host)[options.pluginId])] !== undefined &&
10767
10745
  mappingsOf(host)[options.pluginId] !== name)
10768
10746
  throw new Error(`BUNDLED:${options.pluginId}`);
10769
10747
  const original = readProjectManifest(targetRoot);
@@ -10773,7 +10751,7 @@ async function installNpmPlugin(options) {
10773
10751
  try {
10774
10752
  previousVersion = readPackageEntry(targetRoot, name).metadata.version || previous;
10775
10753
  }
10776
- catch (_c) {
10754
+ catch (_f) {
10777
10755
  /* Keep the declared spec if the previous package is incomplete. */
10778
10756
  }
10779
10757
  }
@@ -10792,24 +10770,32 @@ async function installNpmPlugin(options) {
10792
10770
  "--save-exact",
10793
10771
  "--no-fund",
10794
10772
  "--no-audit",
10795
- "--ignore-scripts"
10773
+ "--ignore-scripts",
10774
+ "--workspaces=false"
10796
10775
  ], targetRoot);
10797
10776
  const entry = readPackageEntry(targetRoot, name);
10798
10777
  const pluginId = options.pluginId || stringValue((_b = objectValue(entry.metadata.elementspanel)) === null || _b === void 0 ? void 0 : _b.id);
10799
10778
  if (!PLUGIN_ID.test(pluginId))
10800
10779
  throw new Error(`Invalid plugin id in ${name}.`);
10801
- if (mappingsOf(host)[pluginId] &&
10802
- dependenciesOf(host)[String(mappingsOf(host)[pluginId])] !== undefined &&
10803
- mappingsOf(host)[pluginId] !== name)
10780
+ if (mappingsOf(host)[pluginId] && mappingsOf(host)[pluginId] !== name)
10804
10781
  throw new Error(`Plugin id ${pluginId} belongs to a bundled dependency.`);
10805
10782
  if (hostId && pluginId !== hostId)
10806
10783
  throw new Error(`The package ${name} changed its plugin id from ${hostId} to ${pluginId}.`);
10807
10784
  const mapped = mappingsOf(readProjectManifest(targetRoot))[pluginId];
10808
10785
  if (mapped !== undefined && mapped !== name)
10809
10786
  throw new Error(`Plugin id ${pluginId} is already installed as ${String(mapped)}.`);
10810
- const record = recordFromPackage(pluginId, name, targetRoot === packageRoot ? "bundled" : "project", entry.root, entry.metadata, side);
10787
+ const record = recordFromPackage(pluginId, name, hostId ? "bundled" : "project", entry.root, entry.metadata, side);
10811
10788
  if (!record)
10812
10789
  throw new Error(`The package ${name} does not provide a ${side} plugin named ${pluginId}.`);
10790
+ if (side === "panel") {
10791
+ const expected = (_d = objectValue((_c = objectValue(entry.metadata.elementspanel)) === null || _c === void 0 ? void 0 : _c.panel)) === null || _d === void 0 ? void 0 : _d.frontendAssets;
10792
+ const available = (_e = objectValue(host.elementspanel)) === null || _e === void 0 ? void 0 : _e.frontendAssets;
10793
+ if ((hostId || expected !== undefined) &&
10794
+ (!Array.isArray(expected) ||
10795
+ !Array.isArray(available) ||
10796
+ expected.some((file) => typeof file !== "string" || !available.includes(file))))
10797
+ throw new Error(`Frontend assets in ${name} do not match this panel. Update the panel or install a matching plugin version.`);
10798
+ }
10813
10799
  if (/^[0-9]/.test(version) && record.version !== version)
10814
10800
  throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
10815
10801
  if (!process.env.EPANEL_PACKAGE_ROOT &&
@@ -10853,7 +10839,8 @@ async function installNpmPlugin(options) {
10853
10839
  "--save",
10854
10840
  "--no-fund",
10855
10841
  "--no-audit",
10856
- "--ignore-scripts"
10842
+ "--ignore-scripts",
10843
+ "--workspaces=false"
10857
10844
  ]
10858
10845
  : [
10859
10846
  "--prefix",
@@ -10862,7 +10849,8 @@ async function installNpmPlugin(options) {
10862
10849
  name,
10863
10850
  "--no-fund",
10864
10851
  "--no-audit",
10865
- "--ignore-scripts"
10852
+ "--ignore-scripts",
10853
+ "--workspaces=false"
10866
10854
  ], targetRoot);
10867
10855
  }
10868
10856
  catch (rollback) {
@@ -10886,22 +10874,29 @@ async function uninstallNpmPlugin(pluginId, options = {}) {
10886
10874
  throw new Error(`Invalid plugin id: ${pluginId}`);
10887
10875
  if (isEssentialNpmPlugin(pluginId))
10888
10876
  throw new Error(`The essential plugin "${pluginId}" cannot be uninstalled.`);
10889
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10890
- return withProjectLock(projectRoot, async () => {
10877
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots(options);
10878
+ return withProjectLock(dependencyRoot, async () => {
10891
10879
  var _a, _b;
10892
- const host = readJson(path_1.default.join(packageRoot, "package.json"));
10893
- const bundledName = mappingsOf(host)[pluginId];
10894
- const targetRoot = typeof bundledName === "string" &&
10895
- dependenciesOf(host)[bundledName] !== undefined &&
10896
- dependenciesOf(readProjectManifest(projectRoot))[bundledName] === undefined
10897
- ? packageRoot
10898
- : projectRoot;
10880
+ const targetRoot = dependencyRoot;
10899
10881
  const manifest = readProjectManifest(targetRoot);
10900
- const name = (_a = mappingsOf(manifest)[pluginId]) !== null && _a !== void 0 ? _a : (_b = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId)) === null || _b === void 0 ? void 0 : _b.name;
10882
+ const name = (_a = mappingsOf(manifest)[pluginId]) !== null && _a !== void 0 ? _a : (_b = listNpmPlugins(options.side, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId)) === null || _b === void 0 ? void 0 : _b.name;
10901
10883
  if (typeof name !== "string" || !PACKAGE_NAME.test(name))
10902
10884
  return null;
10903
- const record = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId);
10904
- await runNpm(["--prefix", targetRoot, "uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], targetRoot);
10885
+ const record = listNpmPlugins(options.side, {
10886
+ packageRoot,
10887
+ projectRoot,
10888
+ skipInvalid: true
10889
+ }).find((item) => item.pluginId === pluginId);
10890
+ await runNpm([
10891
+ "--prefix",
10892
+ targetRoot,
10893
+ "uninstall",
10894
+ name,
10895
+ "--no-fund",
10896
+ "--no-audit",
10897
+ "--ignore-scripts",
10898
+ "--workspaces=false"
10899
+ ], targetRoot);
10905
10900
  const updated = readProjectManifest(targetRoot);
10906
10901
  const plugins = { ...mappingsOf(updated) };
10907
10902
  delete plugins[pluginId];
@@ -11075,11 +11070,11 @@ const plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
11075
11070
  /** Poll package boundaries, allowing npm to finish replacing files before reconciliation. */
11076
11071
  function watchNpmPlugins(ctx, side, reload, changed) {
11077
11072
  const stamp = () => {
11078
- const { packageRoot, projectRoot } = (0, plugin_npm_1.npmPluginRoots)();
11073
+ const { packageRoot, dependencyRoot } = (0, plugin_npm_1.npmPluginRoots)({ side });
11079
11074
  if (!node_fs_1.default.existsSync(node_path_1.default.join(packageRoot, "package.json")))
11080
11075
  return "";
11081
11076
  const files = [];
11082
- for (const root of [packageRoot, projectRoot])
11077
+ for (const root of [packageRoot, dependencyRoot])
11083
11078
  for (const name of [
11084
11079
  "package.json",
11085
11080
  "package-lock.json",
@@ -11103,7 +11098,7 @@ function watchNpmPlugins(ctx, side, reload, changed) {
11103
11098
  let candidate = applied;
11104
11099
  let busy = false;
11105
11100
  ctx.setInterval(() => {
11106
- if (busy || (0, plugin_npm_1.npmPluginOperationPending)())
11101
+ if (busy || (0, plugin_npm_1.npmPluginOperationPending)(side))
11107
11102
  return;
11108
11103
  try {
11109
11104
  const next = stamp();
@@ -11134,6 +11129,51 @@ function watchNpmPlugins(ctx, side, reload, changed) {
11134
11129
  }
11135
11130
 
11136
11131
 
11132
+ /***/ },
11133
+
11134
+ /***/ "../common/src/process_restart.ts"
11135
+ (__unused_webpack_module, exports) {
11136
+
11137
+ "use strict";
11138
+
11139
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
11140
+ exports.isProcessRestartPending = isProcessRestartPending;
11141
+ exports.requestProcessRestart = requestProcessRestart;
11142
+ const RESTART = Symbol.for("elementspanel.process.restart");
11143
+ function isProcessRestartPending(ctx) {
11144
+ var _a;
11145
+ return ((_a = ctx.root[RESTART]) === null || _a === void 0 ? void 0 : _a.pending) === true;
11146
+ }
11147
+ /** The CLI retains instance processes and starts a fresh worker after this one exits. */
11148
+ function requestProcessRestart(ctx, settle, ready = async () => { }) {
11149
+ if (isProcessRestartPending(ctx))
11150
+ return true;
11151
+ if (process.env.EPANEL_SUPERVISED !== "1" || !process.connected || !process.send)
11152
+ return false;
11153
+ const state = { pending: true };
11154
+ ctx.root[RESTART] = state;
11155
+ const retainProcesses = () => new Promise((resolve, reject) => process.send({ type: "elementspanel:prepare-restart" }, (error) => error ? reject(error) : resolve()));
11156
+ setImmediate(() => {
11157
+ void (async () => {
11158
+ await ready();
11159
+ // Detach output consumers before cleanup so output and exits during the
11160
+ // restart are retained for the next daemon generation.
11161
+ await retainProcesses();
11162
+ await ctx.parallel("process/restarting");
11163
+ // A pending start can finish while restart hooks are awaiting it.
11164
+ await retainProcesses();
11165
+ await ctx.stop();
11166
+ await settle();
11167
+ process.send({ type: "elementspanel:restart" }, (error) => process.exit(error ? 1 : 0));
11168
+ })().catch((error) => {
11169
+ ctx.logger.error("Failed to restart the process:", error);
11170
+ process.exit(1);
11171
+ });
11172
+ });
11173
+ return true;
11174
+ }
11175
+
11176
+
11137
11177
  /***/ },
11138
11178
 
11139
11179
  /***/ "../common/src/process_tools.ts"
@@ -12081,6 +12121,226 @@ module.exports = require("util");
12081
12121
  "use strict";
12082
12122
  module.exports = require("zlib");
12083
12123
 
12124
+ /***/ },
12125
+
12126
+ /***/ "../../scripts/runtime/npm-plugins.cjs"
12127
+ (module, __unused_webpack_exports, __webpack_require__) {
12128
+
12129
+ const fs = __webpack_require__("node:fs");
12130
+ const path = __webpack_require__("node:path");
12131
+ const { createRequire } = __webpack_require__("node:module");
12132
+
12133
+ const readJson = (filename) => JSON.parse(fs.readFileSync(filename, "utf8"));
12134
+ const readOptional = (filename) => {
12135
+ try {
12136
+ return readJson(filename);
12137
+ } catch (error) {
12138
+ if (error.code === "ENOENT") return null;
12139
+ throw error;
12140
+ }
12141
+ };
12142
+ const validId = /^[a-z][a-z0-9_-]{1,63}$/;
12143
+ const validName = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
12144
+
12145
+ function packageManifest(root, name) {
12146
+ const localRequire = createRequire(path.join(root, "package.json"));
12147
+ const filename = (localRequire.resolve.paths(name) || [])
12148
+ .map((directory) => path.join(directory, name, "package.json"))
12149
+ .find((candidate) => fs.existsSync(candidate));
12150
+ if (!filename) throw new Error(`Missing npm package: ${name}`);
12151
+ return fs.realpathSync(filename);
12152
+ }
12153
+
12154
+ function within(root, filename) {
12155
+ const relative = path.relative(root, filename);
12156
+ return (
12157
+ relative === "" ||
12158
+ (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
12159
+ );
12160
+ }
12161
+
12162
+ function projectRoot(packageRoot) {
12163
+ if (process.env.EPANEL_PROJECT_ROOT) return path.resolve(process.env.EPANEL_PROJECT_ROOT);
12164
+ const parent = path.resolve(packageRoot, "..");
12165
+ if (path.basename(parent) === "core") return path.dirname(parent);
12166
+ // An installed runtime sits at <project>/node_modules/@scope/<side>; the
12167
+ // project itself is three levels above the package. A development checkout
12168
+ // sits below core/ and is handled above.
12169
+ const scope = parent;
12170
+ const nodeModules = path.dirname(scope);
12171
+ if (path.basename(nodeModules) === "node_modules" && path.basename(scope).startsWith("@")) {
12172
+ return path.dirname(nodeModules);
12173
+ }
12174
+ return parent;
12175
+ }
12176
+
12177
+ function projectManifestFile(packageRoot, side, options = {}) {
12178
+ const selectedSide =
12179
+ side ||
12180
+ readJson(path.join(packageRoot, "package.json")).elementspanel?.side ||
12181
+ path.basename(packageRoot);
12182
+ if (!["panel", "daemon"].includes(selectedSide)) throw new Error("Missing runtime side.");
12183
+ const directory =
12184
+ options.runtimeDirectory ||
12185
+ (!options.projectRoot && process.env.EPANEL_RUNTIME_ROOT) ||
12186
+ path.join(options.projectRoot || projectRoot(packageRoot), selectedSide);
12187
+ return path.join(path.resolve(directory), "package.json");
12188
+ }
12189
+
12190
+ function mappingOf(manifest) {
12191
+ const plugins = manifest?.elementspanel?.plugins;
12192
+ return plugins && typeof plugins === "object" && !Array.isArray(plugins) ? plugins : {};
12193
+ }
12194
+
12195
+ function dependenciesOf(manifest) {
12196
+ const dependencies = manifest?.dependencies;
12197
+ return dependencies && typeof dependencies === "object" ? dependencies : {};
12198
+ }
12199
+
12200
+ /**
12201
+ * Resolve plugins declared in the side's package.json, including hoisted npm
12202
+ * installations. The host catalogue reserves built-in ids without owning their
12203
+ * versions. Other explicitly mapped plugins remain dormant until enabled.
12204
+ * Runtime startup passes skipInvalid so an optional package being removed does
12205
+ * not stop the host; the caller separately validates the small set of core
12206
+ * plugins.
12207
+ */
12208
+ function resolveNpmPlugins(packageRoot, side, options = {}) {
12209
+ const skipInvalid = options.skipInvalid === true;
12210
+ const host = readJson(path.join(packageRoot, "package.json"));
12211
+ const selectedSide = side || host.elementspanel?.side || path.basename(packageRoot);
12212
+ const projectFile = projectManifestFile(packageRoot, selectedSide, options);
12213
+ const project = readOptional(projectFile);
12214
+ const dependencyRoot = path.dirname(projectFile);
12215
+
12216
+ // The side manifest owns every dependency; the host only reserves identities.
12217
+ const configured = new Map();
12218
+ for (const [id, name] of Object.entries(mappingOf(project)))
12219
+ configured.set(id, { name, source: "project" });
12220
+ for (const name of Object.keys(dependenciesOf(project))) {
12221
+ if (!validName.test(name) || [...configured.values()].some((item) => item.name === name))
12222
+ continue;
12223
+ try {
12224
+ const metadata = readJson(packageManifest(dependencyRoot, name));
12225
+ const id = metadata.elementspanel?.id;
12226
+ if (validId.test(id) && !configured.has(id))
12227
+ configured.set(id, { name, source: "project", automatic: true });
12228
+ } catch {
12229
+ /* Ignore incomplete npm installations until the next scan. */
12230
+ }
12231
+ }
12232
+ for (const [id, name] of Object.entries(mappingOf(host))) {
12233
+ if (dependenciesOf(project)[name] !== undefined)
12234
+ configured.set(id, {
12235
+ name,
12236
+ source: "bundled",
12237
+ automatic: true
12238
+ });
12239
+ else if (configured.has(id)) {
12240
+ if (!skipInvalid) throw new Error(`Plugin id ${id} belongs to built-in package ${name}.`);
12241
+ configured.delete(id);
12242
+ }
12243
+ }
12244
+
12245
+ const projectDependencies = dependenciesOf(project);
12246
+ const folders = new Set();
12247
+ const records = [];
12248
+
12249
+ for (const [id, mapping] of configured) {
12250
+ const name = mapping.name;
12251
+ if (
12252
+ !validId.test(id) ||
12253
+ typeof name !== "string" ||
12254
+ !validName.test(name) ||
12255
+ projectDependencies[name] === undefined
12256
+ ) {
12257
+ if (skipInvalid) continue;
12258
+ throw new Error(`Invalid npm plugin dependency: ${id}.`);
12259
+ }
12260
+
12261
+ let manifestPath;
12262
+ let failure;
12263
+ try {
12264
+ manifestPath = packageManifest(dependencyRoot, name);
12265
+ } catch (error) {
12266
+ failure = error;
12267
+ }
12268
+ if (!manifestPath) {
12269
+ if (skipInvalid) continue;
12270
+ throw new Error(`Missing npm plugin ${name}. Run npm install in ${dependencyRoot}.`, {
12271
+ cause: failure
12272
+ });
12273
+ }
12274
+ let metadata;
12275
+ try {
12276
+ metadata = readJson(manifestPath);
12277
+ } catch (error) {
12278
+ if (skipInvalid) continue;
12279
+ throw error;
12280
+ }
12281
+ const definition = metadata.elementspanel?.[selectedSide];
12282
+ // A project dependency may only ship one side; the other side simply does
12283
+ // not see it. A built-in plugin declared by the runtime must carry this side.
12284
+ if (!definition) {
12285
+ if (mapping.source === "project" || skipInvalid) continue;
12286
+ throw new Error(`Invalid ${selectedSide} entry in npm plugin ${name}.`);
12287
+ }
12288
+ const pluginRoot = path.dirname(manifestPath);
12289
+ if (
12290
+ typeof definition.directory !== "string" ||
12291
+ typeof definition.folder !== "string" ||
12292
+ !validId.test(definition.folder) ||
12293
+ path.isAbsolute(definition.directory)
12294
+ ) {
12295
+ if (skipInvalid) continue;
12296
+ throw new Error(`Npm plugin entry escapes its package: ${name}.`);
12297
+ }
12298
+ const directory = path.resolve(pluginRoot, definition.directory);
12299
+ if (directory === pluginRoot || !within(pluginRoot, directory)) {
12300
+ if (skipInvalid) continue;
12301
+ throw new Error(`Npm plugin entry escapes its package: ${name}.`);
12302
+ }
12303
+ let entries;
12304
+ try {
12305
+ entries = readJson(path.join(directory, "plugin.json"));
12306
+ } catch (error) {
12307
+ if (skipInvalid) continue;
12308
+ throw error;
12309
+ }
12310
+ if (
12311
+ metadata.elementspanel?.id !== id ||
12312
+ typeof metadata.version !== "string" ||
12313
+ !metadata.version.trim() ||
12314
+ !entries ||
12315
+ typeof entries !== "object" ||
12316
+ Array.isArray(entries)
12317
+ ) {
12318
+ if (skipInvalid) continue;
12319
+ throw new Error(`Invalid npm plugin identity or version in package.json: ${name}.`);
12320
+ }
12321
+ if (folders.has(definition.folder)) {
12322
+ if (skipInvalid) continue;
12323
+ throw new Error(`Duplicate npm plugin asset directory: ${definition.folder}.`);
12324
+ }
12325
+ folders.add(definition.folder);
12326
+ records.push({
12327
+ id,
12328
+ name,
12329
+ version: metadata.version,
12330
+ source: mapping.source,
12331
+ defaultEnabled: mapping.automatic === true || mapping.source !== "project",
12332
+ packageRoot: pluginRoot,
12333
+ directory,
12334
+ folder: definition.folder,
12335
+ dependencies: Object.keys(metadata.dependencies || {})
12336
+ });
12337
+ }
12338
+ return records;
12339
+ }
12340
+
12341
+ module.exports = { resolveNpmPlugins, projectManifestFile };
12342
+
12343
+
12084
12344
  /***/ },
12085
12345
 
12086
12346
  /***/ "../../scripts/workspace-layout.cjs"