@bonsae/nrg 0.21.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -3
  3. package/package.json +14 -9
  4. package/schemas/labels.schema.json +15 -5
  5. package/server/index.cjs +2 -1358
  6. package/test/client/component/index.js +41 -224
  7. package/test/client/component/setup.js +201 -1475
  8. package/test/client/e2e/config.js +12 -0
  9. package/test/client/e2e/index.js +419 -199
  10. package/test/client/unit/index.js +19 -32
  11. package/test/client/unit/setup.js +28 -21
  12. package/test/server/integration/index.js +2 -26
  13. package/test/server/unit/index.js +2 -184
  14. package/types/client.d.ts +1 -266
  15. package/types/server.d.ts +1 -900
  16. package/types/test-client-component.d.ts +7 -143
  17. package/types/test-client-e2e.d.ts +0 -6
  18. package/types/test-client-unit.d.ts +11 -105
  19. package/types/test-server-integration.d.ts +73 -49
  20. package/types/test-server-unit.d.ts +26 -2
  21. package/types/vite.d.ts +2 -0
  22. package/vite/index.js +378 -150
  23. package/server/resources/nrg-client.js +0 -7493
  24. package/test/client/component/nrg.css +0 -1
  25. package/types/shims/brands.d.ts +0 -32
  26. package/types/shims/client/form/components/node-red-config-input.vue.d.ts +0 -125
  27. package/types/shims/client/form/components/node-red-editor-input.vue.d.ts +0 -124
  28. package/types/shims/client/form/components/node-red-input-label.vue.d.ts +0 -34
  29. package/types/shims/client/form/components/node-red-input.vue.d.ts +0 -123
  30. package/types/shims/client/form/components/node-red-json-schema-form.vue.d.ts +0 -772
  31. package/types/shims/client/form/components/node-red-select-input.vue.d.ts +0 -132
  32. package/types/shims/client/form/components/node-red-toggle.vue.d.ts +0 -36
  33. package/types/shims/client/form/components/node-red-typed-input.vue.d.ts +0 -151
  34. package/types/shims/client/globals.d.ts +0 -320
  35. package/types/shims/client/types.d.ts +0 -227
  36. package/types/shims/components.d.ts +0 -23
  37. package/types/shims/constants.d.ts +0 -4
  38. package/types/shims/schema-options.d.ts +0 -24
  39. package/types/shims/shims-vue.d.ts +0 -5
  40. package/types/shims/typebox.d.ts +0 -10
package/vite/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/vite/plugin.ts
2
- import path14 from "path";
2
+ import path15 from "path";
3
3
 
4
4
  // src/vite/defaults.ts
5
5
  var DEFAULT_OUTPUT_DIR = "./dist";
