@bonsae/nrg-runtime 0.36.0 → 0.37.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.
package/index.cjs CHANGED
@@ -292,7 +292,7 @@ function initValidator(RED) {
292
292
  var import_node_path = __toESM(require("node:path"), 1);
293
293
  var import_node_fs = __toESM(require("node:fs"), 1);
294
294
  var import_node_module = require("node:module");
295
- var CLIENT_ASSET = true ? "nrg.0682c337.js" : "nrg.js";
295
+ var CLIENT_ASSET = true ? "nrg.8ad0ea04.js" : "nrg.js";
296
296
  function serveFile(filePath) {
297
297
  return (_req, res, next) => {
298
298
  if (!import_node_fs.default.existsSync(filePath)) return next();
@@ -792,6 +792,15 @@ function setupContext(context, store) {
792
792
  var import_node_async_hooks = require("node:async_hooks");
793
793
  var RETURN_PROPERTY_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
794
794
  var INPUT_KEY = "input";
795
+ var SOURCE_KEY = "source";
796
+ var RESERVED_RETURN_PROPERTIES = /* @__PURE__ */ new Set([
797
+ SOURCE_KEY,
798
+ INPUT_KEY,
799
+ "error",
800
+ "complete",
801
+ "status",
802
+ "_msgid"
803
+ ]);
795
804
  var IONode = class _IONode extends Node {
796
805
  static align;
797
806
  /**
@@ -846,11 +855,18 @@ var IONode = class _IONode extends Node {
846
855
  const outputReturnProperties = this.config.outputReturnProperties;
847
856
  if (outputReturnProperties) {
848
857
  for (const [port, key] of Object.entries(outputReturnProperties)) {
849
- if (typeof key === "string" && key.trim() && !RETURN_PROPERTY_PATTERN.test(key.trim())) {
858
+ const trimmed = typeof key === "string" ? key.trim() : "";
859
+ if (!trimmed) continue;
860
+ if (!RETURN_PROPERTY_PATTERN.test(trimmed)) {
850
861
  throw new NrgError(
851
862
  `Invalid return property "${key}" for output port ${port} in ${this.constructor.type} \u2014 it must be a valid JavaScript identifier (letters, digits, _, $; not starting with a digit)`
852
863
  );
853
864
  }
865
+ if (RESERVED_RETURN_PROPERTIES.has(trimmed)) {
866
+ throw new NrgError(
867
+ `Reserved return property "${trimmed}" for output port ${port} in ${this.constructor.type} \u2014 the framework owns this key on the outgoing message (source/input provenance and the built-in ports). Choose another name.`
868
+ );
869
+ }
854
870
  }
855
871
  }
856
872
  }
@@ -874,11 +890,8 @@ var IONode = class _IONode extends Node {
874
890
  const result = await this.#input(msg, store);
875
891
  if (this.config.completePort) {
876
892
  this.#sendToPort("complete", {
877
- ...msg,
878
- ...result !== void 0 ? { output: result } : {},
879
- complete: {
880
- source: this.#nodeSource()
881
- },
893
+ ...result !== void 0 ? { complete: result } : {},
894
+ [SOURCE_KEY]: this.#nodeSource(),
882
895
  [INPUT_KEY]: msg
883
896
  });
884
897
  }
@@ -886,31 +899,29 @@ var IONode = class _IONode extends Node {
886
899
  this.node.log("Input processed");
887
900
  } catch (error) {
888
901
  const errorMsg = error instanceof Error ? error.message : "Unknown error during input handling";
889
- if (this.config.errorPort && !store.errorEmitted) {
890
- const errorData = error && typeof error === "object" ? { ...error } : {};
891
- this.#sendToPort("error", {
892
- ...msg,
893
- error: {
894
- ...errorData,
895
- name: error?.name ?? "Error",
896
- message: errorMsg,
897
- source: this.#nodeSource()
898
- },
899
- [INPUT_KEY]: msg
900
- });
901
- }
902
- if (error instanceof Error) {
903
- this.node.error(
904
- "Error while processing input: " + error.message,
905
- msg
906
- );
907
- done(error);
902
+ const err = error instanceof Error ? error : new Error(errorMsg);
903
+ if (!(error instanceof NrgError) && this.config.errorPort) {
904
+ if (!store.errorEmitted) {
905
+ const errorData = error && typeof error === "object" ? { ...error } : {};
906
+ for (const k of Object.keys(errorData)) {
907
+ if (errorData[k] === void 0) delete errorData[k];
908
+ }
909
+ const stack = error instanceof Error ? error.stack : void 0;
910
+ this.#sendToPort("error", {
911
+ error: {
912
+ ...errorData,
913
+ name: error?.name ?? "Error",
914
+ message: errorMsg,
915
+ ...stack ? { stack } : {}
916
+ },
917
+ [SOURCE_KEY]: this.#nodeSource(),
918
+ [INPUT_KEY]: msg
919
+ });
920
+ this.node.error(errorMsg);
921
+ }
922
+ done();
908
923
  } else {
909
- this.node.error(
910
- "Unknown error occurred during input handling",
911
- msg
912
- );
913
- done(new Error(errorMsg));
924
+ done(err);
914
925
  }
915
926
  }
916
927
  }
@@ -1046,13 +1057,27 @@ var IONode = class _IONode extends Node {
1046
1057
  #wrapOutgoing(value, mode, port) {
1047
1058
  const key = this.#returnPropertyKey(port);
1048
1059
  const input = _IONode.#invocation.getStore()?.inputMsg ?? {};
1049
- if (mode === "reset") {
1050
- return { [key]: value };
1051
- }
1052
- if (mode === "trace") {
1053
- return { ...input, [key]: value, [INPUT_KEY]: input };
1054
- }
1055
- return { ...input, [key]: value };
1060
+ const source = this.#outputSource(port);
1061
+ const frame = (msg) => Object.keys(msg).length ? { [key]: value, [SOURCE_KEY]: source, [INPUT_KEY]: msg } : { [key]: value, [SOURCE_KEY]: source };
1062
+ if (mode === "reset") return { [key]: value, [SOURCE_KEY]: source };
1063
+ if (mode === "trace") return frame(input);
1064
+ const lastOnly = { ...input };
1065
+ delete lastOnly[INPUT_KEY];
1066
+ return frame(lastOnly);
1067
+ }
1068
+ /**
1069
+ * Provenance stamped on every data-port output under `msg.source`: the
1070
+ * producing node plus the port the message was sent on (with the named-port
1071
+ * name when the node declares a `Port<T>` record). Message metadata, like
1072
+ * `_msgid` — never part of the typed result.
1073
+ */
1074
+ #outputSource(port) {
1075
+ const portName = this.#namedPortKeys()?.[port];
1076
+ return {
1077
+ ...this.#nodeSource(),
1078
+ port,
1079
+ ...portName ? { portName } : {}
1080
+ };
1056
1081
  }
1057
1082
  // --- Built-in port management ---
1058
1083
  // Private, override-proof accessors the framework reads for port routing. A
@@ -1165,21 +1190,22 @@ var IONode = class _IONode extends Node {
1165
1190
  });
1166
1191
  }
1167
1192
  error(message, msg) {
1168
- super.error(message, msg);
1169
1193
  if (msg && this.config.errorPort) {
1170
1194
  this.#sendToPort("error", {
1171
- ...msg,
1172
1195
  error: {
1173
- // `name` keeps the error-port payload Catch-node compatible and
1174
- // consistent with the auto-emit from a thrown error.
1196
+ // `name` keeps the error-port payload consistent with the auto-emit
1197
+ // from a thrown error.
1175
1198
  name: "Error",
1176
- message,
1177
- source: this.#nodeSource()
1199
+ message
1178
1200
  },
1201
+ [SOURCE_KEY]: this.#nodeSource(),
1179
1202
  [INPUT_KEY]: msg
1180
1203
  });
1204
+ super.error(message);
1181
1205
  const store = _IONode.#invocation.getStore();
1182
1206
  if (store) store.errorEmitted = true;
1207
+ } else {
1208
+ super.error(message, msg);
1183
1209
  }
1184
1210
  }
1185
1211
  updateWires(wires) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonsae/nrg-runtime",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "description": "The runtime for Node-RED nodes built with @bonsae/nrg — node base classes, schemas, AJV validator, and the editor client. Carries no build tooling.",
5
5
  "author": "Allan Oricil <allanoricil@duck.com>",
6
6
  "license": "MIT",