@elementspanel/epanel-plugin-instance 1.0.6 → 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.
@@ -9668,6 +9668,39 @@ async function loadPluginModule(entry, directory, revision) {
9668
9668
 
9669
9669
  "use strict";
9670
9670
 
9671
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9672
+ if (k2 === undefined) k2 = k;
9673
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9674
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9675
+ desc = { enumerable: true, get: function() { return m[k]; } };
9676
+ }
9677
+ Object.defineProperty(o, k2, desc);
9678
+ }) : (function(o, m, k, k2) {
9679
+ if (k2 === undefined) k2 = k;
9680
+ o[k2] = m[k];
9681
+ }));
9682
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
9683
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
9684
+ }) : function(o, v) {
9685
+ o["default"] = v;
9686
+ });
9687
+ var __importStar = (this && this.__importStar) || (function () {
9688
+ var ownKeys = function(o) {
9689
+ ownKeys = Object.getOwnPropertyNames || function (o) {
9690
+ var ar = [];
9691
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
9692
+ return ar;
9693
+ };
9694
+ return ownKeys(o);
9695
+ };
9696
+ return function (mod) {
9697
+ if (mod && mod.__esModule) return mod;
9698
+ var result = {};
9699
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
9700
+ __setModuleDefault(result, mod);
9701
+ return result;
9702
+ };
9703
+ })();
9671
9704
  var __importDefault = (this && this.__importDefault) || function (mod) {
9672
9705
  return (mod && mod.__esModule) ? mod : { "default": mod };
9673
9706
  };
@@ -9686,6 +9719,8 @@ const path_1 = __importDefault(__webpack_require__("path"));
9686
9719
  const module_1 = __webpack_require__("module");
9687
9720
  const plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
9688
9721
  const plugin_contract_1 = __webpack_require__("../common/src/plugin_contract.ts");
9722
+ // @ts-ignore Shared by the source runtime and the published npm launcher.
9723
+ const npmPlugins = __importStar(__webpack_require__("../../scripts/runtime/npm-plugins.cjs"));
9689
9724
  const PLUGIN_ID = /^[a-z][a-z0-9_-]{1,63}$/;
9690
9725
  const PACKAGE_NAME = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
9691
9726
  const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$/;
@@ -9728,9 +9763,18 @@ function currentProjectRoot(packageRoot = currentPackageRoot()) {
9728
9763
  }
9729
9764
  function npmPluginRoots(options = {}) {
9730
9765
  const packageRoot = path_1.default.resolve(options.packageRoot ?? currentPackageRoot());
9766
+ const projectRoot = path_1.default.resolve(options.projectRoot ?? currentProjectRoot(packageRoot));
9767
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
9768
+ const side = options.side ||
9769
+ stringValue(objectValue(host?.elementspanel)?.side) ||
9770
+ path_1.default.basename(packageRoot);
9731
9771
  return {
9732
9772
  packageRoot,
9733
- projectRoot: path_1.default.resolve(options.projectRoot ?? currentProjectRoot(packageRoot))
9773
+ projectRoot,
9774
+ dependencyRoot: path_1.default.dirname(npmPlugins.projectManifestFile(packageRoot, side, {
9775
+ projectRoot,
9776
+ runtimeDirectory: process.env.EPANEL_RUNTIME_ROOT
9777
+ }))
9734
9778
  };
9735
9779
  }