@@ -766,7 +766,7 @@ var NodeRedLauncher = class {
766
766
  // src/vite/plugins/server.ts
767
767
  import chokidar from "chokidar";
768
768
  import treeKill2 from "tree-kill";
769
- import path13 from "path";
769
+ import path14 from "path";
770
770
 
771
771
  // src/vite/server/build.ts
772
772
  import { build as viteBuild } from "vite";
@@ -778,6 +778,26 @@ import dts from "vite-plugin-dts";
778
778
  import fs5 from "fs";
779
779
  import path4 from "path";
780
780
  import ts from "typescript";
781
+ var RUNTIME_TYPE_REWRITES = {
782
+ "@bonsae/nrg/server": "@bonsae/nrg-runtime/server",
783
+ "@bonsae/nrg/client": "@bonsae/nrg-runtime/client"
784
+ };
785
+ function rewriteRuntimeTypeImports(outDir, entryNames) {
786
+ for (const name of entryNames) {
787
+ const dtsPath = path4.join(outDir, `${name}.d.ts`);
788
+ if (!fs5.existsSync(dtsPath)) continue;
789
+ const original = fs5.readFileSync(dtsPath, "utf-8");
790
+ let rewritten = original;
791
+ for (const [from, to] of Object.entries(RUNTIME_TYPE_REWRITES)) {
792
+ const escaped = from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
793
+ const re = new RegExp(`(['"])${escaped}\\1`, "g");
794
+ rewritten = rewritten.replace(re, `$1${to}$1`);
795
+ }
796
+ if (rewritten !== original) {
797
+ fs5.writeFileSync(dtsPath, rewritten);
798
+ }
799
+ }
800
+ }
781
801
  function collectTsFiles(dir) {
782
802
  if (!fs5.existsSync(dir)) return [];
783
803
  return fs5.readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => {
@@ -1152,18 +1172,23 @@ ${reexports}
1152
1172
  }
1153
1173
 
1154
1174
  // src/vite/server/plugins/output-wrapper.ts
1155
- function cjsWrapper() {
1175
+ function nrgServerSpecifier(isDev) {
1176
+ return isDev ? "@bonsae/nrg/server" : "@bonsae/nrg-runtime/server";
1177
+ }
1178
+ function cjsWrapper(isDev = false) {
1179
+ const serverSpecifier = nrgServerSpecifier(isDev);
1156
1180
  return {
1157
1181
  name: "vite-plugin-node-red:server:cjs-wrapper",
1158
1182
  renderChunk(code, chunk, outputOptions) {
1159
1183
  if (!chunk.isEntry || outputOptions.format !== "cjs") return null;
1160
- const footer = `(function(){var _exp=module.exports&&module.exports.__esModule?module.exports.default:module.exports;if(_exp&&typeof _exp==="object"&&Array.isArray(_exp.nodes)){var _nrg=require("@bonsae/nrg/server");module.exports=_nrg.registerTypes(_exp.nodes);}else if(typeof _exp==="function"&&Array.isArray(_exp.nodes)){module.exports=_exp;_exp.nodes.forEach(function(cls){if(cls&&cls.type){_exp[cls.type.replace(/(?:^|[-_])(\\w)/g,function(_,c){return c.toUpperCase();})] = cls;}});}})();`;
1184
+ const footer = `(function(){var _exp=module.exports&&module.exports.__esModule?module.exports.default:module.exports;if(_exp&&typeof _exp==="object"&&Array.isArray(_exp.nodes)){var _nrg=require(${JSON.stringify(serverSpecifier)});module.exports=_nrg.registerTypes(_exp.nodes);}else if(typeof _exp==="function"&&Array.isArray(_exp.nodes)){module.exports=_exp;_exp.nodes.forEach(function(cls){if(cls&&cls.type){_exp[cls.type.replace(/(?:^|[-_])(\\w)/g,function(_,c){return c.toUpperCase();})] = cls;}});}})();`;
1161
1185
  return { code: `${code}
1162
1186
  ${footer}`, map: null };
1163
1187
  }
1164
1188
  };
1165
1189
  }
1166
- function esmWrapper() {
1190
+ function esmWrapper(isDev = false) {
1191
+ const serverSpecifier = nrgServerSpecifier(isDev);
1167
1192
  return {
1168
1193
  name: "vite-plugin-node-red:server:esm-wrapper",
1169
1194
  renderChunk(code, chunk, outputOptions) {
@@ -1189,7 +1214,9 @@ function esmWrapper() {
1189
1214
  `import { dirname as __nrgDirname } from "path";`,
1190
1215
  `var __filename = __nrgFileURLToPath(import.meta.url);`,
1191
1216
  `var __dirname = __nrgDirname(__filename);`,
1192
- `import { registerTypes as __nrgRegisterTypes } from "@bonsae/nrg/server";`,
1217
+ `import { registerTypes as __nrgRegisterTypes } from ${JSON.stringify(
1218
+ serverSpecifier
1219
+ )};`,
1193
1220
  ``
1194
1221
  ].join("\n");
1195
1222
  const replacement = [
@@ -1213,6 +1240,12 @@ var nodeBuiltins = /* @__PURE__ */ new Set([
1213
1240
  ...builtinModules2,
1214
1241
  ...builtinModules2.map((m) => `node:${m}`)
1215
1242
  ]);
1243
+ var RUNTIME_REWRITES = {
1244
+ "@bonsae/nrg/server": "@bonsae/nrg-runtime/server"
1245
+ };
1246
+ var RUNTIME_VERSION_SOURCE = {
1247
+ "@bonsae/nrg-runtime": "@bonsae/nrg"
1248
+ };
1216
1249
  function buildTypesPath(entryName) {
1217
1250
  return `./${entryName}.d.ts`;
1218
1251
  }
@@ -1269,7 +1302,8 @@ function packageJsonGenerator(options) {
1269
1302
  bundled = [],
1270
1303
  types = false,
1271
1304
  entryNames = [],
1272
- format = "cjs"
1305
+ format = "cjs",
1306
+ isDev = false
1273
1307
  } = options;
1274
1308
  const trackedDependencies = /* @__PURE__ */ new Set();
1275
1309
  return {
@@ -1287,12 +1321,13 @@ function packageJsonGenerator(options) {
1287
1321
  if (nodeBuiltins.has(source)) {
1288
1322
  return { id: source, external: true };
1289
1323
  }
1290
- const packageName = source.startsWith("@") ? source.split("/").slice(0, 2).join("/") : source.split("/")[0];
1324
+ const resolved = isDev ? source : RUNTIME_REWRITES[source] ?? source;
1325
+ const packageName = resolved.startsWith("@") ? resolved.split("/").slice(0, 2).join("/") : resolved.split("/")[0];
1291
1326
  if (bundled.includes(packageName)) {
1292
1327
  return null;
1293
1328
  }
1294
1329
  trackedDependencies.add(packageName);
1295
- return { id: source, external: true };
1330
+ return { id: resolved, external: true };
1296
1331
  }
1297
1332
  },
1298
1333
  closeBundle() {
@@ -1305,14 +1340,19 @@ function packageJsonGenerator(options) {
1305
1340
  fs6.readFileSync(rootPackageJsonPath, "utf-8")
1306
1341
  );
1307
1342
  const sourceDeps = rootPackageJson.dependencies ?? {};
1343
+ const devDeps = rootPackageJson.devDependencies ?? {};
1308
1344
  const peerDeps = rootPackageJson.peerDependencies ?? {};
1309
1345
  let distDependencies = {};
1310
1346
  for (const dep of trackedDependencies) {
1311
1347
  if (peerDeps[dep]) {
1312
1348
  continue;
1313
1349
  }
1350
+ const versionSource = RUNTIME_VERSION_SOURCE[dep];
1351
+ const sourceVersion = versionSource ? sourceDeps[versionSource] ?? devDeps[versionSource] : void 0;
1314
1352
  if (sourceDeps[dep]) {
1315
1353
  distDependencies[dep] = sourceDeps[dep];
1354
+ } else if (sourceVersion) {
1355
+ distDependencies[dep] = sourceVersion;
1316
1356
  } else {
1317
1357
  const dependencyPackageJsonPath = path5.resolve(
1318
1358
  `./node_modules/${dep}/package.json`
@@ -1399,9 +1439,10 @@ async function build(serverOpts, buildContext) {
1399
1439
  bundled,
1400
1440
  types: types && !buildContext.isDev,
1401
1441
  entryNames: Object.keys(entryPoints),
1402
- format
1442
+ format,
1443
+ isDev: buildContext.isDev
1403
1444
  }),
1404
- isEsm ? esmWrapper() : cjsWrapper()
1445
+ isEsm ? esmWrapper(buildContext.isDev) : cjsWrapper(buildContext.isDev)
1405
1446
  ];
1406
1447
  if (types && !buildContext.isDev) {
1407
1448
  plugins.push(
@@ -1443,6 +1484,9 @@ async function build(serverOpts, buildContext) {
1443
1484
  };
1444
1485
  try {
1445
1486
  await viteBuild(config);
1487
+ if (types && !buildContext.isDev) {
1488
+ rewriteRuntimeTypeImports(buildContext.outDir, Object.keys(entryPoints));
1489
+ }
1446
1490
  if (isEsm) {
1447
1491
  const bridgeCode = `'use strict';
1448
1492
  // CJS bridge \u2014 auto-generated by @bonsae/nrg/vite
@@ -1469,12 +1513,13 @@ module.exports = function (RED) {
1469
1513
  // src/vite/client/build.ts
1470
1514
  import { build as viteBuild2 } from "vite";
1471
1515
  import vue from "@vitejs/plugin-vue";
1472
- import fs13 from "fs";
1473
- import path12 from "path";
1516
+ import fs14 from "fs";
1517
+ import path13 from "path";
1518
+ import crypto from "crypto";
1474
1519
 
1475
1520
  // src/vite/client/plugins/help-generator.ts
1476
- import fs8 from "fs";
1477
- import path7 from "path";
1521
+ import fs9 from "fs";
1522
+ import path8 from "path";
1478
1523
  import { pathToFileURL as pathToFileURL2 } from "url";
1479
1524
  import { createRequire as createRequire2 } from "module";
1480
1525
 
@@ -1705,8 +1750,127 @@ function getHelpTranslations(lang) {
1705
1750
  return translations[lang] ?? translations["en-US"];
1706
1751
  }
1707
1752
 
1753
+ // src/vite/client/plugins/unsafe-types.ts
1754
+ import ts2 from "typescript";
1755
+ import fs8 from "fs";
1756
+ import path7 from "path";
1757
+ var NRG_SERVER_MODULE = "@bonsae/nrg/server";
1758
+ function normalizeType(text) {
1759
+ return text.replace(/\s+/g, " ").trim();
1760
+ }
1761
+ function resolveImports(sf) {
1762
+ const defineSchema = /* @__PURE__ */ new Set();
1763
+ const schemaType = /* @__PURE__ */ new Set();
1764
+ sf.forEachChild((node) => {
1765
+ if (!ts2.isImportDeclaration(node) || !ts2.isStringLiteral(node.moduleSpecifier) || node.moduleSpecifier.text !== NRG_SERVER_MODULE || !node.importClause?.namedBindings || !ts2.isNamedImports(node.importClause.namedBindings)) {
1766
+ return;
1767
+ }
1768
+ for (const el of node.importClause.namedBindings.elements) {
1769
+ const imported = (el.propertyName ?? el.name).text;
1770
+ const local = el.name.text;
1771
+ if (imported === "defineSchema") defineSchema.add(local);
1772
+ if (imported === "SchemaType") schemaType.add(local);
1773
+ }
1774
+ });
1775
+ return { defineSchema, schemaType };
1776
+ }
1777
+ function staticPropName(name) {
1778
+ if (ts2.isIdentifier(name) || ts2.isStringLiteralLike(name)) return name.text;
1779
+ return void 0;
1780
+ }
1781
+ function unsafeTypeArg(node, sf, schemaType) {
1782
+ if (!ts2.isCallExpression(node) || !ts2.isPropertyAccessExpression(node.expression) || !ts2.isIdentifier(node.expression.expression) || !schemaType.has(node.expression.expression.text)) {
1783
+ return void 0;
1784
+ }
1785
+ const method = node.expression.name.text;
1786
+ if (method === "Unsafe" && node.typeArguments?.length) {
1787
+ return normalizeType(node.typeArguments[0].getText(sf));
1788
+ }
1789
+ if (method === "Array" && node.arguments.length) {
1790
+ const inner = unsafeTypeArg(node.arguments[0], sf, schemaType);
1791
+ if (inner) return `${inner}[]`;
1792
+ }
1793
+ return void 0;
1794
+ }
1795
+ function readSchemaCall(call, sf, schemaType) {
1796
+ const [propsArg, optsArg] = call.arguments;
1797
+ if (!propsArg || !optsArg || !ts2.isObjectLiteralExpression(propsArg) || !ts2.isObjectLiteralExpression(optsArg)) {
1798
+ return void 0;
1799
+ }
1800
+ let id;
1801
+ for (const p of optsArg.properties) {
1802
+ if (ts2.isPropertyAssignment(p) && staticPropName(p.name) === "$id" && ts2.isStringLiteralLike(p.initializer)) {
1803
+ id = p.initializer.text;
1804
+ }
1805
+ }
1806
+ if (!id) return void 0;
1807
+ const props = {};
1808
+ for (const p of propsArg.properties) {
1809
+ if (!ts2.isPropertyAssignment(p)) continue;
1810
+ const key = staticPropName(p.name);
1811
+ if (key === void 0) continue;
1812
+ const type = unsafeTypeArg(p.initializer, sf, schemaType);
1813
+ if (type) props[key] = type;
1814
+ }
1815
+ return { id, props };
1816
+ }
1817
+ function extractUnsafeTypesFromSource(fileName, code) {
1818
+ const out = /* @__PURE__ */ new Map();
1819
+ const sf = ts2.createSourceFile(fileName, code, ts2.ScriptTarget.Latest, true);
1820
+ const { defineSchema, schemaType } = resolveImports(sf);
1821
+ if (defineSchema.size === 0 || schemaType.size === 0) return out;
1822
+ const visit = (node) => {
1823
+ if (ts2.isCallExpression(node) && ts2.isIdentifier(node.expression) && defineSchema.has(node.expression.text)) {
1824
+ const result = readSchemaCall(node, sf, schemaType);
1825
+ if (result && Object.keys(result.props).length > 0) {
1826
+ if (out.has(result.id)) {
1827
+ throw new Error(
1828
+ `Duplicate schema $id "${result.id}" in ${fileName} \u2014 $id must be unique`
1829
+ );
1830
+ }
1831
+ out.set(result.id, result.props);
1832
+ }
1833
+ }
1834
+ ts2.forEachChild(node, visit);
1835
+ };
1836
+ visit(sf);
1837
+ return out;
1838
+ }
1839
+ function collectTsFiles2(dir) {
1840
+ const out = [];
1841
+ if (!fs8.existsSync(dir)) return out;
1842
+ for (const entry of fs8.readdirSync(dir, { withFileTypes: true })) {
1843
+ const full = path7.join(dir, entry.name);
1844
+ if (entry.isDirectory()) {
1845
+ if (entry.name === "node_modules") continue;
1846
+ out.push(...collectTsFiles2(full));
1847
+ } else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".spec.ts")) {
1848
+ out.push(full);
1849
+ }
1850
+ }
1851
+ return out;
1852
+ }
1853
+ function extractUnsafeTypes(srcDir) {
1854
+ const merged = /* @__PURE__ */ new Map();
1855
+ for (const file of collectTsFiles2(srcDir).sort()) {
1856
+ const perFile = extractUnsafeTypesFromSource(
1857
+ file,
1858
+ fs8.readFileSync(file, "utf-8")
1859
+ );
1860
+ for (const [id, props] of perFile) {
1861
+ if (merged.has(id)) {
1862
+ throw new Error(
1863
+ `Duplicate schema $id "${id}" across ${srcDir} \u2014 $id must be unique`
1864
+ );
1865
+ }
1866
+ merged.set(id, props);
1867
+ }
1868
+ }
1869
+ return merged;
1870
+ }
1871
+
1708
1872
  // src/vite/client/plugins/help-generator.ts
1709
- function buildPropertyRow(name, schema, required, label) {
1873
+ function buildPropertyRow(name, schema, required, label, parsedType) {
1710
1874
  let type = "";
1711
1875
  if (schema["x-nrg-node-type"]) {
1712
1876
  type = `NodeRef \u2192 ${schema["x-nrg-node-type"]}`;
@@ -1714,6 +1878,8 @@ function buildPropertyRow(name, schema, required, label) {
1714
1878
  type = "TypedInput";
1715
1879
  } else if (schema.type) {
1716
1880
  type = String(schema.type);
1881
+ } else if (parsedType) {
1882
+ type = parsedType;
1717
1883
  }
1718
1884
  if (schema.enum) type += ` (${schema.enum.join(", ")})`;
1719
1885
  const constraints = [];
@@ -1749,16 +1915,19 @@ function generateSchemaSection(options) {
1749
1915
  t,
1750
1916
  labels,
1751
1917
  heading = "###",
1752
- includeDefault = true
1918
+ includeDefault = true,
1919
+ unsafeTypes
1753
1920
  } = options;
1754
1921
  if (!schema?.properties) return "";
1922
+ const parsed = unsafeTypes?.get(schema.$id);
1755
1923
  const required = new Set(schema.required ?? []);
1756
1924
  const rows = Object.entries(schema.properties).filter(([key]) => !SKIP_FIELDS.has(key)).map(
1757
1925
  ([key, propSchema]) => buildPropertyRow(
1758
1926
  key,
1759
1927
  propSchema,
1760
1928
  required.has(key),
1761
- labels?.[key]
1929
+ labels?.[key],
1930
+ parsed?.[key]
1762
1931
  )
1763
1932
  );
1764
1933
  if (rows.length === 0) return "";
@@ -1796,9 +1965,9 @@ ${table}
1796
1965
  `;
1797
1966
  }
1798
1967
  function loadNodeLabels(labelPath) {
1799
- if (!fs8.existsSync(labelPath)) return {};
1968
+ if (!fs9.existsSync(labelPath)) return {};
1800
1969
  try {
1801
- const raw = JSON.parse(fs8.readFileSync(labelPath, "utf-8"));
1970
+ const raw = JSON.parse(fs9.readFileSync(labelPath, "utf-8"));
1802
1971
  return {
1803
1972
  description: raw.description,
1804
1973
  configs: raw.configs,
@@ -1810,7 +1979,7 @@ function loadNodeLabels(labelPath) {
1810
1979
  return {};
1811
1980
  }
1812
1981
  }
1813
- function generateHelpDoc(nodeClass, labels, t) {
1982
+ function generateHelpDoc(nodeClass, labels, t, unsafeTypes) {
1814
1983
  const lines = [];
1815
1984
  if (labels.description) {
1816
1985
  lines.push(`<p>${labels.description}</p>`);
@@ -1819,14 +1988,16 @@ function generateHelpDoc(nodeClass, labels, t) {
1819
1988
  title: t.sections.properties,
1820
1989
  schema: nodeClass.configSchema,
1821
1990
  t,
1822
- labels: labels.configs
1991
+ labels: labels.configs,
1992
+ unsafeTypes
1823
1993
  });
1824
1994
  if (configSection) lines.push(configSection);
1825
1995
  const credsSection = generateSchemaSection({
1826
1996
  title: t.sections.credentials,
1827
1997
  schema: nodeClass.credentialsSchema,
1828
1998
  t,
1829
- labels: labels.credentials
1999
+ labels: labels.credentials,
2000
+ unsafeTypes
1830
2001
  });
1831
2002
  if (credsSection) lines.push(credsSection);
1832
2003
  if (nodeClass.inputSchema) {
@@ -1835,7 +2006,8 @@ function generateHelpDoc(nodeClass, labels, t) {
1835
2006
  schema: nodeClass.inputSchema,
1836
2007
  t,
1837
2008
  labels: labels.input,
1838
- includeDefault: false
2009
+ includeDefault: false,
2010
+ unsafeTypes
1839
2011
  });
1840
2012
  if (inputSection) lines.push(inputSection);
1841
2013
  }
@@ -1852,7 +2024,8 @@ function generateHelpDoc(nodeClass, labels, t) {
1852
2024
  t,
1853
2025
  labels: portPropLabels,
1854
2026
  heading: "####",
1855
- includeDefault: false
2027
+ includeDefault: false,
2028
+ unsafeTypes
1856
2029
  });
1857
2030
  if (section) portSections.push(section);
1858
2031
  });
@@ -1872,7 +2045,8 @@ ${portSections.join("\n")}`
1872
2045
  t,
1873
2046
  labels: portPropLabels,
1874
2047
  heading: "####",
1875
- includeDefault: false
2048
+ includeDefault: false,
2049
+ unsafeTypes
1876
2050
  });
1877
2051
  if (section) portSections.push(section);
1878
2052
  }
@@ -1889,7 +2063,8 @@ ${portSections.join("\n")}`
1889
2063
  schema: os3,
1890
2064
  t,
1891
2065
  labels: outputPropLabels,
1892
- includeDefault: false
2066
+ includeDefault: false,
2067
+ unsafeTypes
1893
2068
  });
1894
2069
  if (section) lines.push(section);
1895
2070
  }
@@ -1897,26 +2072,26 @@ ${portSections.join("\n")}`
1897
2072
  return lines.join("\n").trim();
1898
2073
  }
1899
2074
  function discoverLanguages(labelsDir, nodeType) {
1900
- const nodeLabelsDir = path7.join(labelsDir, nodeType);
1901
- if (!fs8.existsSync(nodeLabelsDir)) return [];
1902
- return fs8.readdirSync(nodeLabelsDir).filter((f) => f.endsWith(".json")).map((f) => path7.basename(f, ".json"));
2075
+ const nodeLabelsDir = path8.join(labelsDir, nodeType);
2076
+ if (!fs9.existsSync(nodeLabelsDir)) return [];
2077
+ return fs9.readdirSync(nodeLabelsDir).filter((f) => f.endsWith(".json")).map((f) => path8.basename(f, ".json"));
1903
2078
  }
1904
2079
  function helpGenerator(options) {
1905
- const { outDir, localesOutDir, docsDir, labelsDir } = options;
2080
+ const { outDir, localesOutDir, docsDir, labelsDir, srcDir } = options;
1906
2081
  return {
1907
2082
  name: "vite-plugin-node-red:client:help-generator",
1908
2083
  apply: "build",
1909
2084
  enforce: "post",
1910
2085
  async closeBundle() {
1911
- const esmPath = path7.resolve(outDir, "index.mjs");
1912
- const cjsPath = path7.resolve(outDir, "index.js");
2086
+ const esmPath = path8.resolve(outDir, "index.mjs");
2087
+ const cjsPath = path8.resolve(outDir, "index.js");
1913
2088
  let packageFn;
1914
2089
  try {
1915
- if (fs8.existsSync(esmPath)) {
2090
+ if (fs9.existsSync(esmPath)) {
1916
2091
  const fileUrl = pathToFileURL2(esmPath).href + `?t=${Date.now()}`;
1917
2092
  const mod = await import(fileUrl);
1918
2093
  packageFn = mod?.default ?? mod;
1919
- } else if (fs8.existsSync(cjsPath)) {
2094
+ } else if (fs9.existsSync(cjsPath)) {
1920
2095
  const require2 = createRequire2(import.meta.url);
1921
2096
  delete require2.cache[cjsPath];
1922
2097
  const rawMod = require2(cjsPath);
@@ -1926,6 +2101,7 @@ function helpGenerator(options) {
1926
2101
  return;
1927
2102
  }
1928
2103
  const nodeClasses = packageFn?.nodes ?? [];
2104
+ const unsafeTypes = srcDir ? extractUnsafeTypes(srcDir) : void 0;
1929
2105
  const helpByLang = /* @__PURE__ */ new Map();
1930
2106
  for (const NodeClass of nodeClasses) {
1931
2107
  const type = NodeClass.type;
@@ -1933,13 +2109,13 @@ function helpGenerator(options) {
1933
2109
  const languages = discoverLanguages(labelsDir, type);
1934
2110
  if (!languages.includes("en-US")) languages.push("en-US");
1935
2111
  for (const lang of languages) {
1936
- const manualMd = path7.join(docsDir, type, `${lang}.md`);
1937
- const manualHtml = path7.join(docsDir, type, `${lang}.html`);
1938
- if (fs8.existsSync(manualMd) || fs8.existsSync(manualHtml)) continue;
1939
- const labelPath = path7.join(labelsDir, type, `${lang}.json`);
2112
+ const manualMd = path8.join(docsDir, type, `${lang}.md`);
2113
+ const manualHtml = path8.join(docsDir, type, `${lang}.html`);
2114
+ if (fs9.existsSync(manualMd) || fs9.existsSync(manualHtml)) continue;
2115
+ const labelPath = path8.join(labelsDir, type, `${lang}.json`);
1940
2116
  const labels = loadNodeLabels(labelPath);
1941
2117
  const t = getHelpTranslations(lang);
1942
- const content = generateHelpDoc(NodeClass, labels, t);
2118
+ const content = generateHelpDoc(NodeClass, labels, t, unsafeTypes);
1943
2119
  if (!content) continue;
1944
2120
  if (!helpByLang.has(lang)) helpByLang.set(lang, []);
1945
2121
  helpByLang.get(lang).push(
@@ -1950,11 +2126,11 @@ ${content}
1950
2126
  }
1951
2127
  }
1952
2128
  for (const [lang, scripts] of helpByLang) {
1953
- const langDir = path7.join(localesOutDir, lang);
1954
- fs8.mkdirSync(langDir, { recursive: true });
1955
- const indexPath = path7.join(langDir, "index.html");
1956
- const existing = fs8.existsSync(indexPath) ? fs8.readFileSync(indexPath, "utf-8") : "";
1957
- fs8.writeFileSync(
2129
+ const langDir = path8.join(localesOutDir, lang);
2130
+ fs9.mkdirSync(langDir, { recursive: true });
2131
+ const indexPath = path8.join(langDir, "index.html");
2132
+ const existing = fs9.existsSync(indexPath) ? fs9.readFileSync(indexPath, "utf-8") : "";
2133
+ fs9.writeFileSync(
1958
2134
  indexPath,
1959
2135
  existing + (existing ? "\n" : "") + scripts.join("\n"),
1960
2136
  "utf-8"
@@ -1966,8 +2142,8 @@ ${content}
1966
2142
 
1967
2143
  // src/vite/client/plugins/html-generator.ts
1968
2144
  import mime from "mime-types";
1969
- import fs9 from "fs";
1970
- import path8 from "path";
2145
+ import fs10 from "fs";
2146
+ import path9 from "path";
1971
2147
  function htmlGenerator(options) {
1972
2148
  const { packageName, licensePath } = options;
1973
2149
  return {
@@ -1977,7 +2153,7 @@ function htmlGenerator(options) {
1977
2153
  generateBundle(_, bundle) {
1978
2154
  const resourcesTags = Object.keys(bundle).map((fileName) => {
1979
2155
  const asset = bundle[fileName];
1980
- const srcPath = path8.join(
2156
+ const srcPath = path9.join(
1981
2157
  "resources",
1982
2158
  packageName,
1983
2159
  fileName.replace(/^resources\/?/, "")
@@ -2005,8 +2181,8 @@ function htmlGenerator(options) {
2005
2181
  return null;
2006
2182
  }
2007
2183
  }).filter(Boolean).join("\n");
2008
- const licenseBanner = licensePath && fs9.existsSync(licensePath) ? `<!--
2009
- ${fs9.readFileSync(licensePath, "utf-8")}
2184
+ const licenseBanner = licensePath && fs10.existsSync(licensePath) ? `<!--
2185
+ ${fs10.readFileSync(licensePath, "utf-8")}
2010
2186
  -->` : "";
2011
2187
  this.emitFile({
2012
2188
  type: "asset",
@@ -2019,8 +2195,8 @@ ${resourcesTags}`
2019
2195
  }
2020
2196
 
2021
2197
  // src/vite/client/plugins/locales-generator.ts
2022
- import fs10 from "fs";
2023
- import path9 from "path";
2198
+ import fs11 from "fs";
2199
+ import path10 from "path";
2024
2200
  import { merge } from "es-toolkit";
2025
2201
  function localesGenerator(options) {
2026
2202
  const { outDir, docsDir, labelsDir } = options;
@@ -2403,17 +2579,17 @@ Supported: ${languages.join(", ")}`
2403
2579
  }
2404
2580
  function forEachFile(baseDir, fileExtensions, processFile) {
2405
2581
  const langMap = /* @__PURE__ */ new Map();
2406
- if (!fs10.existsSync(baseDir)) return langMap;
2407
- const nodeDirs = fs10.readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory());
2582
+ if (!fs11.existsSync(baseDir)) return langMap;
2583
+ const nodeDirs = fs11.readdirSync(baseDir, { withFileTypes: true }).filter((d) => d.isDirectory());
2408
2584
  for (const nodeDir of nodeDirs) {
2409
2585
  const nodeType = nodeDir.name;
2410
- const nodePath = path9.join(baseDir, nodeType);
2411
- const files = fs10.readdirSync(nodePath);
2586
+ const nodePath = path10.join(baseDir, nodeType);
2587
+ const files = fs11.readdirSync(nodePath);
2412
2588
  for (const file of files) {
2413
- const ext = path9.extname(file);
2589
+ const ext = path10.extname(file);
2414
2590
  if (!fileExtensions.includes(ext)) continue;
2415
- const lang = path9.basename(file, ext);
2416
- const filePath = path9.join(nodePath, file);
2591
+ const lang = path10.basename(file, ext);
2592
+ const filePath = path10.join(nodePath, file);
2417
2593
  validateLanguage(lang, filePath);
2418
2594
  const value = processFile({ ext, filePath, nodeType });
2419
2595
  if (value == null) continue;
@@ -2431,10 +2607,10 @@ Supported: ${languages.join(", ")}`
2431
2607
  }
2432
2608
  function writeOutput(langMap, fileName, serialize) {
2433
2609
  for (const [lang, data] of langMap.entries()) {
2434
- const langOutDir = path9.join(outDir, lang);
2435
- fs10.mkdirSync(langOutDir, { recursive: true });
2436
- fs10.writeFileSync(
2437
- path9.join(langOutDir, fileName),
2610
+ const langOutDir = path10.join(outDir, lang);
2611
+ fs11.mkdirSync(langOutDir, { recursive: true });
2612
+ fs11.writeFileSync(
2613
+ path10.join(langOutDir, fileName),
2438
2614
  serialize(data),
2439
2615
  "utf-8"
2440
2616
  );
@@ -2446,7 +2622,7 @@ Supported: ${languages.join(", ")}`
2446
2622
  ({ ext, filePath, nodeType }) => {
2447
2623
  const type = ext === ".html" ? "text/html" : ext === ".md" ? "text/markdown" : null;
2448
2624
  if (!type) return null;
2449
- const content = fs10.readFileSync(filePath, "utf-8");
2625
+ const content = fs11.readFileSync(filePath, "utf-8");
2450
2626
  return [
2451
2627
  `<script type="${type}" data-help-name="${nodeType}">
2452
2628
  ${content}
@@ -2463,7 +2639,7 @@ ${content}
2463
2639
  labelsDir,
2464
2640
  [".json"],
2465
2641
  ({ filePath, nodeType }) => {
2466
- const parsed = JSON.parse(fs10.readFileSync(filePath, "utf-8"));
2642
+ const parsed = JSON.parse(fs11.readFileSync(filePath, "utf-8"));
2467
2643
  if (parsed[nodeType] && typeof parsed[nodeType] === "object") {
2468
2644
  console.warn(
2469
2645
  `[locales] Warning: "${filePath}" uses nested format (root key "${nodeType}"). Label files should be flat \u2014 the node type is added automatically. See https://bonsaedev.github.io/nrg/guide/building-and-running`
@@ -2513,8 +2689,8 @@ function minifier() {
2513
2689
  // src/vite/client/plugins/node-definitions-inliner.ts
2514
2690
  import { createRequire as createRequire3 } from "module";
2515
2691
  import { pathToFileURL as pathToFileURL3 } from "url";
2516
- import path10 from "path";
2517
- import fs11 from "fs";
2692
+ import path11 from "path";
2693
+ import fs12 from "fs";
2518
2694
  import mime2 from "mime-types";
2519
2695
  var VIRTUAL_ID = "virtual:nrg/node-definitions";
2520
2696
  var RESOLVED_ID = "\0" + VIRTUAL_ID;
@@ -2545,17 +2721,68 @@ function getCredentialsFromSchema(schema) {
2545
2721
  return result;
2546
2722
  }
2547
2723
  function resolveIcon(iconsDir, type) {
2548
- if (!fs11.existsSync(iconsDir)) return void 0;
2549
- return fs11.readdirSync(iconsDir).find((f) => {
2550
- if (path10.basename(f, path10.extname(f)) !== type) return false;
2724
+ if (!fs12.existsSync(iconsDir)) return void 0;
2725
+ return fs12.readdirSync(iconsDir).find((f) => {
2726
+ if (path11.basename(f, path11.extname(f)) !== type) return false;
2551
2727
  const mimeType = mime2.lookup(f);
2552
2728
  return mimeType !== false && mimeType.startsWith("image/");
2553
2729
  });
2554
2730
  }
2555
- function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir, nodesDir, hasUserEntry = true) {
2731
+ var RUNTIME_SPECIFIER = "@bonsae/nrg-runtime/server";
2732
+ var RUNTIME_SPECIFIER_RE = /(['"])@bonsae\/nrg-runtime\/server\1/g;
2733
+ function resolveRuntimeServer(serverOutDir) {
2734
+ const roots = [path11.join(serverOutDir, "index.js"), import.meta.url];
2735
+ for (const root of roots) {
2736
+ try {
2737
+ return createRequire3(root).resolve(RUNTIME_SPECIFIER);
2738
+ } catch {
2739
+ }
2740
+ }
2741
+ return void 0;
2742
+ }
2743
+ async function loadServerPackageExport(serverOutDir) {
2744
+ const esmEntryPath = path11.resolve(serverOutDir, "index.mjs");
2745
+ const cjsEntryPath = path11.resolve(serverOutDir, "index.js");
2746
+ const isEsm = fs12.existsSync(esmEntryPath);
2747
+ const entryPath = isEsm ? esmEntryPath : fs12.existsSync(cjsEntryPath) ? cjsEntryPath : void 0;
2748
+ if (!entryPath) return void 0;
2749
+ const code = fs12.readFileSync(entryPath, "utf-8");
2750
+ const runtimeServer = code.includes(RUNTIME_SPECIFIER) ? resolveRuntimeServer(serverOutDir) : void 0;
2751
+ let tempPath;
2752
+ let loadPath = entryPath;
2753
+ if (runtimeServer) {
2754
+ const replacement = isEsm ? JSON.stringify(pathToFileURL3(runtimeServer).href) : JSON.stringify(runtimeServer);
2755
+ const rewritten = code.replace(RUNTIME_SPECIFIER_RE, replacement);
2756
+ tempPath = path11.resolve(
2757
+ serverOutDir,
2758
+ `.nrg-server-${Date.now()}${isEsm ? ".mjs" : ".cjs"}`
2759
+ );
2760
+ fs12.writeFileSync(tempPath, rewritten);
2761
+ loadPath = tempPath;
2762
+ }
2763
+ const require2 = createRequire3(import.meta.url);
2764
+ try {
2765
+ if (isEsm) {
2766
+ const fileUrl = pathToFileURL3(loadPath).href + `?t=${Date.now()}`;
2767
+ const mod = await import(fileUrl);
2768
+ return mod?.default ?? mod;
2769
+ }
2770
+ delete require2.cache[loadPath];
2771
+ const rawMod = require2(loadPath);
2772
+ return rawMod?.default ?? rawMod;
2773
+ } finally {
2774
+ if (tempPath) {
2775
+ try {
2776
+ delete require2.cache[tempPath];
2777
+ fs12.rmSync(tempPath);
2778
+ } catch {
2779
+ }
2780
+ }
2781
+ }
2782
+ }
2783
+ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir, nodesDir, hasUserEntry = true, cacheDir = path11.resolve("node_modules", ".nrg", "client")) {
2556
2784
  let _nodeTypes = [];
2557
2785
  let _definitions = {};
2558
- const cacheDir = path10.resolve("node_modules", ".nrg", "client");
2559
2786
  return {
2560
2787
  name: "vite-plugin-node-red:client:node-definitions-inliner",
2561
2788
  enforce: "pre",
@@ -2564,19 +2791,7 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
2564
2791
  async buildStart() {
2565
2792
  _nodeTypes = [];
2566
2793
  _definitions = {};
2567
- const esmEntryPath = path10.resolve(serverOutDir, "index.mjs");
2568
- const cjsEntryPath = path10.resolve(serverOutDir, "index.js");
2569
- let packageFn;
2570
- if (fs11.existsSync(esmEntryPath)) {
2571
- const fileUrl = pathToFileURL3(esmEntryPath).href + `?t=${Date.now()}`;
2572
- const mod = await import(fileUrl);
2573
- packageFn = mod?.default ?? mod;
2574
- } else if (fs11.existsSync(cjsEntryPath)) {
2575
- const require2 = createRequire3(import.meta.url);
2576
- delete require2.cache[cjsEntryPath];
2577
- const rawMod = require2(cjsEntryPath);
2578
- packageFn = rawMod?.default ?? rawMod;
2579
- }
2794
+ const packageFn = await loadServerPackageExport(serverOutDir);
2580
2795
  const nodeClasses = packageFn?.nodes ?? [];
2581
2796
  for (const NodeClass of nodeClasses) {
2582
2797
  const type = NodeClass.type;
@@ -2615,14 +2830,14 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
2615
2830
  };
2616
2831
  }
2617
2832
  if (!hasUserEntry) {
2618
- const nodesCache = path10.resolve(cacheDir, "nodes");
2619
- if (fs11.existsSync(nodesCache)) {
2620
- fs11.rmSync(nodesCache, { recursive: true });
2833
+ const nodesCache = path11.resolve(cacheDir, "nodes");
2834
+ if (fs12.existsSync(nodesCache)) {
2835
+ fs12.rmSync(nodesCache, { recursive: true });
2621
2836
  }
2622
- fs11.mkdirSync(nodesCache, { recursive: true });
2837
+ fs12.mkdirSync(nodesCache, { recursive: true });
2623
2838
  for (const type of _nodeTypes) {
2624
- const userTsPath = nodesDir ? path10.resolve(nodesDir, `${type}.ts`) : null;
2625
- if (userTsPath && fs11.existsSync(userTsPath)) continue;
2839
+ const userTsPath = nodesDir ? path11.resolve(nodesDir, `${type}.ts`) : null;
2840
+ if (userTsPath && fs12.existsSync(userTsPath)) continue;
2626
2841
  const content = [
2627
2842
  `// auto-generated by nrg`,
2628
2843
  `import { defineNode } from "@bonsae/nrg/client";`,
@@ -2632,13 +2847,13 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
2632
2847
  `});`,
2633
2848
  ``
2634
2849
  ].join("\n");
2635
- fs11.writeFileSync(path10.resolve(nodesCache, `${type}.ts`), content);
2850
+ fs12.writeFileSync(path11.resolve(nodesCache, `${type}.ts`), content);
2636
2851
  }
2637
2852
  const entryContent = generateEntryCode("");
2638
- fs11.mkdirSync(path10.dirname(path10.resolve(cacheDir, "index.ts")), {
2853
+ fs12.mkdirSync(path11.dirname(path11.resolve(cacheDir, "index.ts")), {
2639
2854
  recursive: true
2640
2855
  });
2641
- fs11.writeFileSync(path10.resolve(cacheDir, "index.ts"), entryContent);
2856
+ fs12.writeFileSync(path11.resolve(cacheDir, "index.ts"), entryContent);
2642
2857
  }
2643
2858
  },
2644
2859
  resolveId(id) {
@@ -2661,12 +2876,12 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
2661
2876
  const nrgImports = /* @__PURE__ */ new Set(["__setSchemas"]);
2662
2877
  const lines = [`import __nrgSchemas from "${VIRTUAL_ID}";`];
2663
2878
  const postLines = [`__setSchemas(__nrgSchemas);`];
2664
- if (componentsDir && fs11.existsSync(componentsDir)) {
2879
+ if (componentsDir && fs12.existsSync(componentsDir)) {
2665
2880
  const formImports = [];
2666
2881
  const formEntries = [];
2667
2882
  for (const type of _nodeTypes) {
2668
- const componentPath = path10.resolve(componentsDir, `${type}.vue`);
2669
- if (fs11.existsSync(componentPath)) {
2883
+ const componentPath = path11.resolve(componentsDir, `${type}.vue`);
2884
+ if (fs12.existsSync(componentPath)) {
2670
2885
  const varName = `__nrgForm_${type.replace(/-/g, "_")}`;
2671
2886
  formImports.push(
2672
2887
  `import ${varName} from ${JSON.stringify(componentPath)};`
@@ -2681,12 +2896,12 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
2681
2896
  }
2682
2897
  }
2683
2898
  if (!hasUserEntry) {
2684
- const nodesCache = path10.resolve(cacheDir, "nodes");
2899
+ const nodesCache = path11.resolve(cacheDir, "nodes");
2685
2900
  const defVarNames = [];
2686
2901
  for (const type of _nodeTypes) {
2687
2902
  const varName = `__nrgNodeDef_${type.replace(/-/g, "_")}`;
2688
- const userTsPath = nodesDir ? path10.resolve(nodesDir, `${type}.ts`) : null;
2689
- const tsPath = userTsPath && fs11.existsSync(userTsPath) ? userTsPath : path10.resolve(nodesCache, `${type}.ts`);
2903
+ const userTsPath = nodesDir ? path11.resolve(nodesDir, `${type}.ts`) : null;
2904
+ const tsPath = userTsPath && fs12.existsSync(userTsPath) ? userTsPath : path11.resolve(nodesCache, `${type}.ts`);
2690
2905
  lines.push(`import ${varName} from ${JSON.stringify(tsPath)};`);
2691
2906
  defVarNames.push(varName);
2692
2907
  }
@@ -2704,8 +2919,8 @@ function nodeDefinitionsInliner(serverOutDir, entryPath, iconsDir, componentsDir
2704
2919
  }
2705
2920
 
2706
2921
  // src/vite/client/plugins/static-copy.ts
2707
- import fs12 from "fs";
2708
- import path11 from "path";
2922
+ import fs13 from "fs";
2923
+ import path12 from "path";
2709
2924
  function staticCopy(options) {
2710
2925
  const { targets } = options;
2711
2926
  return {
@@ -2714,23 +2929,23 @@ function staticCopy(options) {
2714
2929
  enforce: "post",
2715
2930
  closeBundle() {
2716
2931
  for (const { src, dest } of targets) {
2717
- if (!fs12.existsSync(src)) continue;
2718
- fs12.mkdirSync(dest, { recursive: true });
2719
- const stat = fs12.statSync(src);
2932
+ if (!fs13.existsSync(src)) continue;
2933
+ fs13.mkdirSync(dest, { recursive: true });
2934
+ const stat = fs13.statSync(src);
2720
2935
  if (stat.isDirectory()) {
2721
- const files = fs12.readdirSync(src);
2936
+ const files = fs13.readdirSync(src);
2722
2937
  for (const file of files) {
2723
- const srcFile = path11.join(src, file);
2724
- const destFile = path11.join(dest, file);
2725
- const fileStat = fs12.statSync(srcFile);
2938
+ const srcFile = path12.join(src, file);
2939
+ const destFile = path12.join(dest, file);
2940
+ const fileStat = fs13.statSync(srcFile);
2726
2941
  if (fileStat.isDirectory()) {
2727
- fs12.cpSync(srcFile, destFile, { recursive: true });
2942
+ fs13.cpSync(srcFile, destFile, { recursive: true });
2728
2943
  } else {
2729
- fs12.copyFileSync(srcFile, destFile);
2944
+ fs13.copyFileSync(srcFile, destFile);
2730
2945
  }
2731
2946
  }
2732
2947
  } else {
2733
- fs12.copyFileSync(src, dest);
2948
+ fs13.copyFileSync(src, dest);
2734
2949
  }
2735
2950
  }
2736
2951
  }
@@ -2738,6 +2953,11 @@ function staticCopy(options) {
2738
2953
  }
2739
2954
 
2740
2955
  // src/vite/client/build.ts
2956
+ function cacheKeyFor(outDir) {
2957
+ const abs = path13.resolve(outDir);
2958
+ const hash = crypto.createHash("sha1").update(abs).digest("hex").slice(0, 8);
2959
+ return `${path13.basename(abs) || "client"}-${hash}`;
2960
+ }
2741
2961
  async function build2(clientBuildOptions, buildContext) {
2742
2962
  const {
2743
2963
  srcDir = "./client",
@@ -2751,74 +2971,81 @@ async function build2(clientBuildOptions, buildContext) {
2751
2971
  globals = {},
2752
2972
  manualChunks
2753
2973
  } = clientBuildOptions;
2754
- const physicalEntryPath = path12.resolve(srcDir, entry);
2974
+ const cacheDir = path13.resolve(
2975
+ "node_modules",
2976
+ ".nrg",
2977
+ "client",
2978
+ cacheKeyFor(buildContext.outDir)
2979
+ );
2980
+ const physicalEntryPath = path13.resolve(srcDir, entry);
2755
2981
  let entryPath;
2756
2982
  let generatedEntry = false;
2757
- if (fs13.existsSync(physicalEntryPath)) {
2983
+ if (fs14.existsSync(physicalEntryPath)) {
2758
2984
  entryPath = physicalEntryPath;
2759
2985
  } else {
2760
- const cacheDir = path12.resolve("node_modules", ".nrg", "client");
2761
- const cachedEntryPath = path12.resolve(cacheDir, entry);
2762
- if (!fs13.existsSync(cacheDir)) {
2763
- fs13.mkdirSync(cacheDir, { recursive: true });
2986
+ const cachedEntryPath = path13.resolve(cacheDir, entry);
2987
+ if (!fs14.existsSync(cacheDir)) {
2988
+ fs14.mkdirSync(cacheDir, { recursive: true });
2764
2989
  }
2765
- fs13.writeFileSync(cachedEntryPath, "// auto-generated entry\n");
2990
+ fs14.writeFileSync(cachedEntryPath, "// auto-generated entry\n");
2766
2991
  entryPath = cachedEntryPath;
2767
2992
  generatedEntry = true;
2768
2993
  }
2769
- const iconsDir = path12.resolve(
2770
- staticDirs.icons ?? path12.join(path12.dirname(path12.resolve(srcDir)), "icons")
2994
+ const iconsDir = path13.resolve(
2995
+ staticDirs.icons ?? path13.join(path13.dirname(path13.resolve(srcDir)), "icons")
2771
2996
  );
2772
2997
  const plugins = [
2773
2998
  vue(),
2774
2999
  nodeDefinitionsInliner(
2775
3000
  buildContext.outDir,
2776
3001
  entryPath,
2777
- fs13.existsSync(iconsDir) ? iconsDir : void 0,
2778
- path12.resolve(srcDir, "components"),
2779
- path12.resolve(srcDir, "nodes"),
2780
- !generatedEntry
3002
+ fs14.existsSync(iconsDir) ? iconsDir : void 0,
3003
+ path13.resolve(srcDir, "components"),
3004
+ path13.resolve(srcDir, "nodes"),
3005
+ !generatedEntry,
3006
+ cacheDir
2781
3007
  )
2782
3008
  ];
2783
3009
  plugins.push(
2784
3010
  htmlGenerator({
2785
3011
  packageName: buildContext.packageName,
2786
- licensePath: licensePath ? path12.resolve(licensePath) : void 0
3012
+ licensePath: licensePath ? path13.resolve(licensePath) : void 0
2787
3013
  })
2788
3014
  );
2789
3015
  if (locales) {
2790
3016
  const { docsDir = "./locales/docs", labelsDir = "./locales/labels" } = locales;
2791
- const localesOutDir = path12.join(buildContext.outDir, "locales");
3017
+ const localesOutDir = path13.join(buildContext.outDir, "locales");
2792
3018
  plugins.push(
2793
3019
  localesGenerator({
2794
3020
  outDir: localesOutDir,
2795
- docsDir: path12.resolve(docsDir),
2796
- labelsDir: path12.resolve(labelsDir)
3021
+ docsDir: path13.resolve(docsDir),
3022
+ labelsDir: path13.resolve(labelsDir)
2797
3023
  })
2798
3024
  );
2799
3025
  plugins.push(
2800
3026
  helpGenerator({
2801
3027
  outDir: buildContext.outDir,
2802
3028
  localesOutDir,
2803
- docsDir: path12.resolve(docsDir),
2804
- labelsDir: path12.resolve(labelsDir)
3029
+ docsDir: path13.resolve(docsDir),
3030
+ labelsDir: path13.resolve(labelsDir),
3031
+ srcDir: buildContext.serverSrcDir
2805
3032
  })
2806
3033
  );
2807
3034
  }
2808
3035
  const copyTargets = [];
2809
- const publicDir = path12.resolve(
2810
- staticDirs.public ?? path12.join(srcDir, "public")
3036
+ const publicDir = path13.resolve(
3037
+ staticDirs.public ?? path13.join(srcDir, "public")
2811
3038
  );
2812
- if (fs13.existsSync(publicDir)) {
3039
+ if (fs14.existsSync(publicDir)) {
2813
3040
  copyTargets.push({
2814
3041
  src: publicDir,
2815
- dest: path12.join(buildContext.outDir, "resources")
3042
+ dest: path13.join(buildContext.outDir, "resources")
2816
3043
  });
2817
3044
  }
2818
- if (fs13.existsSync(iconsDir)) {
3045
+ if (fs14.existsSync(iconsDir)) {
2819
3046
  copyTargets.push({
2820
3047
  src: iconsDir,
2821
- dest: path12.join(buildContext.outDir, "icons")
3048
+ dest: path13.join(buildContext.outDir, "icons")
2822
3049
  });
2823
3050
  }
2824
3051
  if (copyTargets.length > 0) {
@@ -2846,10 +3073,10 @@ async function build2(clientBuildOptions, buildContext) {
2846
3073
  configFile: false,
2847
3074
  logLevel: "warn",
2848
3075
  base: `/resources/${buildContext.packageName}`,
2849
- publicDir: path12.resolve(srcDir, "public"),
3076
+ publicDir: path13.resolve(srcDir, "public"),
2850
3077
  resolve: {
2851
3078
  alias: {
2852
- "@": path12.resolve(srcDir)
3079
+ "@": path13.resolve(srcDir)
2853
3080
  }
2854
3081
  },
2855
3082
  plugins,
@@ -2903,8 +3130,8 @@ async function build2(clientBuildOptions, buildContext) {
2903
3130
  throw new BuildError("client", error);
2904
3131
  } finally {
2905
3132
  if (generatedEntry) {
2906
- if (fs13.existsSync(entryPath)) {
2907
- fs13.unlinkSync(entryPath);
3133
+ if (fs14.existsSync(entryPath)) {
3134
+ fs14.unlinkSync(entryPath);
2908
3135
  }
2909
3136
  }
2910
3137
  }
@@ -3063,20 +3290,20 @@ function serverPlugin(options) {
3063
3290
  });
3064
3291
  logger.startGroup("Node-RED");
3065
3292
  nodeRedLauncher.flushLogs();
3066
- const serverSrcDir = path13.resolve(
3293
+ const serverSrcDir = path14.resolve(
3067
3294
  serverBuildOptions.srcDir ?? "./server"
3068
3295
  );
3069
- const clientSrcDir = path13.resolve(
3296
+ const clientSrcDir = path14.resolve(
3070
3297
  clientBuildOptions.srcDir ?? "./client"
3071
3298
  );
3072
- const localesDocsDir = path13.resolve(
3299
+ const localesDocsDir = path14.resolve(
3073
3300
  clientBuildOptions.locales?.docsDir ?? "./locales/docs"
3074
3301
  );
3075
- const localesLabelsDir = path13.resolve(
3302
+ const localesLabelsDir = path14.resolve(
3076
3303
  clientBuildOptions.locales?.labelsDir ?? "./locales/labels"
3077
3304
  );
3078
- const iconsDir = path13.resolve(
3079
- clientBuildOptions.staticDirs?.icons ?? path13.join(path13.dirname(clientSrcDir), "icons")
3305
+ const iconsDir = path14.resolve(
3306
+ clientBuildOptions.staticDirs?.icons ?? path14.join(path14.dirname(clientSrcDir), "icons")
3080
3307
  );
3081
3308
  const watchPaths = [
3082
3309
  serverSrcDir,
@@ -3096,7 +3323,7 @@ function serverPlugin(options) {
3096
3323
  );
3097
3324
  const handleFileChange = (file, event) => {
3098
3325
  if (!initialStartDone) return;
3099
- logger.info(`${event}: ${path13.relative(process.cwd(), file)}`);
3326
+ logger.info(`${event}: ${path14.relative(process.cwd(), file)}`);
3100
3327
  debounceBeforeStart();
3101
3328
  };
3102
3329
  watcher.on("change", (file) => handleFileChange(file, "Changed"));
@@ -3190,11 +3417,12 @@ function nrg(options = {}) {
3190
3417
  server.nodeRed
3191
3418
  );
3192
3419
  const extraFilesCopyTargets = build3.extraFilesCopyTargets ?? DEFAULT_EXTRA_FILES_COPY_TARGETS;
3193
- const resolvedOutDir = path14.resolve(outDir);
3420
+ const resolvedOutDir = path15.resolve(outDir);
3194
3421
  const buildContext = {
3195
3422
  outDir: resolvedOutDir,
3196
3423
  packageName: getPackageName(),
3197
- isDev: process.env.NODE_ENV === "development"
3424
+ isDev: process.env.NODE_ENV === "development",
3425
+ serverSrcDir: path15.resolve(serverBuildOptions.srcDir ?? "./server")
3198
3426
  };
3199
3427
  const nodeRedLauncher = new NodeRedLauncher(
3200
3428
  resolvedOutDir,