9736
9780
  function projectManifestFile(projectRoot) {
@@ -9758,15 +9802,10 @@ function pluginRecord(pluginId, name, source, packageRoot, directory, folder, ve
9758
9802
  function readPackageEntry(packageRoot, name) {
9759
9803
  // Read package boundaries directly: Node caches require.resolve() paths even
9760
9804
  // after npm moves an installation between nested and hoisted node_modules.
9761
- let manifestPath;
9762
- for (const root of [packageRoot, currentProjectRoot(packageRoot)]) {
9763
- const localRequire = (0, module_1.createRequire)(path_1.default.join(root, "package.json"));
9764
- manifestPath = (localRequire.resolve.paths(name) || [])
9765
- .map((directory) => path_1.default.join(directory, name, "package.json"))
9766
- .find((filename) => fs_extra_1.default.existsSync(filename));
9767
- if (manifestPath)
9768
- break;
9769
- }
9805
+ const localRequire = (0, module_1.createRequire)(path_1.default.join(packageRoot, "package.json"));
9806
+ let manifestPath = (localRequire.resolve.paths(name) || [])
9807
+ .map((directory) => path_1.default.join(directory, name, "package.json"))
9808
+ .find((filename) => fs_extra_1.default.existsSync(filename));
9770
9809
  if (!manifestPath)
9771
9810
  throw new Error(`Missing npm plugin package: ${name}.`);
9772
9811
  manifestPath = fs_extra_1.default.realpathSync(manifestPath);
@@ -9813,83 +9852,26 @@ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side)
9813
9852
  /**
9814
9853
  * Every npm plugin the runtime can load for a side, built-in and project-installed.
9815
9854
  *
9816
- * Built-in package names win over a project mapping for the same id. An explicit
9817
- * project dependency on that same package selects the user's installed version.
9818
- * A project package that does not describe the requested side is skipped instead of failing, which
9819
- * is what lets a two-sided project share one dependency list between sides.
9855
+ * Built-in package names reserve their plugin ids. The side manifest selects
9856
+ * the installed versions; a package without that side is skipped.
9820
9857
  */
9821
9858
  function listNpmPlugins(side, options = {}) {
9822
- const { packageRoot, projectRoot } = npmPluginRoots(options);
9823
- const host = readJson(path_1.default.join(packageRoot, "package.json"));
9824
- if (!host)
9825
- throw new Error(`Missing runtime package metadata: ${packageRoot}`);
9826
- const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot)
9827
- ? null
9828
- : readProjectManifest(projectRoot);
9829
- const hostMappings = mappingsOf(host);
9830
- const projectMappings = { ...mappingsOf(project) };
9831
- for (const name of Object.keys(dependenciesOf(project))) {
9832
- if (!PACKAGE_NAME.test(name) || Object.values(projectMappings).includes(name))
9833
- continue;
9834
- try {
9835
- const entry = readPackageEntry(projectRoot, name);
9836
- const id = stringValue(objectValue(entry.metadata.elementspanel)?.id);
9837
- if (PLUGIN_ID.test(id) && projectMappings[id] === undefined)
9838
- projectMappings[id] = name;
9839
- }
9840
- catch {
9841
- /* npm may still be writing the package. */
9842
- }
9843
- }
9844
- const mappings = new Map();
9845
- for (const [pluginId, name] of Object.entries(projectMappings)) {
9846
- if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
9847
- continue;
9848
- mappings.set(pluginId, { name, source: "project" });
9849
- }
9850
- for (const [pluginId, name] of Object.entries(hostMappings)) {
9851
- if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
9852
- continue;
9853
- if (dependenciesOf(host)[name] === undefined)
9854
- continue;
9855
- mappings.set(pluginId, {
9856
- name,
9857
- source: dependenciesOf(project)[name] === undefined ? "bundled" : "project"
9858
- });
9859
- }
9860
- const hostDependencies = dependenciesOf(host);
9861
- const projectDependencies = dependenciesOf(project);
9862
- const folders = new Set();
9859
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots({ ...options, side });
9863
9860
  const records = [];
9864
- for (const [pluginId, mapping] of mappings) {
9865
- const declared = hostDependencies[mapping.name] ?? projectDependencies[mapping.name];
9866
- if (declared === undefined) {
9867
- if (options.skipInvalid)
9868
- continue;
9869
- throw new Error(`Missing npm plugin dependency: ${mapping.name}. Run npm install in the project directory.`);
9870
- }
9871
- let entry;
9872
- try {
9873
- entry = readPackageEntry(mapping.source === "project" ? projectRoot : packageRoot, mapping.name);
9874
- }
9875
- catch (error) {
9876
- if (options.skipInvalid)
9877
- continue;
9878
- throw error;
9879
- }
9880
- const record = recordFromPackage(pluginId, mapping.name, mapping.source, entry.root, entry.metadata, side);
9861
+ for (const item of npmPlugins.resolveNpmPlugins(packageRoot, side, {
9862
+ projectRoot,
9863
+ runtimeDirectory: dependencyRoot,
9864
+ skipInvalid: options.skipInvalid
9865
+ })) {
9866
+ const metadata = readJson(path_1.default.join(item.packageRoot, "package.json"));
9867
+ if (!metadata)
9868
+ continue;
9869
+ const record = recordFromPackage(item.id, item.name, item.source, item.packageRoot, metadata, side);
9881
9870
  if (!record) {
9882
- if (options.skipInvalid || mapping.source === "project")
9883
- continue;
9884
- throw new Error(`Invalid ${side ?? "plugin"} entry in npm plugin ${mapping.name}.`);
9885
- }
9886
- const folder = record.folder;
9887
- if (folders.has(folder)) {
9888
9871
  if (options.skipInvalid)
9889
9872
  continue;
9890
- throw new Error(`Duplicate npm plugin asset directory: ${folder}.`);
9873
+ throw new Error(`Invalid ${side ?? "plugin"} entry in npm plugin ${item.name}.`);
9891
9874
  }
9892
- folders.add(folder);
9893
9875
  records.push(record);
9894
9876
  }
9895
9877
  return records;
@@ -9986,10 +9968,9 @@ async function acquireProjectLock(projectRoot) {
9986
9968
  }
9987
9969
  }
9988
9970
  }
9989
- function npmPluginOperationPending() {
9990
- const { packageRoot, projectRoot } = npmPluginRoots();
9991
- return (operations.size > 0 ||
9992
- [packageRoot, projectRoot].some((root) => fs_extra_1.default.existsSync(path_1.default.join(root, LOCK_NAME))));
9971
+ function npmPluginOperationPending(side) {
9972
+ const { dependencyRoot } = npmPluginRoots({ side });
9973
+ return operations.size > 0 || fs_extra_1.default.existsSync(path_1.default.join(dependencyRoot, LOCK_NAME));
9993
9974
  }
9994
9975
  function withProjectLock(projectRoot, operation) {
9995
9976
  const key = process.platform === "win32" ? projectRoot.toLowerCase() : projectRoot;
@@ -10024,18 +10005,15 @@ async function installNpmPlugin(options) {
10024
10005
  throw new Error(`Invalid plugin id: ${options.pluginId}`);
10025
10006
  if (!VERSION.test(version))
10026
10007
  throw new Error(`Invalid plugin version: ${version}`);
10027
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10028
- return withProjectLock(projectRoot, async () => {
10008
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots(options);
10009
+ return withProjectLock(dependencyRoot, async () => {
10029
10010
  const host = readJson(path_1.default.join(packageRoot, "package.json"));
10030
10011
  if (!host)
10031
10012
  throw new Error(`Missing runtime package metadata: ${packageRoot}`);
10032
- const hostId = Object.entries(mappingsOf(host)).find(([, value]) => value === name && dependenciesOf(host)[name] !== undefined)?.[0];
10033
- const targetRoot = hostId && dependenciesOf(readProjectManifest(projectRoot))[name] === undefined
10034
- ? packageRoot
10035
- : projectRoot;
10013
+ const hostId = Object.entries(mappingsOf(host)).find(([, value]) => value === name)?.[0];
10014
+ const targetRoot = dependencyRoot;
10036
10015
  if (options.pluginId &&
10037
10016
  mappingsOf(host)[options.pluginId] &&
10038
- dependenciesOf(host)[String(mappingsOf(host)[options.pluginId])] !== undefined &&
10039
10017
  mappingsOf(host)[options.pluginId] !== name)
10040
10018
  throw new Error(`BUNDLED:${options.pluginId}`);
10041
10019
  const original = readProjectManifest(targetRoot);
@@ -10064,24 +10042,33 @@ async function installNpmPlugin(options) {
10064
10042
  "--save-exact",
10065
10043
  "--no-fund",
10066
10044
  "--no-audit",
10067
- "--ignore-scripts"
10045
+ "--ignore-scripts",
10046
+ "--workspaces=false"
10068
10047
  ], targetRoot);
10069
10048
  const entry = readPackageEntry(targetRoot, name);
10070
10049
  const pluginId = options.pluginId || stringValue(objectValue(entry.metadata.elementspanel)?.id);
10071
10050
  if (!PLUGIN_ID.test(pluginId))
10072
10051
  throw new Error(`Invalid plugin id in ${name}.`);
10073
- if (mappingsOf(host)[pluginId] &&
10074
- dependenciesOf(host)[String(mappingsOf(host)[pluginId])] !== undefined &&
10075
- mappingsOf(host)[pluginId] !== name)
10052
+ if (mappingsOf(host)[pluginId] && mappingsOf(host)[pluginId] !== name)
10076
10053
  throw new Error(`Plugin id ${pluginId} belongs to a bundled dependency.`);
10077
10054
  if (hostId && pluginId !== hostId)
10078
10055
  throw new Error(`The package ${name} changed its plugin id from ${hostId} to ${pluginId}.`);
10079
10056
  const mapped = mappingsOf(readProjectManifest(targetRoot))[pluginId];
10080
10057
  if (mapped !== undefined && mapped !== name)
10081
10058
  throw new Error(`Plugin id ${pluginId} is already installed as ${String(mapped)}.`);
10082
- const record = recordFromPackage(pluginId, name, targetRoot === packageRoot ? "bundled" : "project", entry.root, entry.metadata, side);
10059
+ const record = recordFromPackage(pluginId, name, hostId ? "bundled" : "project", entry.root, entry.metadata, side);
10083
10060
  if (!record)
10084
10061
  throw new Error(`The package ${name} does not provide a ${side} plugin named ${pluginId}.`);
10062
+ if (side === "panel") {
10063
+ const expected = objectValue(objectValue(entry.metadata.elementspanel)?.panel)
10064
+ ?.frontendAssets;
10065
+ const available = objectValue(host.elementspanel)?.frontendAssets;
10066
+ if ((hostId || expected !== undefined) &&
10067
+ (!Array.isArray(expected) ||
10068
+ !Array.isArray(available) ||
10069
+ expected.some((file) => typeof file !== "string" || !available.includes(file))))
10070
+ throw new Error(`Frontend assets in ${name} do not match this panel. Update the panel or install a matching plugin version.`);
10071
+ }
10085
10072
  if (/^[0-9]/.test(version) && record.version !== version)
10086
10073
  throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
10087
10074
  if (!process.env.EPANEL_PACKAGE_ROOT &&
@@ -10125,7 +10112,8 @@ async function installNpmPlugin(options) {
10125
10112
  "--save",
10126
10113
  "--no-fund",
10127
10114
  "--no-audit",
10128
- "--ignore-scripts"
10115
+ "--ignore-scripts",
10116
+ "--workspaces=false"
10129
10117
  ]
10130
10118
  : [
10131
10119
  "--prefix",
@@ -10134,7 +10122,8 @@ async function installNpmPlugin(options) {
10134
10122
  name,
10135
10123
  "--no-fund",
10136
10124
  "--no-audit",
10137
- "--ignore-scripts"
10125
+ "--ignore-scripts",
10126
+ "--workspaces=false"
10138
10127
  ], targetRoot);
10139
10128
  }
10140
10129
  catch (rollback) {
@@ -10158,22 +10147,29 @@ async function uninstallNpmPlugin(pluginId, options = {}) {
10158
10147
  throw new Error(`Invalid plugin id: ${pluginId}`);
10159
10148
  if (isEssentialNpmPlugin(pluginId))
10160
10149
  throw new Error(`The essential plugin "${pluginId}" cannot be uninstalled.`);
10161
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10162
- return withProjectLock(projectRoot, async () => {
10163
- const host = readJson(path_1.default.join(packageRoot, "package.json"));
10164
- const bundledName = mappingsOf(host)[pluginId];
10165
- const targetRoot = typeof bundledName === "string" &&
10166
- dependenciesOf(host)[bundledName] !== undefined &&
10167
- dependenciesOf(readProjectManifest(projectRoot))[bundledName] === undefined
10168
- ? packageRoot
10169
- : projectRoot;
10150
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots(options);
10151
+ return withProjectLock(dependencyRoot, async () => {
10152
+ const targetRoot = dependencyRoot;
10170
10153
  const manifest = readProjectManifest(targetRoot);
10171
10154
  const name = mappingsOf(manifest)[pluginId] ??
10172
- listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId)?.name;
10155
+ listNpmPlugins(options.side, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId)?.name;
10173
10156
  if (typeof name !== "string" || !PACKAGE_NAME.test(name))
10174
10157
  return null;
10175
- const record = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId);
10176
- await runNpm(["--prefix", targetRoot, "uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], targetRoot);
10158
+ const record = listNpmPlugins(options.side, {
10159
+ packageRoot,
10160
+ projectRoot,
10161
+ skipInvalid: true
10162
+ }).find((item) => item.pluginId === pluginId);
10163
+ await runNpm([
10164
+ "--prefix",
10165
+ targetRoot,
10166
+ "uninstall",
10167
+ name,
10168
+ "--no-fund",
10169
+ "--no-audit",
10170
+ "--ignore-scripts",
10171
+ "--workspaces=false"
10172
+ ], targetRoot);
10177
10173
  const updated = readProjectManifest(targetRoot);
10178
10174
  const plugins = { ...mappingsOf(updated) };
10179
10175
  delete plugins[pluginId];
@@ -10347,11 +10343,11 @@ const plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
10347
10343
  /** Poll package boundaries, allowing npm to finish replacing files before reconciliation. */
10348
10344
  function watchNpmPlugins(ctx, side, reload, changed) {
10349
10345
  const stamp = () => {
10350
- const { packageRoot, projectRoot } = (0, plugin_npm_1.npmPluginRoots)();
10346
+ const { packageRoot, dependencyRoot } = (0, plugin_npm_1.npmPluginRoots)({ side });
10351
10347
  if (!node_fs_1.default.existsSync(node_path_1.default.join(packageRoot, "package.json")))
10352
10348
  return "";
10353
10349
  const files = [];
10354
- for (const root of [packageRoot, projectRoot])
10350
+ for (const root of [packageRoot, dependencyRoot])
10355
10351
  for (const name of [
10356
10352
  "package.json",
10357
10353
  "package-lock.json",
@@ -10375,7 +10371,7 @@ function watchNpmPlugins(ctx, side, reload, changed) {
10375
10371
  let candidate = applied;
10376
10372
  let busy = false;
10377
10373
  ctx.setInterval(() => {
10378
- if (busy || (0, plugin_npm_1.npmPluginOperationPending)())
10374
+ if (busy || (0, plugin_npm_1.npmPluginOperationPending)(side))
10379
10375
  return;
10380
10376
  try {
10381
10377
  const next = stamp();
@@ -11460,6 +11456,226 @@ module.exports = require("stream/promises");
11460
11456
  "use strict";
11461
11457
  module.exports = require("util");
11462
11458
 
11459
+ /***/ },
11460
+
11461
+ /***/ "../../scripts/runtime/npm-plugins.cjs"
11462
+ (module, __unused_webpack_exports, __webpack_require__) {
11463
+
11464
+ const fs = __webpack_require__("node:fs");
11465
+ const path = __webpack_require__("node:path");
11466
+ const { createRequire } = __webpack_require__("node:module");
11467
+
11468
+ const readJson = (filename) => JSON.parse(fs.readFileSync(filename, "utf8"));
11469
+ const readOptional = (filename) => {
11470
+ try {
11471
+ return readJson(filename);
11472
+ } catch (error) {
11473
+ if (error.code === "ENOENT") return null;
11474
+ throw error;
11475
+ }
11476
+ };
11477
+ const validId = /^[a-z][a-z0-9_-]{1,63}$/;
11478
+ const validName = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
11479
+
11480
+ function packageManifest(root, name) {
11481
+ const localRequire = createRequire(path.join(root, "package.json"));
11482
+ const filename = (localRequire.resolve.paths(name) || [])
11483
+ .map((directory) => path.join(directory, name, "package.json"))
11484
+ .find((candidate) => fs.existsSync(candidate));
11485
+ if (!filename) throw new Error(`Missing npm package: ${name}`);
11486
+ return fs.realpathSync(filename);
11487
+ }
11488
+
11489
+ function within(root, filename) {
11490
+ const relative = path.relative(root, filename);
11491
+ return (
11492
+ relative === "" ||
11493
+ (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
11494
+ );
11495
+ }
11496
+
11497
+ function projectRoot(packageRoot) {
11498
+ if (process.env.EPANEL_PROJECT_ROOT) return path.resolve(process.env.EPANEL_PROJECT_ROOT);
11499
+ const parent = path.resolve(packageRoot, "..");
11500
+ if (path.basename(parent) === "core") return path.dirname(parent);
11501
+ // An installed runtime sits at <project>/node_modules/@scope/<side>; the
11502
+ // project itself is three levels above the package. A development checkout
11503
+ // sits below core/ and is handled above.
11504
+ const scope = parent;
11505
+ const nodeModules = path.dirname(scope);
11506
+ if (path.basename(nodeModules) === "node_modules" && path.basename(scope).startsWith("@")) {
11507
+ return path.dirname(nodeModules);
11508
+ }
11509
+ return parent;
11510
+ }
11511
+
11512
+ function projectManifestFile(packageRoot, side, options = {}) {
11513
+ const selectedSide =
11514
+ side ||
11515
+ readJson(path.join(packageRoot, "package.json")).elementspanel?.side ||
11516
+ path.basename(packageRoot);
11517
+ if (!["panel", "daemon"].includes(selectedSide)) throw new Error("Missing runtime side.");
11518
+ const directory =
11519
+ options.runtimeDirectory ||
11520
+ (!options.projectRoot && process.env.EPANEL_RUNTIME_ROOT) ||
11521
+ path.join(options.projectRoot || projectRoot(packageRoot), selectedSide);
11522
+ return path.join(path.resolve(directory), "package.json");
11523
+ }
11524
+
11525
+ function mappingOf(manifest) {
11526
+ const plugins = manifest?.elementspanel?.plugins;
11527
+ return plugins && typeof plugins === "object" && !Array.isArray(plugins) ? plugins : {};
11528
+ }
11529
+
11530
+ function dependenciesOf(manifest) {
11531
+ const dependencies = manifest?.dependencies;
11532
+ return dependencies && typeof dependencies === "object" ? dependencies : {};
11533
+ }
11534
+
11535
+ /**
11536
+ * Resolve plugins declared in the side's package.json, including hoisted npm
11537
+ * installations. The host catalogue reserves built-in ids without owning their
11538
+ * versions. Other explicitly mapped plugins remain dormant until enabled.
11539
+ * Runtime startup passes skipInvalid so an optional package being removed does
11540
+ * not stop the host; the caller separately validates the small set of core
11541
+ * plugins.
11542
+ */
11543
+ function resolveNpmPlugins(packageRoot, side, options = {}) {
11544
+ const skipInvalid = options.skipInvalid === true;
11545
+ const host = readJson(path.join(packageRoot, "package.json"));
11546
+ const selectedSide = side || host.elementspanel?.side || path.basename(packageRoot);
11547
+ const projectFile = projectManifestFile(packageRoot, selectedSide, options);
11548
+ const project = readOptional(projectFile);
11549
+ const dependencyRoot = path.dirname(projectFile);
11550
+
11551
+ // The side manifest owns every dependency; the host only reserves identities.
11552
+ const configured = new Map();
11553
+ for (const [id, name] of Object.entries(mappingOf(project)))
11554
+ configured.set(id, { name, source: "project" });
11555
+ for (const name of Object.keys(dependenciesOf(project))) {
11556
+ if (!validName.test(name) || [...configured.values()].some((item) => item.name === name))
11557
+ continue;
11558
+ try {
11559
+ const metadata = readJson(packageManifest(dependencyRoot, name));
11560
+ const id = metadata.elementspanel?.id;
11561
+ if (validId.test(id) && !configured.has(id))
11562
+ configured.set(id, { name, source: "project", automatic: true });
11563
+ } catch {
11564
+ /* Ignore incomplete npm installations until the next scan. */
11565
+ }
11566
+ }
11567
+ for (const [id, name] of Object.entries(mappingOf(host))) {
11568
+ if (dependenciesOf(project)[name] !== undefined)
11569
+ configured.set(id, {
11570
+ name,
11571
+ source: "bundled",
11572
+ automatic: true
11573
+ });
11574
+ else if (configured.has(id)) {
11575
+ if (!skipInvalid) throw new Error(`Plugin id ${id} belongs to built-in package ${name}.`);
11576
+ configured.delete(id);
11577
+ }
11578
+ }
11579
+
11580
+ const projectDependencies = dependenciesOf(project);
11581
+ const folders = new Set();
11582
+ const records = [];
11583
+
11584
+ for (const [id, mapping] of configured) {
11585
+ const name = mapping.name;
11586
+ if (
11587
+ !validId.test(id) ||
11588
+ typeof name !== "string" ||
11589
+ !validName.test(name) ||
11590
+ projectDependencies[name] === undefined
11591
+ ) {
11592
+ if (skipInvalid) continue;
11593
+ throw new Error(`Invalid npm plugin dependency: ${id}.`);
11594
+ }
11595
+
11596
+ let manifestPath;
11597
+ let failure;
11598
+ try {
11599
+ manifestPath = packageManifest(dependencyRoot, name);
11600
+ } catch (error) {
11601
+ failure = error;
11602
+ }
11603
+ if (!manifestPath) {
11604
+ if (skipInvalid) continue;
11605
+ throw new Error(`Missing npm plugin ${name}. Run npm install in ${dependencyRoot}.`, {
11606
+ cause: failure
11607
+ });
11608
+ }
11609
+ let metadata;
11610
+ try {
11611
+ metadata = readJson(manifestPath);
11612
+ } catch (error) {
11613
+ if (skipInvalid) continue;
11614
+ throw error;
11615
+ }
11616
+ const definition = metadata.elementspanel?.[selectedSide];
11617
+ // A project dependency may only ship one side; the other side simply does
11618
+ // not see it. A built-in plugin declared by the runtime must carry this side.
11619
+ if (!definition) {
11620
+ if (mapping.source === "project" || skipInvalid) continue;
11621
+ throw new Error(`Invalid ${selectedSide} entry in npm plugin ${name}.`);
11622
+ }
11623
+ const pluginRoot = path.dirname(manifestPath);
11624
+ if (
11625
+ typeof definition.directory !== "string" ||
11626
+ typeof definition.folder !== "string" ||
11627
+ !validId.test(definition.folder) ||
11628
+ path.isAbsolute(definition.directory)
11629
+ ) {
11630
+ if (skipInvalid) continue;
11631
+ throw new Error(`Npm plugin entry escapes its package: ${name}.`);
11632
+ }
11633
+ const directory = path.resolve(pluginRoot, definition.directory);
11634
+ if (directory === pluginRoot || !within(pluginRoot, directory)) {
11635
+ if (skipInvalid) continue;
11636
+ throw new Error(`Npm plugin entry escapes its package: ${name}.`);
11637
+ }
11638
+ let entries;
11639
+ try {
11640
+ entries = readJson(path.join(directory, "plugin.json"));
11641
+ } catch (error) {
11642
+ if (skipInvalid) continue;
11643
+ throw error;
11644
+ }
11645
+ if (
11646
+ metadata.elementspanel?.id !== id ||
11647
+ typeof metadata.version !== "string" ||
11648
+ !metadata.version.trim() ||
11649
+ !entries ||
11650
+ typeof entries !== "object" ||
11651
+ Array.isArray(entries)
11652
+ ) {
11653
+ if (skipInvalid) continue;
11654
+ throw new Error(`Invalid npm plugin identity or version in package.json: ${name}.`);
11655
+ }
11656
+ if (folders.has(definition.folder)) {
11657
+ if (skipInvalid) continue;
11658
+ throw new Error(`Duplicate npm plugin asset directory: ${definition.folder}.`);
11659
+ }
11660
+ folders.add(definition.folder);
11661
+ records.push({
11662
+ id,
11663
+ name,
11664
+ version: metadata.version,
11665
+ source: mapping.source,
11666
+ defaultEnabled: mapping.automatic === true || mapping.source !== "project",
11667
+ packageRoot: pluginRoot,
11668
+ directory,
11669
+ folder: definition.folder,
11670
+ dependencies: Object.keys(metadata.dependencies || {})
11671
+ });
11672
+ }
11673
+ return records;
11674
+ }
11675
+
11676
+ module.exports = { resolveNpmPlugins, projectManifestFile };
11677
+
11678
+
11463
11679
  /***/ },
11464
11680
 
11465
11681
  /***/ "../../scripts/workspace-layout.cjs"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementspanel/epanel-plugin-instance",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Application instance and Java runtime management.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Csituka_D <694428809@qq.com> (https://github.com/JessDaodao)",
@@ -39,7 +39,45 @@
39
39
  "frontend/assets/style-0-style-10-_panel-plugin-build-entry_instance-242e9b23.css"
40
40
  ],
41
41
  "directory": "panel",
42
- "folder": "instance"
42
+ "folder": "instance",
43
+ "frontendAssets": [
44
+ "assets/AppDialog.vue_vue_type_style_index_0_lang-f1610327.js",
45
+ "assets/CardPanel-6fb07d57.js",
46
+ "assets/DesktopDialog-653072f1.js",
47
+ "assets/Loading.vue_vue_type_script_setup_true_lang-e02f05df.js",
48
+ "assets/PageToolbar-6c8b1cf6.js",
49
+ "assets/_elements-sdk_cordis-e3bebc89.js",
50
+ "assets/_elements-sdk_cosmokit-e4842f5c.js",
51
+ "assets/_plugin-vue_export-helper-c27b6911.js",
52
+ "assets/const-5d04f817.js",
53
+ "assets/const-9c0a1a51.js",
54
+ "assets/context-f49cfcce.js",
55
+ "assets/copy-9f2e18f6.js",
56
+ "assets/dayjs.min-25183c0a.js",
57
+ "assets/desktopNotice-d83e9e67.js",
58
+ "assets/htmlparser2-bcb8f9db.js",
59
+ "assets/i18n-66e8c48a.js",
60
+ "assets/index-c684efbd.js",
61
+ "assets/instance-5ec65e42.js",
62
+ "assets/instance_contract-0115efe5.js",
63
+ "assets/lodash-001e4f93.js",
64
+ "assets/msl-logo-699c8076.png",
65
+ "assets/node-e836f0a9.js",
66
+ "assets/nodes-5b8baedc.js",
67
+ "assets/protocol-97f7bab7.js",
68
+ "assets/router-ffe15376.js",
69
+ "assets/useAppRouters-32a87787.js",
70
+ "assets/useAppStateStore-e16e4edd.js",
71
+ "assets/useDefineApi-5b6599dc.js",
72
+ "assets/useInstanceActionAvailability-514826f4.js",
73
+ "assets/useKeyboardEvents-b70c8bcd.js",
74
+ "assets/useMountComponent-26d35a1e.js",
75
+ "assets/useOverviewInfo-f30aafac.js",
76
+ "assets/usePolling-73859228.js",
77
+ "assets/useScreen-7eb281ed.js",
78
+ "assets/vue-828bfe55.js",
79
+ "assets/vuetifyModal-47e2f27c.js"
80
+ ]
43
81
  },
44
82
  "daemon": {
45
83
  "priority": 40,
@@ -50,8 +88,8 @@
50
88
  }
51
89
  },
52
90
  "peerDependencies": {
53
- "@elementspanel/panel": "^1.0.3",
54
- "@elementspanel/daemon": "^1.0.3"
91
+ "@elementspanel/panel": "^1.0.0",
92
+ "@elementspanel/daemon": "^1.0.0"
55
93
  },
56
94
  "peerDependenciesMeta": {
57
95
  "@elementspanel/panel": {
@@ -10386,6 +10386,39 @@ async function loadPluginModule(entry, directory, revision) {
10386
10386
 
10387
10387
  "use strict";
10388
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
+ })();
10389
10422
  var __importDefault = (this && this.__importDefault) || function (mod) {
10390
10423
  return (mod && mod.__esModule) ? mod : { "default": mod };
10391
10424
  };
@@ -10405,6 +10438,8 @@ const path_1 = __importDefault(__webpack_require__("path"));
10405
10438
  const module_1 = __webpack_require__("module");
10406
10439
  const plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
10407
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"));
10408
10443
  const PLUGIN_ID = /^[a-z][a-z0-9_-]{1,63}$/;
10409
10444
  const PACKAGE_NAME = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
10410
10445
  const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$/;
@@ -10446,11 +10481,20 @@ function currentProjectRoot(packageRoot = currentPackageRoot()) {
10446
10481
  return scope;
10447
10482
  }
10448
10483
  function npmPluginRoots(options = {}) {
10449
- var _a, _b;
10484
+ var _a, _b, _c;
10450
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);
10451
10491
  return {
10452
10492
  packageRoot,
10453
- 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
+ }))
10454
10498
  };
10455
10499
  }
10456
10500
  function projectManifestFile(projectRoot) {
@@ -10481,15 +10525,10 @@ function pluginRecord(pluginId, name, source, packageRoot, directory, folder, ve
10481
10525
  function readPackageEntry(packageRoot, name) {
10482
10526
  // Read package boundaries directly: Node caches require.resolve() paths even
10483
10527
  // after npm moves an installation between nested and hoisted node_modules.
10484
- let manifestPath;
10485
- for (const root of [packageRoot, currentProjectRoot(packageRoot)]) {
10486
- const localRequire = (0, module_1.createRequire)(path_1.default.join(root, "package.json"));
10487
- manifestPath = (localRequire.resolve.paths(name) || [])
10488
- .map((directory) => path_1.default.join(directory, name, "package.json"))
10489
- .find((filename) => fs_extra_1.default.existsSync(filename));
10490
- if (manifestPath)
10491
- break;
10492
- }
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));
10493
10532
  if (!manifestPath)
10494
10533
  throw new Error(`Missing npm plugin package: ${name}.`);
10495
10534
  manifestPath = fs_extra_1.default.realpathSync(manifestPath);
@@ -10537,84 +10576,26 @@ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side)
10537
10576
  /**
10538
10577
  * Every npm plugin the runtime can load for a side, built-in and project-installed.
10539
10578
  *
10540
- * Built-in package names win over a project mapping for the same id. An explicit
10541
- * project dependency on that same package selects the user's installed version.
10542
- * A project package that does not describe the requested side is skipped instead of failing, which
10543
- * 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.
10544
10581
  */
10545
10582
  function listNpmPlugins(side, options = {}) {
10546
- var _a, _b;
10547
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10548
- const host = readJson(path_1.default.join(packageRoot, "package.json"));
10549
- if (!host)
10550
- throw new Error(`Missing runtime package metadata: ${packageRoot}`);
10551
- const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot)
10552
- ? null
10553
- : readProjectManifest(projectRoot);
10554
- const hostMappings = mappingsOf(host);
10555
- const projectMappings = { ...mappingsOf(project) };
10556
- for (const name of Object.keys(dependenciesOf(project))) {
10557
- if (!PACKAGE_NAME.test(name) || Object.values(projectMappings).includes(name))
10558
- continue;
10559
- try {
10560
- const entry = readPackageEntry(projectRoot, name);
10561
- const id = stringValue((_a = objectValue(entry.metadata.elementspanel)) === null || _a === void 0 ? void 0 : _a.id);
10562
- if (PLUGIN_ID.test(id) && projectMappings[id] === undefined)
10563
- projectMappings[id] = name;
10564
- }
10565
- catch (_c) {
10566
- /* npm may still be writing the package. */
10567
- }
10568
- }
10569
- const mappings = new Map();
10570
- for (const [pluginId, name] of Object.entries(projectMappings)) {
10571
- if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
10572
- continue;
10573
- mappings.set(pluginId, { name, source: "project" });
10574
- }
10575
- for (const [pluginId, name] of Object.entries(hostMappings)) {
10576
- if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
10577
- continue;
10578
- if (dependenciesOf(host)[name] === undefined)
10579
- continue;
10580
- mappings.set(pluginId, {
10581
- name,
10582
- source: dependenciesOf(project)[name] === undefined ? "bundled" : "project"
10583
- });
10584
- }
10585
- const hostDependencies = dependenciesOf(host);
10586
- const projectDependencies = dependenciesOf(project);
10587
- const folders = new Set();
10583
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots({ ...options, side });
10588
10584
  const records = [];
10589
- for (const [pluginId, mapping] of mappings) {
10590
- const declared = (_b = hostDependencies[mapping.name]) !== null && _b !== void 0 ? _b : projectDependencies[mapping.name];
10591
- if (declared === undefined) {
10592
- if (options.skipInvalid)
10593
- continue;
10594
- throw new Error(`Missing npm plugin dependency: ${mapping.name}. Run npm install in the project directory.`);
10595
- }
10596
- let entry;
10597
- try {
10598
- entry = readPackageEntry(mapping.source === "project" ? projectRoot : packageRoot, mapping.name);
10599
- }
10600
- catch (error) {
10601
- if (options.skipInvalid)
10602
- continue;
10603
- throw error;
10604
- }
10605
- 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);
10606
10594
  if (!record) {
10607
- if (options.skipInvalid || mapping.source === "project")
10608
- continue;
10609
- throw new Error(`Invalid ${side !== null && side !== void 0 ? side : "plugin"} entry in npm plugin ${mapping.name}.`);
10610
- }
10611
- const folder = record.folder;
10612
- if (folders.has(folder)) {
10613
10595
  if (options.skipInvalid)
10614
10596
  continue;
10615
- 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}.`);
10616
10598
  }
10617
- folders.add(folder);
10618
10599
  records.push(record);
10619
10600
  }
10620
10601
  return records;
@@ -10713,10 +10694,9 @@ async function acquireProjectLock(projectRoot) {
10713
10694
  }
10714
10695
  }
10715
10696
  }
10716
- function npmPluginOperationPending() {
10717
- const { packageRoot, projectRoot } = npmPluginRoots();
10718
- return (operations.size > 0 ||
10719
- [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));
10720
10700
  }
10721
10701
  function withProjectLock(projectRoot, operation) {
10722
10702
  var _a;
@@ -10752,19 +10732,16 @@ async function installNpmPlugin(options) {
10752
10732
  throw new Error(`Invalid plugin id: ${options.pluginId}`);
10753
10733
  if (!VERSION.test(version))
10754
10734
  throw new Error(`Invalid plugin version: ${version}`);
10755
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10756
- return withProjectLock(projectRoot, async () => {
10757
- var _a, _b;
10735
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots(options);
10736
+ return withProjectLock(dependencyRoot, async () => {
10737
+ var _a, _b, _c, _d, _e;
10758
10738
  const host = readJson(path_1.default.join(packageRoot, "package.json"));
10759
10739
  if (!host)
10760
10740
  throw new Error(`Missing runtime package metadata: ${packageRoot}`);
10761
- const hostId = (_a = Object.entries(mappingsOf(host)).find(([, value]) => value === name && dependenciesOf(host)[name] !== undefined)) === null || _a === void 0 ? void 0 : _a[0];
10762
- const targetRoot = hostId && dependenciesOf(readProjectManifest(projectRoot))[name] === undefined
10763
- ? packageRoot
10764
- : 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;
10765
10743
  if (options.pluginId &&
10766
10744
  mappingsOf(host)[options.pluginId] &&
10767
- dependenciesOf(host)[String(mappingsOf(host)[options.pluginId])] !== undefined &&
10768
10745
  mappingsOf(host)[options.pluginId] !== name)
10769
10746
  throw new Error(`BUNDLED:${options.pluginId}`);
10770
10747
  const original = readProjectManifest(targetRoot);
@@ -10774,7 +10751,7 @@ async function installNpmPlugin(options) {
10774
10751
  try {
10775
10752
  previousVersion = readPackageEntry(targetRoot, name).metadata.version || previous;
10776
10753
  }
10777
- catch (_c) {
10754
+ catch (_f) {
10778
10755
  /* Keep the declared spec if the previous package is incomplete. */
10779
10756
  }
10780
10757
  }
@@ -10793,24 +10770,32 @@ async function installNpmPlugin(options) {
10793
10770
  "--save-exact",
10794
10771
  "--no-fund",
10795
10772
  "--no-audit",
10796
- "--ignore-scripts"
10773
+ "--ignore-scripts",
10774
+ "--workspaces=false"
10797
10775
  ], targetRoot);
10798
10776
  const entry = readPackageEntry(targetRoot, name);
10799
10777
  const pluginId = options.pluginId || stringValue((_b = objectValue(entry.metadata.elementspanel)) === null || _b === void 0 ? void 0 : _b.id);
10800
10778
  if (!PLUGIN_ID.test(pluginId))
10801
10779
  throw new Error(`Invalid plugin id in ${name}.`);
10802
- if (mappingsOf(host)[pluginId] &&
10803
- dependenciesOf(host)[String(mappingsOf(host)[pluginId])] !== undefined &&
10804
- mappingsOf(host)[pluginId] !== name)
10780
+ if (mappingsOf(host)[pluginId] && mappingsOf(host)[pluginId] !== name)
10805
10781
  throw new Error(`Plugin id ${pluginId} belongs to a bundled dependency.`);
10806
10782
  if (hostId && pluginId !== hostId)
10807
10783
  throw new Error(`The package ${name} changed its plugin id from ${hostId} to ${pluginId}.`);
10808
10784
  const mapped = mappingsOf(readProjectManifest(targetRoot))[pluginId];
10809
10785
  if (mapped !== undefined && mapped !== name)
10810
10786
  throw new Error(`Plugin id ${pluginId} is already installed as ${String(mapped)}.`);
10811
- 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);
10812
10788
  if (!record)
10813
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
+ }
10814
10799
  if (/^[0-9]/.test(version) && record.version !== version)
10815
10800
  throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
10816
10801
  if (!process.env.EPANEL_PACKAGE_ROOT &&
@@ -10854,7 +10839,8 @@ async function installNpmPlugin(options) {
10854
10839
  "--save",
10855
10840
  "--no-fund",
10856
10841
  "--no-audit",
10857
- "--ignore-scripts"
10842
+ "--ignore-scripts",
10843
+ "--workspaces=false"
10858
10844
  ]
10859
10845
  : [
10860
10846
  "--prefix",
@@ -10863,7 +10849,8 @@ async function installNpmPlugin(options) {
10863
10849
  name,
10864
10850
  "--no-fund",
10865
10851
  "--no-audit",
10866
- "--ignore-scripts"
10852
+ "--ignore-scripts",
10853
+ "--workspaces=false"
10867
10854
  ], targetRoot);
10868
10855
  }
10869
10856
  catch (rollback) {
@@ -10887,22 +10874,29 @@ async function uninstallNpmPlugin(pluginId, options = {}) {
10887
10874
  throw new Error(`Invalid plugin id: ${pluginId}`);
10888
10875
  if (isEssentialNpmPlugin(pluginId))
10889
10876
  throw new Error(`The essential plugin "${pluginId}" cannot be uninstalled.`);
10890
- const { packageRoot, projectRoot } = npmPluginRoots(options);
10891
- return withProjectLock(projectRoot, async () => {
10877
+ const { packageRoot, projectRoot, dependencyRoot } = npmPluginRoots(options);
10878
+ return withProjectLock(dependencyRoot, async () => {
10892
10879
  var _a, _b;
10893
- const host = readJson(path_1.default.join(packageRoot, "package.json"));
10894
- const bundledName = mappingsOf(host)[pluginId];
10895
- const targetRoot = typeof bundledName === "string" &&
10896
- dependenciesOf(host)[bundledName] !== undefined &&
10897
- dependenciesOf(readProjectManifest(projectRoot))[bundledName] === undefined
10898
- ? packageRoot
10899
- : projectRoot;
10880
+ const targetRoot = dependencyRoot;
10900
10881
  const manifest = readProjectManifest(targetRoot);
10901
- 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;
10902
10883
  if (typeof name !== "string" || !PACKAGE_NAME.test(name))
10903
10884
  return null;
10904
- const record = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId);
10905
- 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);
10906
10900
  const updated = readProjectManifest(targetRoot);
10907
10901
  const plugins = { ...mappingsOf(updated) };
10908
10902
  delete plugins[pluginId];
@@ -11076,11 +11070,11 @@ const plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
11076
11070
  /** Poll package boundaries, allowing npm to finish replacing files before reconciliation. */
11077
11071
  function watchNpmPlugins(ctx, side, reload, changed) {
11078
11072
  const stamp = () => {
11079
- const { packageRoot, projectRoot } = (0, plugin_npm_1.npmPluginRoots)();
11073
+ const { packageRoot, dependencyRoot } = (0, plugin_npm_1.npmPluginRoots)({ side });
11080
11074
  if (!node_fs_1.default.existsSync(node_path_1.default.join(packageRoot, "package.json")))
11081
11075
  return "";
11082
11076
  const files = [];
11083
- for (const root of [packageRoot, projectRoot])
11077
+ for (const root of [packageRoot, dependencyRoot])
11084
11078
  for (const name of [
11085
11079
  "package.json",
11086
11080
  "package-lock.json",
@@ -11104,7 +11098,7 @@ function watchNpmPlugins(ctx, side, reload, changed) {
11104
11098
  let candidate = applied;
11105
11099
  let busy = false;
11106
11100
  ctx.setInterval(() => {
11107
- if (busy || (0, plugin_npm_1.npmPluginOperationPending)())
11101
+ if (busy || (0, plugin_npm_1.npmPluginOperationPending)(side))
11108
11102
  return;
11109
11103
  try {
11110
11104
  const next = stamp();
@@ -12127,6 +12121,226 @@ module.exports = require("util");
12127
12121
  "use strict";
12128
12122
  module.exports = require("zlib");
12129
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
+
12130
12344
  /***/ },
12131
12345
 
12132
12346
  /***/ "../../scripts/workspace-layout.cjs"