@bian-womp/spark-graph 0.3.71 → 0.3.73

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 (83) hide show
  1. package/lib/cjs/index.cjs +163 -268
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/builder/GraphBuilder.d.ts.map +1 -1
  4. package/lib/cjs/src/builder/Registry.d.ts.map +1 -1
  5. package/lib/cjs/src/core/categories.d.ts.map +1 -1
  6. package/lib/cjs/src/core/order.d.ts.map +1 -1
  7. package/lib/cjs/src/core/type-utils.d.ts.map +1 -1
  8. package/lib/cjs/src/core/types.d.ts.map +1 -1
  9. package/lib/cjs/src/examples/arrays.d.ts.map +1 -1
  10. package/lib/cjs/src/examples/async.d.ts.map +1 -1
  11. package/lib/cjs/src/examples/progress.d.ts.map +1 -1
  12. package/lib/cjs/src/examples/runMode.d.ts.map +1 -1
  13. package/lib/cjs/src/examples/shared.d.ts.map +1 -1
  14. package/lib/cjs/src/examples/simple.d.ts.map +1 -1
  15. package/lib/cjs/src/examples/snapshot.d.ts.map +1 -1
  16. package/lib/cjs/src/index.d.ts +7 -7
  17. package/lib/cjs/src/index.d.ts.map +1 -1
  18. package/lib/cjs/src/misc/base.d.ts.map +1 -1
  19. package/lib/cjs/src/misc/utils/LevelLogger.d.ts.map +1 -1
  20. package/lib/cjs/src/misc/utils/json.d.ts.map +1 -1
  21. package/lib/cjs/src/misc/utils/merge.d.ts.map +1 -1
  22. package/lib/cjs/src/plugins/composite.d.ts.map +1 -1
  23. package/lib/cjs/src/runtime/Engine.d.ts.map +1 -1
  24. package/lib/cjs/src/runtime/GraphLifecycleApi.d.ts.map +1 -1
  25. package/lib/cjs/src/runtime/GraphRuntime.d.ts +7 -6
  26. package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
  27. package/lib/cjs/src/runtime/LocalEngine.d.ts.map +1 -1
  28. package/lib/cjs/src/runtime/components/EdgePropagator.d.ts +2 -7
  29. package/lib/cjs/src/runtime/components/EdgePropagator.d.ts.map +1 -1
  30. package/lib/cjs/src/runtime/components/EventEmitter.d.ts.map +1 -1
  31. package/lib/cjs/src/runtime/components/Graph.d.ts.map +1 -1
  32. package/lib/cjs/src/runtime/components/HandleResolver.d.ts.map +1 -1
  33. package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  34. package/lib/cjs/src/runtime/components/RunContextManager.d.ts.map +1 -1
  35. package/lib/cjs/src/runtime/components/RuntimeValidatorManager.d.ts.map +1 -1
  36. package/lib/cjs/src/runtime/components/graph-utils.d.ts.map +1 -1
  37. package/lib/cjs/src/runtime/components/interfaces.d.ts +2 -1
  38. package/lib/cjs/src/runtime/components/interfaces.d.ts.map +1 -1
  39. package/lib/cjs/src/runtime/components/types.d.ts.map +1 -1
  40. package/lib/cjs/src/runtime/utils.d.ts +9 -0
  41. package/lib/cjs/src/runtime/utils.d.ts.map +1 -1
  42. package/lib/esm/index.js +163 -268
  43. package/lib/esm/index.js.map +1 -1
  44. package/lib/esm/src/builder/GraphBuilder.d.ts.map +1 -1
  45. package/lib/esm/src/builder/Registry.d.ts.map +1 -1
  46. package/lib/esm/src/core/categories.d.ts.map +1 -1
  47. package/lib/esm/src/core/order.d.ts.map +1 -1
  48. package/lib/esm/src/core/type-utils.d.ts.map +1 -1
  49. package/lib/esm/src/core/types.d.ts.map +1 -1
  50. package/lib/esm/src/examples/arrays.d.ts.map +1 -1
  51. package/lib/esm/src/examples/async.d.ts.map +1 -1
  52. package/lib/esm/src/examples/progress.d.ts.map +1 -1
  53. package/lib/esm/src/examples/runMode.d.ts.map +1 -1
  54. package/lib/esm/src/examples/shared.d.ts.map +1 -1
  55. package/lib/esm/src/examples/simple.d.ts.map +1 -1
  56. package/lib/esm/src/examples/snapshot.d.ts.map +1 -1
  57. package/lib/esm/src/index.d.ts +7 -7
  58. package/lib/esm/src/index.d.ts.map +1 -1
  59. package/lib/esm/src/misc/base.d.ts.map +1 -1
  60. package/lib/esm/src/misc/utils/LevelLogger.d.ts.map +1 -1
  61. package/lib/esm/src/misc/utils/json.d.ts.map +1 -1
  62. package/lib/esm/src/misc/utils/merge.d.ts.map +1 -1
  63. package/lib/esm/src/plugins/composite.d.ts.map +1 -1
  64. package/lib/esm/src/runtime/Engine.d.ts.map +1 -1
  65. package/lib/esm/src/runtime/GraphLifecycleApi.d.ts.map +1 -1
  66. package/lib/esm/src/runtime/GraphRuntime.d.ts +7 -6
  67. package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
  68. package/lib/esm/src/runtime/LocalEngine.d.ts.map +1 -1
  69. package/lib/esm/src/runtime/components/EdgePropagator.d.ts +2 -7
  70. package/lib/esm/src/runtime/components/EdgePropagator.d.ts.map +1 -1
  71. package/lib/esm/src/runtime/components/EventEmitter.d.ts.map +1 -1
  72. package/lib/esm/src/runtime/components/Graph.d.ts.map +1 -1
  73. package/lib/esm/src/runtime/components/HandleResolver.d.ts.map +1 -1
  74. package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  75. package/lib/esm/src/runtime/components/RunContextManager.d.ts.map +1 -1
  76. package/lib/esm/src/runtime/components/RuntimeValidatorManager.d.ts.map +1 -1
  77. package/lib/esm/src/runtime/components/graph-utils.d.ts.map +1 -1
  78. package/lib/esm/src/runtime/components/interfaces.d.ts +2 -1
  79. package/lib/esm/src/runtime/components/interfaces.d.ts.map +1 -1
  80. package/lib/esm/src/runtime/components/types.d.ts.map +1 -1
  81. package/lib/esm/src/runtime/utils.d.ts +9 -0
  82. package/lib/esm/src/runtime/utils.d.ts.map +1 -1
  83. package/package.json +4 -2
package/lib/esm/index.js CHANGED
@@ -2,9 +2,7 @@ function typed(typeId, value) {
2
2
  return { __spark_type: typeId, __spark_value: value };
3
3
  }
4
4
  function isTyped(v) {
5
- return (!!v &&
6
- typeof v === "object" &&
7
- Object.prototype.hasOwnProperty.call(v, "__spark_type"));
5
+ return !!v && typeof v === "object" && Object.prototype.hasOwnProperty.call(v, "__spark_type");
8
6
  }
9
7
  function unwrapTypeId(v) {
10
8
  if (isTyped(v))
@@ -64,12 +62,8 @@ function mergeInputHandleDescriptors(staticDesc, dynamicDesc) {
64
62
  (typeof dynamicDesc === "string" || Array.isArray(dynamicDesc))) {
65
63
  return dynamicDesc;
66
64
  }
67
- const staticObj = typeof staticDesc === "string" || Array.isArray(staticDesc)
68
- ? { typeId: staticDesc }
69
- : staticDesc;
70
- const dynamicObj = typeof dynamicDesc === "string" || Array.isArray(dynamicDesc)
71
- ? { typeId: dynamicDesc }
72
- : dynamicDesc;
65
+ const staticObj = typeof staticDesc === "string" || Array.isArray(staticDesc) ? { typeId: staticDesc } : staticDesc;
66
+ const dynamicObj = typeof dynamicDesc === "string" || Array.isArray(dynamicDesc) ? { typeId: dynamicDesc } : dynamicDesc;
73
67
  // Merge: dynamic takes precedence, but merge metadata objects
74
68
  const merged = {
75
69
  typeId: dynamicObj.typeId ?? staticObj.typeId,
@@ -150,7 +144,7 @@ class Registry {
150
144
  };
151
145
  this.types.set(arrayId, arrayDesc);
152
146
  }
153
- this.registerCoercion(desc.id, arrayId, (value) => Array.isArray(value) ? value : [value]);
147
+ this.registerCoercion(desc.id, arrayId, (value) => (Array.isArray(value) ? value : [value]));
154
148
  this.registerCoercion(arrayId, desc.id, (value) => {
155
149
  if (!Array.isArray(value))
156
150
  return value;
@@ -176,8 +170,8 @@ class Registry {
176
170
  const arrayId = `${typeId}[]`;
177
171
  if (this.types.has(arrayId) && !this.serializers.has(arrayId)) {
178
172
  this.serializers.set(arrayId, {
179
- serialize: (value) => Array.isArray(value) ? value.map((el) => s.serialize(el)) : value,
180
- deserialize: (data) => Array.isArray(data) ? data.map((el) => s.deserialize(el)) : data,
173
+ serialize: (value) => (Array.isArray(value) ? value.map((el) => s.serialize(el)) : value),
174
+ deserialize: (data) => (Array.isArray(data) ? data.map((el) => s.deserialize(el)) : data),
181
175
  });
182
176
  }
183
177
  return this;
@@ -353,8 +347,7 @@ class Registry {
353
347
  let i = 0;
354
348
  while (i < queue.length) {
355
349
  const q = queue[i];
356
- if (e.cost.edges < q.cost.edges ||
357
- (e.cost.edges === q.cost.edges && e.cost.async < q.cost.async)) {
350
+ if (e.cost.edges < q.cost.edges || (e.cost.edges === q.cost.edges && e.cost.async < q.cost.async)) {
358
351
  break;
359
352
  }
360
353
  i++;
@@ -473,9 +466,7 @@ class Registry {
473
466
  const rec = this.enums.get(id);
474
467
  if (!rec)
475
468
  return value;
476
- return rec.valueToLabel.has(n)
477
- ? n
478
- : Array.from(rec.valueToLabel.keys())[0] ?? 0;
469
+ return rec.valueToLabel.has(n) ? n : (Array.from(rec.valueToLabel.keys())[0] ?? 0);
479
470
  });
480
471
  this.registerCoercion(id, labelType, (value) => {
481
472
  const n = Number(value);
@@ -922,8 +913,7 @@ class Graph {
922
913
  if (inbound.length === 0)
923
914
  return true;
924
915
  for (const e of inbound) {
925
- if (node.resolvedHandles?.inputs?.[e.target.handle] &&
926
- !node.inputs[e.target.handle]) {
916
+ if (node.resolvedHandles?.inputs?.[e.target.handle] && !node.inputs[e.target.handle]) {
927
917
  return false;
928
918
  }
929
919
  }
@@ -1001,9 +991,7 @@ function getValueAtPath(obj, pathSegments) {
1001
991
  let current = obj;
1002
992
  for (let i = 0; i < pathSegments.length - 1; i++) {
1003
993
  const segment = pathSegments[i];
1004
- if (current === null ||
1005
- current === undefined ||
1006
- typeof current !== "object") {
994
+ if (current === null || current === undefined || typeof current !== "object") {
1007
995
  return null;
1008
996
  }
1009
997
  if (typeof segment === "string") {
@@ -1052,9 +1040,7 @@ function getValueAtPath(obj, pathSegments) {
1052
1040
  return null;
1053
1041
  return { value: current[index], parent: current, key: index };
1054
1042
  }
1055
- else if (current !== null &&
1056
- current !== undefined &&
1057
- typeof current === "object") {
1043
+ else if (current !== null && current !== undefined && typeof current === "object") {
1058
1044
  return {
1059
1045
  value: current[lastSegment],
1060
1046
  parent: current,
@@ -1168,29 +1154,20 @@ function findMatchingPaths(obj, pathSegments, currentPath = []) {
1168
1154
  if (Array.isArray(obj)) {
1169
1155
  const index = parseInt(currentSegment, 10);
1170
1156
  if (!isNaN(index) && index >= 0 && index < obj.length) {
1171
- results.push(...findMatchingPaths(obj[index], remainingSegments, [
1172
- ...currentPath,
1173
- index,
1174
- ]));
1157
+ results.push(...findMatchingPaths(obj[index], remainingSegments, [...currentPath, index]));
1175
1158
  }
1176
1159
  }
1177
1160
  else if (obj !== null && obj !== undefined && typeof obj === "object") {
1178
1161
  const objRecord = obj;
1179
1162
  if (currentSegment in objRecord) {
1180
- results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [
1181
- ...currentPath,
1182
- currentSegment,
1183
- ]));
1163
+ results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [...currentPath, currentSegment]));
1184
1164
  }
1185
1165
  }
1186
1166
  }
1187
1167
  else if (typeof currentSegment === "number") {
1188
1168
  if (Array.isArray(obj)) {
1189
1169
  if (currentSegment >= 0 && currentSegment < obj.length) {
1190
- results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [
1191
- ...currentPath,
1192
- currentSegment,
1193
- ]));
1170
+ results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [...currentPath, currentSegment]));
1194
1171
  }
1195
1172
  }
1196
1173
  }
@@ -1204,10 +1181,7 @@ function findMatchingPaths(obj, pathSegments, currentPath = []) {
1204
1181
  const objRecord = obj;
1205
1182
  for (const key of Object.keys(objRecord)) {
1206
1183
  if (currentSegment.test(key)) {
1207
- results.push(...findMatchingPaths(objRecord[key], remainingSegments, [
1208
- ...currentPath,
1209
- key,
1210
- ]));
1184
+ results.push(...findMatchingPaths(objRecord[key], remainingSegments, [...currentPath, key]));
1211
1185
  }
1212
1186
  }
1213
1187
  }
@@ -1218,12 +1192,8 @@ function stringifyJson(obj, oneLiner) {
1218
1192
  // No formatting requested: behave exactly like native JSON.stringify.
1219
1193
  if (!oneLiner)
1220
1194
  return JSON.stringify(obj);
1221
- const indentSize = Number.isFinite(oneLiner.indent)
1222
- ? Math.max(0, Math.floor(oneLiner.indent))
1223
- : 2;
1224
- const maxDepth = typeof oneLiner.maxDepth === "number" && Number.isFinite(oneLiner.maxDepth)
1225
- ? oneLiner.maxDepth
1226
- : undefined;
1195
+ const indentSize = Number.isFinite(oneLiner.indent) ? Math.max(0, Math.floor(oneLiner.indent)) : 2;
1196
+ const maxDepth = typeof oneLiner.maxDepth === "number" && Number.isFinite(oneLiner.maxDepth) ? oneLiner.maxDepth : undefined;
1227
1197
  const patterns = (oneLiner.paths || []).filter(Boolean);
1228
1198
  // Preserve JSON.stringify semantics for things like toJSON(), dropping functions/undefined, etc.
1229
1199
  // Note: this still throws on circular structures (same as JSON.stringify).
@@ -1426,22 +1396,13 @@ function compilePathMatcher(pattern) {
1426
1396
  res = ti < pathTokens.length && go(pi + 1, ti + 1);
1427
1397
  }
1428
1398
  else if (p === "#") {
1429
- res =
1430
- ti < pathTokens.length &&
1431
- isNumericSegment(pathTokens[ti]) &&
1432
- go(pi + 1, ti + 1);
1399
+ res = ti < pathTokens.length && isNumericSegment(pathTokens[ti]) && go(pi + 1, ti + 1);
1433
1400
  }
1434
1401
  else if (p === "[*]") {
1435
- res =
1436
- ti < pathTokens.length &&
1437
- isIndex(pathTokens[ti]) &&
1438
- go(pi + 1, ti + 1);
1402
+ res = ti < pathTokens.length && isIndex(pathTokens[ti]) && go(pi + 1, ti + 1);
1439
1403
  }
1440
1404
  else {
1441
- res =
1442
- ti < pathTokens.length &&
1443
- eq(p.toLowerCase(), pathTokens[ti].toLowerCase()) &&
1444
- go(pi + 1, ti + 1);
1405
+ res = ti < pathTokens.length && eq(p.toLowerCase(), pathTokens[ti].toLowerCase()) && go(pi + 1, ti + 1);
1445
1406
  }
1446
1407
  }
1447
1408
  memo.set(key, res);
@@ -1467,10 +1428,7 @@ function stringifySceneAndOps(obj) {
1467
1428
  "**.perUserExt.*.#",
1468
1429
  "**.perUserExt.*.*.#",
1469
1430
  ],
1470
- criteria: ({ value, key }) => (typeof value === "object" &&
1471
- value &&
1472
- Object.keys(value).sort().join(",") === "x,y,z") ||
1473
- key === "value",
1431
+ criteria: ({ value, key }) => (typeof value === "object" && value && Object.keys(value).sort().join(",") === "x,y,z") || key === "value",
1474
1432
  });
1475
1433
  }
1476
1434
 
@@ -1540,8 +1498,7 @@ class LevelLogger {
1540
1498
  parseJsonStringIfFull(str) {
1541
1499
  const trimmed = str.trim();
1542
1500
  // Check if the string starts with { or [ and ends with } or ]
1543
- if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
1544
- (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1501
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1545
1502
  try {
1546
1503
  const parsed = JSON.parse(trimmed);
1547
1504
  return JSON.stringify(parsed, null, 2);
@@ -1560,8 +1517,7 @@ class LevelLogger {
1560
1517
  parseJsonStringToObject(str) {
1561
1518
  const trimmed = str.trim();
1562
1519
  // Check if the string starts with { or [ and ends with } or ]
1563
- if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
1564
- (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1520
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
1565
1521
  try {
1566
1522
  return JSON.parse(trimmed);
1567
1523
  }
@@ -1662,10 +1618,7 @@ class LevelLogger {
1662
1618
  // (starts with { or [ and contains newlines indicating it was formatted),
1663
1619
  // output it directly without stringifySceneAndOps to preserve formatting
1664
1620
  // Wrap it with json`...` to distinguish it as formatted JSON
1665
- if (parseJsonString &&
1666
- typeof v === "string" &&
1667
- v.trim().match(/^[{\[]/) &&
1668
- v.includes("\n")) {
1621
+ if (parseJsonString && typeof v === "string" && v.trim().match(/^[{\[]/) && v.includes("\n")) {
1669
1622
  return `${k}=json\`${v}\``;
1670
1623
  }
1671
1624
  // If this key had a JSON string parsed and we're formatting JSON,
@@ -1680,9 +1633,7 @@ class LevelLogger {
1680
1633
  })
1681
1634
  .join(formatJson ? "\n" : " ")}`
1682
1635
  : "";
1683
- const prefixedMessage = this.prefix
1684
- ? `${this.prefix} ${message}${contextStr}`
1685
- : `${message}${contextStr}`;
1636
+ const prefixedMessage = this.prefix ? `${this.prefix} ${message}${contextStr}` : `${message}${contextStr}`;
1686
1637
  switch (requestedLevel) {
1687
1638
  case "debug":
1688
1639
  console.info(prefixedMessage);
@@ -1917,10 +1868,7 @@ class RunContextManager {
1917
1868
  });
1918
1869
  return;
1919
1870
  }
1920
- if (ctx.pendingNodes > 0 ||
1921
- ctx.pendingEdges > 0 ||
1922
- ctx.pendingResolvers > 0 ||
1923
- ctx.pendingQueued > 0) {
1871
+ if (ctx.pendingNodes > 0 || ctx.pendingEdges > 0 || ctx.pendingResolvers > 0 || ctx.pendingQueued > 0) {
1924
1872
  return; // Still has pending work
1925
1873
  }
1926
1874
  this.logger.info("finish-run-context", {
@@ -2039,6 +1987,19 @@ function valuesEqual(a, b) {
2039
1987
  }
2040
1988
  return false;
2041
1989
  }
1990
+ /**
1991
+ * Format a node reference as <typeId>.<nodeId> for logs.
1992
+ */
1993
+ function formatNodeRef(graph, nodeId) {
1994
+ const node = graph.getNode(nodeId);
1995
+ return `${node?.typeId ?? "unknown"}.${nodeId}`;
1996
+ }
1997
+ /**
1998
+ * Format a node handle reference as <typeId>.<nodeId>.<handle> for logs.
1999
+ */
2000
+ function formatNodeHandleRef(graph, nodeId, handle) {
2001
+ return `${formatNodeRef(graph, nodeId)}.${handle}`;
2002
+ }
2042
2003
 
2043
2004
  function tryHandleResolving(def, registry, environment) {
2044
2005
  const out = new Map();
@@ -2146,9 +2107,7 @@ function extractEdgeTypes(sourceNodeId, sourceHandle, targetNodeId, targetHandle
2146
2107
  const srcDeclared = srcResolved
2147
2108
  ? srcResolved.outputs[sourceHandle]
2148
2109
  : registry.nodes.get(graph.getNode(sourceNodeId)?.typeId || "")?.outputs[sourceHandle];
2149
- const dstDeclared = getInputDeclaredTypes(dstResolved
2150
- ? dstResolved.inputs
2151
- : registry.nodes.get(graph.getNode(targetNodeId)?.typeId || "")?.inputs, targetHandle);
2110
+ const dstDeclared = getInputDeclaredTypes(dstResolved ? dstResolved.inputs : registry.nodes.get(graph.getNode(targetNodeId)?.typeId || "")?.inputs, targetHandle);
2152
2111
  return {
2153
2112
  srcDeclared,
2154
2113
  dstDeclared,
@@ -2244,9 +2203,7 @@ function buildEdgeConverters(srcDeclared, dstDeclared, registry, edgeLabel) {
2244
2203
  const { typeId, payload } = extractPayload(v);
2245
2204
  const res = getCoercion(typeId);
2246
2205
  if (!res) {
2247
- const fallbackType = isDstUnion && typeId && dstTypes.includes(typeId)
2248
- ? typeId
2249
- : undefined;
2206
+ const fallbackType = isDstUnion && typeId && dstTypes.includes(typeId) ? typeId : undefined;
2250
2207
  return wrapIfDstUnion(fallbackType, payload);
2251
2208
  }
2252
2209
  if (res.kind === "async" && res.convertAsync) {
@@ -2267,9 +2224,7 @@ function buildEdgeConverters(srcDeclared, dstDeclared, registry, edgeLabel) {
2267
2224
  const { typeId, payload } = extractPayload(v);
2268
2225
  const res = getCoercion(typeId);
2269
2226
  if (!res) {
2270
- const fallbackType = isDstUnion && typeId && dstTypes.includes(typeId)
2271
- ? typeId
2272
- : undefined;
2227
+ const fallbackType = isDstUnion && typeId && dstTypes.includes(typeId) ? typeId : undefined;
2273
2228
  return wrapIfDstUnion(fallbackType, payload);
2274
2229
  }
2275
2230
  if (res.kind === "async") {
@@ -2443,15 +2398,11 @@ class HandleResolver {
2443
2398
  // Update edge properties via Graph
2444
2399
  this.graph.updateEdgeProperties(e.id, {
2445
2400
  dstDeclared,
2446
- srcUnionTypes: Array.isArray(srcDeclared)
2447
- ? [...srcDeclared]
2448
- : undefined,
2401
+ srcUnionTypes: Array.isArray(srcDeclared) ? [...srcDeclared] : undefined,
2449
2402
  convert: conv.convert,
2450
2403
  convertAsync: conv.convertAsync,
2451
2404
  });
2452
- if (e.target.nodeId === nodeId &&
2453
- oldDstDeclared === undefined &&
2454
- dstDeclared !== undefined) {
2405
+ if (e.target.nodeId === nodeId && oldDstDeclared === undefined && dstDeclared !== undefined) {
2455
2406
  const srcNode = this.graph.getNode(e.source.nodeId);
2456
2407
  if (srcNode) {
2457
2408
  const srcValue = srcNode.outputs[e.source.handle];
@@ -2487,7 +2438,7 @@ class HandleResolver {
2487
2438
  const nodeLogValue = LOG_LEVEL_VALUES[nodeLogLevel] ?? 1;
2488
2439
  const shouldLog = nodeLogValue <= LOG_LEVEL_VALUES.debug && nodeLogLevel !== "silent";
2489
2440
  if (shouldLog) {
2490
- console.info(`[node:${nodeId}:${node.typeId}] resolveHandles-start`);
2441
+ console.info(`[node:${formatNodeRef(this.graph, nodeId)}] resolveHandles-start`);
2491
2442
  }
2492
2443
  let resolved;
2493
2444
  try {
@@ -2502,13 +2453,13 @@ class HandleResolver {
2502
2453
  catch {
2503
2454
  // Log resolveHandles-done even on error
2504
2455
  if (shouldLog) {
2505
- console.info(`[node:${nodeId}:${node.typeId}] resolveHandles-done (error)`);
2456
+ console.info(`[node:${formatNodeRef(this.graph, nodeId)}] resolveHandles-done (error)`);
2506
2457
  }
2507
2458
  return;
2508
2459
  }
2509
2460
  // Log resolveHandles-done
2510
2461
  if (shouldLog) {
2511
- console.info(`[node:${nodeId}:${node.typeId}] resolveHandles-done`);
2462
+ console.info(`[node:${formatNodeRef(this.graph, nodeId)}] resolveHandles-done`);
2512
2463
  }
2513
2464
  // If a newer recompute was scheduled, drop this result
2514
2465
  if ((this.recomputeTokenByNode.get(nodeId) ?? 0) !== token)
@@ -2570,11 +2521,10 @@ class HandleResolver {
2570
2521
  * EdgePropagator component - handles value propagation through edges
2571
2522
  */
2572
2523
  class EdgePropagator {
2573
- constructor(graph, eventEmitter, runContextManager, handleResolver, nodeExecutor, runtime) {
2524
+ constructor(graph, eventEmitter, runContextManager, nodeExecutor, runtime) {
2574
2525
  this.graph = graph;
2575
2526
  this.eventEmitter = eventEmitter;
2576
2527
  this.runContextManager = runContextManager;
2577
- this.handleResolver = handleResolver;
2578
2528
  this.nodeExecutor = nodeExecutor;
2579
2529
  this.runtime = runtime;
2580
2530
  this.arrayInputBuckets = new Map();
@@ -2604,9 +2554,7 @@ class EdgePropagator {
2604
2554
  const effectiveRunContexts = runContextIds && runContextIds.size > 0
2605
2555
  ? this.filterEffectiveRunContexts(edge, srcNodeId, runContextIds)
2606
2556
  : undefined;
2607
- if (runContextIds &&
2608
- runContextIds.size > 0 &&
2609
- !(effectiveRunContexts && effectiveRunContexts.size > 0)) {
2557
+ if (runContextIds && runContextIds.size > 0 && !(effectiveRunContexts && effectiveRunContexts.size > 0)) {
2610
2558
  return; // No valid run-contexts for this edge
2611
2559
  }
2612
2560
  // Validate union types
@@ -2647,12 +2595,16 @@ class EdgePropagator {
2647
2595
  const isUnion = Array.isArray(edge.srcUnionTypes);
2648
2596
  const isTypedValue = isTyped(value);
2649
2597
  if (isUnion && !isTypedValue) {
2598
+ const sourceTypeId = this.graph.getNode(edge.source.nodeId)?.typeId;
2599
+ const targetTypeId = this.graph.getNode(edge.target.nodeId)?.typeId;
2650
2600
  const err = new Error(`Output ${srcNodeId}.${edge.source.handle} requires typed value for union output (allowed: ${edge.srcUnionTypes.join("|")})`);
2651
2601
  this.eventEmitter.emit("error", {
2652
2602
  kind: "edge-convert",
2653
2603
  edgeId: edge.id,
2654
2604
  source: { nodeId: edge.source.nodeId, handle: edge.source.handle },
2655
2605
  target: { nodeId: edge.target.nodeId, handle: edge.target.handle },
2606
+ sourceTypeId,
2607
+ targetTypeId,
2656
2608
  err,
2657
2609
  });
2658
2610
  return false;
@@ -2668,7 +2620,14 @@ class EdgePropagator {
2668
2620
  convertedValue = edge.convert(value);
2669
2621
  }
2670
2622
  else {
2671
- console.warn(`No convert function for edge ${edge.id} of type ${edge.source.nodeId}.${edge.source.handle} -> ${edge.target.nodeId}.${edge.target.handle}`);
2623
+ const fromType = (Array.isArray(edge.srcUnionTypes) && edge.srcUnionTypes.length > 0
2624
+ ? edge.srcUnionTypes.join("|")
2625
+ : undefined) ??
2626
+ unwrapTypeId(value) ??
2627
+ edge.typeId ??
2628
+ "unknown";
2629
+ const toType = Array.isArray(edge.dstDeclared) ? edge.dstDeclared.join("|") : (edge.dstDeclared ?? "unknown");
2630
+ console.warn(`No convert function for edge ${edge.id} [${formatNodeHandleRef(this.graph, edge.source.nodeId, edge.source.handle)} -> ${formatNodeHandleRef(this.graph, edge.target.nodeId, edge.target.handle)}] from:${fromType} to:${toType}`);
2672
2631
  }
2673
2632
  this.applyToTarget(edge, convertedValue, effectiveRunContexts);
2674
2633
  }
@@ -2679,20 +2638,22 @@ class EdgePropagator {
2679
2638
  if (!edge.convertAsync)
2680
2639
  return;
2681
2640
  // Track edge run-context IDs for pendingEdges tracking
2682
- const edgeRunContextIds = effectiveRunContexts
2683
- ? Array.from(effectiveRunContexts)
2684
- : undefined;
2641
+ const edgeRunContextIds = effectiveRunContexts ? Array.from(effectiveRunContexts) : undefined;
2685
2642
  if (edgeRunContextIds) {
2686
2643
  for (const id of edgeRunContextIds) {
2687
2644
  this.runContextManager.startEdgeConversion(id, edge.id);
2688
2645
  }
2689
2646
  }
2647
+ const sourceTypeId = this.graph.getNode(edge.source.nodeId)?.typeId;
2648
+ const targetTypeId = this.graph.getNode(edge.target.nodeId)?.typeId;
2690
2649
  this.eventEmitter.emit("stats", {
2691
2650
  kind: "edge-start",
2692
2651
  edgeId: edge.id,
2693
2652
  typeId: edge.typeId,
2694
2653
  source: { nodeId: edge.source.nodeId, handle: edge.source.handle },
2695
2654
  target: { nodeId: edge.target.nodeId, handle: edge.target.handle },
2655
+ sourceTypeId,
2656
+ targetTypeId,
2696
2657
  });
2697
2658
  const controller = new AbortController();
2698
2659
  const startAt = Date.now();
@@ -2752,7 +2713,7 @@ class EdgePropagator {
2752
2713
  // Set input value (respecting skipPropagateValues)
2753
2714
  const shouldSetValue = this.shouldSetInputValue(effectiveRunContexts);
2754
2715
  if (shouldSetValue && valueChanged) {
2755
- this.setTargetInput(edge, processedValue);
2716
+ this.runtime.setTargetInput(edge, processedValue, "applyToTarget");
2756
2717
  }
2757
2718
  else if (shouldSetValue && !valueChanged) {
2758
2719
  // Even if value didn't change, update timestamp if we're forcing execution
@@ -2770,7 +2731,7 @@ class EdgePropagator {
2770
2731
  if (!dstIsArray) {
2771
2732
  return value;
2772
2733
  }
2773
- const toArray = (x) => Array.isArray(x) ? x : x === undefined ? [] : [x];
2734
+ const toArray = (x) => (Array.isArray(x) ? x : x === undefined ? [] : [x]);
2774
2735
  let forNode = this.arrayInputBuckets.get(edge.target.nodeId);
2775
2736
  if (!forNode) {
2776
2737
  forNode = new Map();
@@ -2808,13 +2769,6 @@ class EdgePropagator {
2808
2769
  }
2809
2770
  return true;
2810
2771
  }
2811
- /**
2812
- * Set target input value and emit event
2813
- */
2814
- setTargetInput(edge, value) {
2815
- this.graph.updateNodeInput(edge.target.nodeId, edge.target.handle, value);
2816
- this.handleResolver.scheduleRecomputeHandles(edge.target.nodeId);
2817
- }
2818
2772
  /**
2819
2773
  * Execute downstream if conditions are met
2820
2774
  */
@@ -2861,6 +2815,8 @@ class EdgePropagator {
2861
2815
  typeId: edge.typeId,
2862
2816
  source: { nodeId: edge.source.nodeId, handle: edge.source.handle },
2863
2817
  target: { nodeId: edge.target.nodeId, handle: edge.target.handle },
2818
+ sourceTypeId: this.graph.getNode(edge.source.nodeId)?.typeId,
2819
+ targetTypeId: this.graph.getNode(edge.target.nodeId)?.typeId,
2864
2820
  durationMs: duration,
2865
2821
  });
2866
2822
  }
@@ -2877,6 +2833,8 @@ class EdgePropagator {
2877
2833
  edgeId: edge.id,
2878
2834
  source: { nodeId: edge.source.nodeId, handle: edge.source.handle },
2879
2835
  target: { nodeId: edge.target.nodeId, handle: edge.target.handle },
2836
+ sourceTypeId: this.graph.getNode(edge.source.nodeId)?.typeId,
2837
+ targetTypeId: this.graph.getNode(edge.target.nodeId)?.typeId,
2880
2838
  err,
2881
2839
  });
2882
2840
  }
@@ -2921,9 +2879,7 @@ class EdgePropagator {
2921
2879
  return;
2922
2880
  // Get resolved handles to filter out invalid outputs
2923
2881
  const resolved = this.graph.getResolvedHandles(nodeId);
2924
- const validOutputHandles = resolved?.outputs
2925
- ? new Set(Object.keys(resolved.outputs))
2926
- : new Set();
2882
+ const validOutputHandles = resolved?.outputs ? new Set(Object.keys(resolved.outputs)) : new Set();
2927
2883
  // Use node's activeRunContexts to propagate to new nodes that were added
2928
2884
  const activeRunContextIds = this.graph.getNodeRunContextIds(nodeId);
2929
2885
  for (const [handle, value] of Object.entries(node.outputs)) {
@@ -2994,7 +2950,7 @@ class NodeExecutor {
2994
2950
  // Create log function that respects node's logLevel using LevelLogger
2995
2951
  const node = this.graph.getNode(nodeId);
2996
2952
  const nodeLogLevel = node?.logLevel ?? "info";
2997
- const logger = new LevelLogger(nodeLogLevel, `[node:${runId || nodeId}:${node?.typeId ?? ""}]`);
2953
+ const logger = new LevelLogger(nodeLogLevel, `[node:${formatNodeRef(this.graph, nodeId)}:${runId}]`);
2998
2954
  const log = (level, message, context) => {
2999
2955
  switch (level) {
3000
2956
  case "debug":
@@ -3022,11 +2978,8 @@ class NodeExecutor {
3022
2978
  execute: (opts) => {
3023
2979
  if (this.graph.allInboundHaveValue(nodeId)) {
3024
2980
  let runContextIdsToUse = this.runtime.getRunMode() === "auto" ? undefined : runContextIds;
3025
- if (this.runtime.getRunMode() === "manual" &&
3026
- (!runContextIds || runContextIds.size === 0)) {
3027
- runContextIdsToUse = new Set([
3028
- this.runContextManager.createRunContext(nodeId, opts),
3029
- ]);
2981
+ if (this.runtime.getRunMode() === "manual" && (!runContextIds || runContextIds.size === 0)) {
2982
+ runContextIdsToUse = new Set([this.runContextManager.createRunContext(nodeId, opts)]);
3030
2983
  }
3031
2984
  this.execute(nodeId, {
3032
2985
  runContextIds: runContextIdsToUse,
@@ -3062,24 +3015,22 @@ class NodeExecutor {
3062
3015
  return;
3063
3016
  const runMode = this.runtime.getRunMode();
3064
3017
  if (!runMode) {
3065
- console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: no runMode, skipping execution`);
3018
+ console.trace(`NodeExecutor.execute[${formatNodeRef(this.graph, nodeId)}:${reason}]: no runMode, skipping execution`);
3066
3019
  return;
3067
3020
  }
3068
3021
  // In manual mode, require runContextIds unless autoRun policy is set
3069
3022
  if (runMode === "manual" && (!runContextIds || runContextIds.size === 0)) {
3070
3023
  // If autoRun is true, auto-generate a run context (similar to createExecutionContext pattern)
3071
3024
  if (node.policy?.autoRun === true) {
3072
- runContextIds = new Set([
3073
- this.runContextManager.createRunContext(nodeId, { propagate: false }),
3074
- ]);
3025
+ runContextIds = new Set([this.runContextManager.createRunContext(nodeId, { propagate: false })]);
3075
3026
  }
3076
3027
  else {
3077
- console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: no runContextIds provided in manual mode, skipping execution`);
3028
+ console.trace(`NodeExecutor.execute[${formatNodeRef(this.graph, nodeId)}:${reason}]: no runContextIds provided in manual mode, skipping execution`);
3078
3029
  return;
3079
3030
  }
3080
3031
  }
3081
3032
  if (runMode === "auto" && runContextIds && runContextIds.size > 0) {
3082
- console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: runContextIds provided in auto mode, ignoring`);
3033
+ console.trace(`NodeExecutor.execute[${formatNodeRef(this.graph, nodeId)}:${reason}]: runContextIds provided in auto mode, ignoring`);
3083
3034
  runContextIds = undefined;
3084
3035
  }
3085
3036
  // Early validation for auto-mode paused state
@@ -3094,6 +3045,7 @@ class NodeExecutor {
3094
3045
  code: runtimeValidationError.code || "RUNTIME_VALIDATION_BLOCKED",
3095
3046
  details: {
3096
3047
  nodeId,
3048
+ nodeTypeId: node?.typeId,
3097
3049
  ...runtimeValidationError.details,
3098
3050
  },
3099
3051
  });
@@ -3104,8 +3056,7 @@ class NodeExecutor {
3104
3056
  if (runContextIds) {
3105
3057
  this.graph.addNodeRunContextIds(nodeId, runContextIds);
3106
3058
  }
3107
- if (!canSkipHandleResolution &&
3108
- !this.handleResolver.getPendingResolution(nodeId)) {
3059
+ if (!canSkipHandleResolution && !this.handleResolver.getPendingResolution(nodeId)) {
3109
3060
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3110
3061
  }
3111
3062
  // Check if handles are being resolved - wait for resolution before executing
@@ -3156,9 +3107,7 @@ class NodeExecutor {
3156
3107
  return false;
3157
3108
  const policy = node.policy ?? {};
3158
3109
  const lastScheduledAt = node.lastScheduledAt;
3159
- return !!(policy.debounceMs &&
3160
- lastScheduledAt &&
3161
- now - lastScheduledAt < policy.debounceMs);
3110
+ return !!(policy.debounceMs && lastScheduledAt && now - lastScheduledAt < policy.debounceMs);
3162
3111
  }
3163
3112
  /**
3164
3113
  * Handle debounced scheduling by replacing the latest queued item
@@ -3441,6 +3390,7 @@ class NodeExecutor {
3441
3390
  this.eventEmitter.emit("error", {
3442
3391
  kind: "node-run",
3443
3392
  nodeId,
3393
+ nodeTypeId: node.typeId,
3444
3394
  runId: plan.runId,
3445
3395
  err,
3446
3396
  });
@@ -3473,9 +3423,7 @@ class NodeExecutor {
3473
3423
  return;
3474
3424
  const controllers = this.graph.getNodeControllers(nodeId);
3475
3425
  const lastEndAt = Date.now();
3476
- const lastDurationMs = node.stats.lastStartAt && lastEndAt
3477
- ? lastEndAt - node.stats.lastStartAt
3478
- : undefined;
3426
+ const lastDurationMs = node.stats.lastStartAt && lastEndAt ? lastEndAt - node.stats.lastStartAt : undefined;
3479
3427
  this.graph.updateNodeStats(nodeId, {
3480
3428
  active: Math.max(0, controllers.size),
3481
3429
  lastEndAt,
@@ -3643,7 +3591,7 @@ class RuntimeValidatorManager {
3643
3591
  }
3644
3592
  catch (err) {
3645
3593
  // Don't let validator errors break execution - log and continue
3646
- console.error(`Runtime validator error for node ${nodeId}:`, err);
3594
+ console.error(`Runtime validator error for node ${formatNodeRef(this.graph, nodeId)}:`, err);
3647
3595
  }
3648
3596
  }
3649
3597
  return null;
@@ -3664,7 +3612,7 @@ class GraphRuntime {
3664
3612
  this.graph = new Graph(this.eventEmitter);
3665
3613
  this.runContextManager = new RunContextManager(this.graph, "warn");
3666
3614
  this.handleResolver = new HandleResolver(this.graph, this.eventEmitter, this.runContextManager, this);
3667
- this.edgePropagator = new EdgePropagator(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this, this);
3615
+ this.edgePropagator = new EdgePropagator(this.graph, this.eventEmitter, this.runContextManager, this, this);
3668
3616
  // Create NodeExecutor with EdgePropagator and HandleResolver
3669
3617
  this.nodeExecutor = new NodeExecutor(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this, this);
3670
3618
  // Create RuntimeValidatorManager
@@ -3753,6 +3701,30 @@ class GraphRuntime {
3753
3701
  on(event, handler) {
3754
3702
  return this.eventEmitter.on(event, handler);
3755
3703
  }
3704
+ /**
3705
+ * Check if an event is an invalidate event that should trigger re-execution
3706
+ */
3707
+ isInvalidateEvent(event) {
3708
+ if (!event || typeof event !== "object")
3709
+ return false;
3710
+ // Check if event has action === "invalidate"
3711
+ const e = event;
3712
+ return e.action === "invalidate";
3713
+ }
3714
+ executeNodeAutoRun(nodeId, opts) {
3715
+ const node = this.graph.getNode(nodeId);
3716
+ const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3717
+ let runContextIdsToUse = undefined;
3718
+ if (this.runMode === "manual") {
3719
+ runContextIdsToUse = new Set([this.runContextManager.createRunContext(nodeId, { propagate: false })]);
3720
+ }
3721
+ if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
3722
+ this.execute(nodeId, {
3723
+ runContextIds: runContextIdsToUse,
3724
+ reason: opts?.reason ?? "executeNodeAutoRun",
3725
+ });
3726
+ }
3727
+ }
3756
3728
  setInputs(nodeId, inputs) {
3757
3729
  const node = this.graph.getNode(nodeId);
3758
3730
  if (!node)
@@ -3778,11 +3750,7 @@ class GraphRuntime {
3778
3750
  : desc
3779
3751
  ? getInputDeclaredTypes(desc.inputs, handle)
3780
3752
  : undefined;
3781
- const typeIds = Array.isArray(declaredTypes)
3782
- ? declaredTypes
3783
- : declaredTypes
3784
- ? [declaredTypes]
3785
- : [];
3753
+ const typeIds = Array.isArray(declaredTypes) ? declaredTypes : declaredTypes ? [declaredTypes] : [];
3786
3754
  if (typeIds.length > 0) {
3787
3755
  const isValidForAny = typeIds.some((tId) => {
3788
3756
  const typeDesc = registry.types.get(tId);
@@ -3793,10 +3761,11 @@ class GraphRuntime {
3793
3761
  });
3794
3762
  if (value !== undefined && !isValidForAny) {
3795
3763
  const typeLabel = typeIds.join("|");
3796
- const errorMessage = `Invalid value for input ${nodeId}.${handle} (type ${typeLabel}): ${JSON.stringify(value)}`;
3764
+ const errorMessage = `Invalid value for input ${formatNodeRef(this.graph, nodeId)}.${handle} (type ${typeLabel}): ${JSON.stringify(value)}`;
3797
3765
  this.eventEmitter.emit("error", {
3798
3766
  kind: "input-validation",
3799
3767
  nodeId,
3768
+ nodeTypeId: node.typeId,
3800
3769
  handle,
3801
3770
  typeId: typeLabel,
3802
3771
  value,
@@ -3811,8 +3780,6 @@ class GraphRuntime {
3811
3780
  const same = valuesEqual(prev, value);
3812
3781
  if (!same) {
3813
3782
  this.graph.updateNodeInput(nodeId, handle, value);
3814
- // Emit value event for input updates
3815
- this.eventEmitter.emit("value", { nodeId, handle, value, io: "input" });
3816
3783
  anyChanged = true;
3817
3784
  }
3818
3785
  }
@@ -3864,16 +3831,6 @@ class GraphRuntime {
3864
3831
  // Forward event to node's onExternalEvent handler for custom actions
3865
3832
  node.runtime.onExternalEvent?.(event, node.state);
3866
3833
  }
3867
- /**
3868
- * Check if an event is an invalidate event that should trigger re-execution
3869
- */
3870
- isInvalidateEvent(event) {
3871
- if (!event || typeof event !== "object")
3872
- return false;
3873
- // Check if event has action === "invalidate"
3874
- const e = event;
3875
- return e.action === "invalidate";
3876
- }
3877
3834
  cancelNodeRuns(nodeIds) {
3878
3835
  this.nodeExecutor.cancelNodeRuns(nodeIds);
3879
3836
  }
@@ -3901,6 +3858,7 @@ class GraphRuntime {
3901
3858
  this.nodeExecutor.setEnvironment(this.environment);
3902
3859
  for (const nodeId of this.graph.getNodeIds()) {
3903
3860
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3861
+ this.executeNodeAutoRun(nodeId, { reason: "setEnvironment" });
3904
3862
  }
3905
3863
  }
3906
3864
  setCustomNodeData(customNodeData) {
@@ -4048,21 +4006,10 @@ class GraphRuntime {
4048
4006
  }
4049
4007
  }
4050
4008
  }
4051
- executeNodeAutoRun(nodeId, opts) {
4052
- const node = this.graph.getNode(nodeId);
4053
- const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
4054
- let runContextIdsToUse = undefined;
4055
- if (this.runMode === "manual") {
4056
- runContextIdsToUse = new Set([
4057
- this.runContextManager.createRunContext(nodeId, { propagate: false }),
4058
- ]);
4059
- }
4060
- if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
4061
- this.execute(nodeId, {
4062
- runContextIds: runContextIdsToUse,
4063
- reason: opts?.reason ?? "executeNodeAutoRun",
4064
- });
4065
- }
4009
+ setTargetInput(edge, value, reason) {
4010
+ this.graph.updateNodeInput(edge.target.nodeId, edge.target.handle, value);
4011
+ this.handleResolver.scheduleRecomputeHandles(edge.target.nodeId);
4012
+ this.executeNodeAutoRun(edge.target.nodeId, { reason });
4066
4013
  }
4067
4014
  copyOutputs(fromNodeId, toNodeId, options) {
4068
4015
  const fromNode = this.getNodeData(fromNodeId);
@@ -4228,8 +4175,7 @@ class GraphRuntime {
4228
4175
  set.add(e.target.handle);
4229
4176
  beforeInbound.set(e.target.nodeId, set);
4230
4177
  // Build beforeOutTargets map
4231
- const tmap = beforeOutTargets.get(e.source.nodeId) ??
4232
- new Map();
4178
+ const tmap = beforeOutTargets.get(e.source.nodeId) ?? new Map();
4233
4179
  const tset = tmap.get(e.source.handle) ?? new Set();
4234
4180
  tset.add(`${e.target.nodeId}.${e.target.handle}`);
4235
4181
  tmap.set(e.source.handle, tset);
@@ -4249,8 +4195,7 @@ class GraphRuntime {
4249
4195
  const changedHandles = {};
4250
4196
  for (const [nodeId, newHandles] of result.resolved) {
4251
4197
  const oldHandles = this.graph.getResolvedHandles(nodeId);
4252
- if (!oldHandles ||
4253
- JSON.stringify(oldHandles) !== JSON.stringify(newHandles)) {
4198
+ if (!oldHandles || JSON.stringify(oldHandles) !== JSON.stringify(newHandles)) {
4254
4199
  changedHandles[nodeId] = newHandles;
4255
4200
  }
4256
4201
  }
@@ -4302,8 +4247,7 @@ class GraphRuntime {
4302
4247
  // Propagate changes on edges added
4303
4248
  const afterOutTargets = new Map();
4304
4249
  this.graph.forEachEdge((e) => {
4305
- const targetMap = afterOutTargets.get(e.source.nodeId) ??
4306
- new Map();
4250
+ const targetMap = afterOutTargets.get(e.source.nodeId) ?? new Map();
4307
4251
  const targetSet = targetMap.get(e.source.handle) ?? new Set();
4308
4252
  targetSet.add(`${e.target.nodeId}.${e.target.handle}`);
4309
4253
  targetMap.set(e.source.handle, targetSet);
@@ -4328,10 +4272,7 @@ class GraphRuntime {
4328
4272
  for (const nodeId of nodesToCheck) {
4329
4273
  const beforeMap = beforeOutTargets.get(nodeId) ?? new Map();
4330
4274
  const afterMap = afterOutTargets.get(nodeId) ?? new Map();
4331
- const handles = new Set([
4332
- ...Array.from(beforeMap.keys()),
4333
- ...Array.from(afterMap.keys()),
4334
- ]);
4275
+ const handles = new Set([...Array.from(beforeMap.keys()), ...Array.from(afterMap.keys())]);
4335
4276
  for (const handle of handles) {
4336
4277
  const beforeTargetSet = beforeMap.get(handle) ?? new Set();
4337
4278
  const afterTargetSet = afterMap.get(handle) ?? new Set();
@@ -4452,7 +4393,10 @@ class GraphBuilder {
4452
4393
  effByNodeId.set(n.nodeId, getEffectiveHandles(n));
4453
4394
  const nodeType = this.registry.nodes.get(n.typeId);
4454
4395
  if (!nodeType) {
4455
- pushIssue("error", "NODE_TYPE_MISSING", `Unknown node type ${n.typeId}`, { typeId: n.typeId, nodeId: n.nodeId });
4396
+ pushIssue("error", "NODE_TYPE_MISSING", `Unknown node type ${n.typeId}`, {
4397
+ typeId: n.typeId,
4398
+ nodeId: n.nodeId,
4399
+ });
4456
4400
  continue;
4457
4401
  }
4458
4402
  if (!this.registry.categories.has(nodeType.categoryId)) {
@@ -4472,9 +4416,7 @@ class GraphBuilder {
4472
4416
  typeof paramValue === "number" &&
4473
4417
  !enumTypes.some((et) => validateEnumValue(et, paramValue, n.nodeId))) {
4474
4418
  const enumDef = this.registry.enums.get(enumTypes[0]);
4475
- const validValues = enumDef
4476
- ? Array.from(enumDef.valueToLabel.keys()).join(", ")
4477
- : "unknown";
4419
+ const validValues = enumDef ? Array.from(enumDef.valueToLabel.keys()).join(", ") : "unknown";
4478
4420
  pushIssue("error", "ENUM_VALUE_INVALID", `Node ${n.nodeId} param ${paramKey} has invalid enum value ${paramValue}. Valid values: ${validValues}`, {
4479
4421
  nodeId: n.nodeId,
4480
4422
  input: paramKey,
@@ -4494,9 +4436,7 @@ class GraphBuilder {
4494
4436
  typeof defaultValue === "number" &&
4495
4437
  !enumTypes.some((et) => validateEnumValue(et, defaultValue, n.nodeId))) {
4496
4438
  const enumDef = this.registry.enums.get(enumTypes[0]);
4497
- const validValues = enumDef
4498
- ? Array.from(enumDef.valueToLabel.keys()).join(", ")
4499
- : "unknown";
4439
+ const validValues = enumDef ? Array.from(enumDef.valueToLabel.keys()).join(", ") : "unknown";
4500
4440
  pushIssue("warning", "ENUM_DEFAULT_INVALID", `Node ${n.nodeId} input default ${handle} has invalid enum value ${defaultValue}. Valid values: ${validValues}`, {
4501
4441
  nodeId: n.nodeId,
4502
4442
  input: handle,
@@ -4539,7 +4479,9 @@ class GraphBuilder {
4539
4479
  if (e.typeId) {
4540
4480
  const type = this.registry.types.get(e.typeId);
4541
4481
  if (!type) {
4542
- pushIssue("error", "TYPE_MISSING", `Edge ${e.id} explicit type ${e.typeId} is missing or unknown`, { edgeId: e.id });
4482
+ pushIssue("error", "TYPE_MISSING", `Edge ${e.id} explicit type ${e.typeId} is missing or unknown`, {
4483
+ edgeId: e.id,
4484
+ });
4543
4485
  }
4544
4486
  }
4545
4487
  if (srcNode) {
@@ -4628,19 +4570,13 @@ class GraphBuilder {
4628
4570
  const outputTypes = {};
4629
4571
  for (const [outerIn, map] of Object.entries(exposure.inputs)) {
4630
4572
  const innerNode = def.nodes.find((n) => n.nodeId === map.nodeId);
4631
- const innerDesc = innerNode
4632
- ? this.registry.nodes.get(innerNode.typeId)
4633
- : undefined;
4634
- const typeIds = innerDesc
4635
- ? getInputDeclaredTypes(innerDesc.inputs, map.handle)
4636
- : undefined;
4573
+ const innerDesc = innerNode ? this.registry.nodes.get(innerNode.typeId) : undefined;
4574
+ const typeIds = innerDesc ? getInputDeclaredTypes(innerDesc.inputs, map.handle) : undefined;
4637
4575
  inputTypes[outerIn] = typeIds ?? "unknown";
4638
4576
  }
4639
4577
  for (const [outerOut, map] of Object.entries(exposure.outputs)) {
4640
4578
  const innerNode = def.nodes.find((n) => n.nodeId === map.nodeId);
4641
- const innerDesc = innerNode
4642
- ? this.registry.nodes.get(innerNode.typeId)
4643
- : undefined;
4579
+ const innerDesc = innerNode ? this.registry.nodes.get(innerNode.typeId) : undefined;
4644
4580
  const typeId = innerDesc ? innerDesc.outputs[map.handle] : undefined;
4645
4581
  const single = Array.isArray(typeId) ? typeId[0] : typeId;
4646
4582
  outputTypes[outerOut] = single ?? "unknown";
@@ -4843,7 +4779,7 @@ const CompositeCategory = (registry) => ({
4843
4779
  });
4844
4780
 
4845
4781
  // Helpers
4846
- const asArray = (v) => Array.isArray(v) ? v : [Number(v)];
4782
+ const asArray = (v) => (Array.isArray(v) ? v : [Number(v)]);
4847
4783
  const broadcast = (a, b) => {
4848
4784
  const aa = asArray(a);
4849
4785
  const bb = asArray(b);
@@ -4856,7 +4792,7 @@ const broadcast = (a, b) => {
4856
4792
  const len = Math.max(aa.length, bb.length);
4857
4793
  return [new Array(len).fill(aa[0] ?? 0), new Array(len).fill(bb[0] ?? 0)];
4858
4794
  };
4859
- const asBoolArray = (v) => Array.isArray(v) ? v.map((x) => Boolean(x)) : [Boolean(v)];
4795
+ const asBoolArray = (v) => (Array.isArray(v) ? v.map((x) => Boolean(x)) : [Boolean(v)]);
4860
4796
  const broadcastBool = (a, b) => {
4861
4797
  const aa = asBoolArray(a);
4862
4798
  const bb = asBoolArray(b);
@@ -4867,10 +4803,7 @@ const broadcastBool = (a, b) => {
4867
4803
  if (bb.length === 1)
4868
4804
  return [aa, new Array(aa.length).fill(bb[0])];
4869
4805
  const len = Math.max(aa.length, bb.length);
4870
- return [
4871
- new Array(len).fill(aa[0] ?? false),
4872
- new Array(len).fill(bb[0] ?? false),
4873
- ];
4806
+ return [new Array(len).fill(aa[0] ?? false), new Array(len).fill(bb[0] ?? false)];
4874
4807
  };
4875
4808
  const clamp = (x, min, max) => Math.min(max, Math.max(min, x));
4876
4809
  const lerp = (a, b, t) => a + (b - a) * t;
@@ -4920,8 +4853,7 @@ function jsonPointerSet(obj, pointer, value) {
4920
4853
  if (next === undefined || next === null) {
4921
4854
  // create container heuristically
4922
4855
  const nextKey = parts[i + 1];
4923
- cur[key] =
4924
- typeof nextKey === "string" && /^[0-9]+$/.test(nextKey) ? [] : {};
4856
+ cur[key] = typeof nextKey === "string" && /^[0-9]+$/.test(nextKey) ? [] : {};
4925
4857
  }
4926
4858
  cur = cur[key];
4927
4859
  }
@@ -5055,9 +4987,7 @@ function setupBasicGraphRegistry(id) {
5055
4987
  }, { withArray: false });
5056
4988
  registry.registerType({
5057
4989
  id: "base.vec3",
5058
- validate: (v) => Array.isArray(v) &&
5059
- v.length === 3 &&
5060
- v.every((x) => typeof x === "number"),
4990
+ validate: (v) => Array.isArray(v) && v.length === 3 && v.every((x) => typeof x === "number"),
5061
4991
  bakeTarget: { nodeTypeId: "base.input.vec3", inputHandle: "Value" },
5062
4992
  }, { withArray: true, arrayPickFirstDefined: true });
5063
4993
  // float -> vec3 : map x to [x,0,0]
@@ -5102,9 +5032,7 @@ function setupBasicGraphRegistry(id) {
5102
5032
  });
5103
5033
  registry.registerCoercion("base.object", "base.vec3", (v) => {
5104
5034
  try {
5105
- if (Array.isArray(v) &&
5106
- v.length === 3 &&
5107
- v.every((x) => typeof x === "number")) {
5035
+ if (Array.isArray(v) && v.length === 3 && v.every((x) => typeof x === "number")) {
5108
5036
  return v;
5109
5037
  }
5110
5038
  return undefined;
@@ -5234,9 +5162,7 @@ function setupBasicGraphRegistry(id) {
5234
5162
  const [a, b] = broadcast(ins.ValueA, ins.ValueB);
5235
5163
  const t = Number(ins.Factor ?? 0);
5236
5164
  const len = Math.max(a.length, b.length);
5237
- const out = new Array(len)
5238
- .fill(0)
5239
- .map((_, i) => lerp(Number(a[i] ?? 0), Number(b[i] ?? 0), t));
5165
+ const out = new Array(len).fill(0).map((_, i) => lerp(Number(a[i] ?? 0), Number(b[i] ?? 0), t));
5240
5166
  return { Value: out };
5241
5167
  },
5242
5168
  });
@@ -5264,9 +5190,7 @@ function setupBasicGraphRegistry(id) {
5264
5190
  const out = vals.map((v) => {
5265
5191
  const t = (Number(v) - fromMin) / (fromMax - fromMin || 1);
5266
5192
  const r = toMin + t * (toMax - toMin);
5267
- return doClamp
5268
- ? clamp(r, Math.min(toMin, toMax), Math.max(toMin, toMax))
5269
- : r;
5193
+ return doClamp ? clamp(r, Math.min(toMin, toMax), Math.max(toMin, toMax)) : r;
5270
5194
  });
5271
5195
  return { Value: out };
5272
5196
  },
@@ -5310,8 +5234,8 @@ function setupBasicGraphRegistry(id) {
5310
5234
  const sum = arr.reduce((s, x) => s + Number(x || 0), 0);
5311
5235
  return arr.length ? sum / arr.length : 0;
5312
5236
  },
5313
- [BaseMathOperation.MinAll]: (arr) => arr.length ? Math.min(...arr.map((x) => Number(x))) : 0,
5314
- [BaseMathOperation.MaxAll]: (arr) => arr.length ? Math.max(...arr.map((x) => Number(x))) : 0,
5237
+ [BaseMathOperation.MinAll]: (arr) => (arr.length ? Math.min(...arr.map((x) => Number(x))) : 0),
5238
+ [BaseMathOperation.MaxAll]: (arr) => (arr.length ? Math.max(...arr.map((x) => Number(x))) : 0),
5315
5239
  };
5316
5240
  if (aggregateByOp[op] !== undefined)
5317
5241
  return { Result: [aggregateByOp[op](a)] };
@@ -5328,8 +5252,8 @@ function setupBasicGraphRegistry(id) {
5328
5252
  const fn = binaryByOp[op] || binaryByOp[BaseMathOperation.Add];
5329
5253
  const len = Math.max(a.length, b.length);
5330
5254
  const out = new Array(len).fill(0).map((_, i) => {
5331
- const ax = a.length === 1 && len > 1 ? a[0] : a[i] ?? 0;
5332
- const bx = b.length === 1 && len > 1 ? b[0] : b[i] ?? 0;
5255
+ const ax = a.length === 1 && len > 1 ? a[0] : (a[i] ?? 0);
5256
+ const bx = b.length === 1 && len > 1 ? b[0] : (b[i] ?? 0);
5333
5257
  return fn(Number(ax), Number(bx));
5334
5258
  });
5335
5259
  return { Result: out };
@@ -5470,9 +5394,7 @@ function setupBasicGraphRegistry(id) {
5470
5394
  impl: (ins) => {
5471
5395
  const arr = Array.isArray(ins.Items) ? ins.Items : [];
5472
5396
  const s = Math.trunc(Number(ins.Start ?? 0));
5473
- const e = Number.isFinite(Number(ins.End))
5474
- ? Math.trunc(Number(ins.End))
5475
- : undefined;
5397
+ const e = Number.isFinite(Number(ins.End)) ? Math.trunc(Number(ins.End)) : undefined;
5476
5398
  return { Result: arr.slice(s, e) };
5477
5399
  },
5478
5400
  });
@@ -5747,11 +5669,7 @@ function setupBasicGraphRegistry(id) {
5747
5669
  outputs: { Keys: "base.string[]" },
5748
5670
  impl: (ins) => {
5749
5671
  const obj = ins.Object;
5750
- const keys = isPlainObject(obj)
5751
- ? Object.keys(obj)
5752
- : Array.isArray(obj)
5753
- ? Object.keys(obj)
5754
- : [];
5672
+ const keys = isPlainObject(obj) ? Object.keys(obj) : Array.isArray(obj) ? Object.keys(obj) : [];
5755
5673
  return { Keys: keys };
5756
5674
  },
5757
5675
  });
@@ -5762,11 +5680,7 @@ function setupBasicGraphRegistry(id) {
5762
5680
  outputs: { Values: "base.object" },
5763
5681
  impl: (ins) => {
5764
5682
  const obj = ins.Object;
5765
- const vals = isPlainObject(obj)
5766
- ? Object.values(obj)
5767
- : Array.isArray(obj)
5768
- ? Object.values(obj)
5769
- : [];
5683
+ const vals = isPlainObject(obj) ? Object.values(obj) : Array.isArray(obj) ? Object.values(obj) : [];
5770
5684
  return { Values: vals };
5771
5685
  },
5772
5686
  });
@@ -5778,11 +5692,7 @@ function setupBasicGraphRegistry(id) {
5778
5692
  impl: (ins) => {
5779
5693
  const root = structuredClone(ins.Object);
5780
5694
  const opsRaw = ins.Ops;
5781
- const ops = Array.isArray(opsRaw)
5782
- ? opsRaw
5783
- : opsRaw
5784
- ? [opsRaw]
5785
- : [];
5695
+ const ops = Array.isArray(opsRaw) ? opsRaw : opsRaw ? [opsRaw] : [];
5786
5696
  let cur = root;
5787
5697
  for (const op of ops) {
5788
5698
  if (!op || typeof op !== "object")
@@ -5873,9 +5783,7 @@ function registerDelayNode(registry) {
5873
5783
  impl: async (ins, ctx) => {
5874
5784
  const ms = Number(ins.DelayMs ?? 200);
5875
5785
  const valueRaw = ins.Value;
5876
- if (valueRaw === undefined ||
5877
- valueRaw === null ||
5878
- Number.isNaN(Number(valueRaw))) {
5786
+ if (valueRaw === undefined || valueRaw === null || Number.isNaN(Number(valueRaw))) {
5879
5787
  return; // wait until x is present to avoid NaN emissions
5880
5788
  }
5881
5789
  await new Promise((resolve, reject) => {
@@ -5945,9 +5853,7 @@ function generateId(prefix, used = new Set()) {
5945
5853
  id = `${prefix}${Math.random().toString(36).slice(2, 8)}`;
5946
5854
  attempts++;
5947
5855
  if (attempts > 1000) {
5948
- id = `${prefix}${Date.now().toString(36)}${Math.random()
5949
- .toString(36)
5950
- .slice(2, 4)}`;
5856
+ id = `${prefix}${Date.now().toString(36)}${Math.random().toString(36).slice(2, 4)}`;
5951
5857
  }
5952
5858
  } while (used.has(id));
5953
5859
  return id;
@@ -5988,12 +5894,10 @@ function installLogging(engine) {
5988
5894
  console.warn(`[error] input-validation: ${e.nodeId}.${e.handle} (type ${e.typeId})`, e.message);
5989
5895
  }
5990
5896
  else if (e.kind === "registry") {
5991
- console.warn(`[error] registry:`, e.err?.message ?? e.err, e.attempt !== undefined
5992
- ? `(attempt ${e.attempt}/${e.maxAttempts ?? "?"})`
5993
- : "");
5897
+ console.warn(`[error] registry:`, e.err?.message ?? e.err, e.attempt !== undefined ? `(attempt ${e.attempt}/${e.maxAttempts ?? "?"})` : "");
5994
5898
  }
5995
5899
  else if (e.kind === "system") {
5996
- console.warn(`[error] system: ${e.message}`, e.code ? `(code: ${e.code})` : "", e.err ? e.err?.message ?? e.err : "", e.details ? JSON.stringify(e.details) : "");
5900
+ console.warn(`[error] system: ${e.message}`, e.code ? `(code: ${e.code})` : "", e.err ? (e.err?.message ?? e.err) : "", e.details ? JSON.stringify(e.details) : "");
5997
5901
  }
5998
5902
  else {
5999
5903
  // Log any other error kinds (shouldn't happen, but handle gracefully)
@@ -6318,11 +6222,7 @@ function buildTypeMaps(def) {
6318
6222
  }
6319
6223
  if (node.resolvedHandles?.outputs) {
6320
6224
  for (const [handleId, handleDesc] of Object.entries(node.resolvedHandles.outputs)) {
6321
- const typeId = typeof handleDesc === "string"
6322
- ? handleDesc
6323
- : Array.isArray(handleDesc)
6324
- ? handleDesc[0]
6325
- : undefined;
6225
+ const typeId = typeof handleDesc === "string" ? handleDesc : Array.isArray(handleDesc) ? handleDesc[0] : undefined;
6326
6226
  if (typeId)
6327
6227
  nodeOutputTypes.set(handleId, typeId);
6328
6228
  }
@@ -6373,8 +6273,7 @@ function applyConversion(items, values, converter, type) {
6373
6273
  });
6374
6274
  if (convertedValue === null) {
6375
6275
  delete values[item.nodeId]?.[item.handleId];
6376
- if (values[item.nodeId] &&
6377
- Object.keys(values[item.nodeId]).length === 0) {
6276
+ if (values[item.nodeId] && Object.keys(values[item.nodeId]).length === 0) {
6378
6277
  delete values[item.nodeId];
6379
6278
  }
6380
6279
  }
@@ -6420,7 +6319,7 @@ function convertSnapshot(snapshot, converter) {
6420
6319
  }
6421
6320
  const { converted: convertedInputs, toConvert: inputsToConvert } = collectValuesToConvert(snapshot.inputs ?? {}, nodeTypeMap, inputHandleTypeMap, outputHandleTypeMap, "input");
6422
6321
  const { converted: convertedOutputs, toConvert: outputsToConvert } = collectValuesToConvert(snapshot.outputs ?? {}, nodeTypeMap, inputHandleTypeMap, outputHandleTypeMap, "output");
6423
- const { converted: convertedInputDefaults, toConvert: inputDefaultsToConvert, } = collectValuesToConvert(inputDefaults, nodeTypeMap, inputHandleTypeMap, outputHandleTypeMap, "inputDefault");
6322
+ const { converted: convertedInputDefaults, toConvert: inputDefaultsToConvert } = collectValuesToConvert(inputDefaults, nodeTypeMap, inputHandleTypeMap, outputHandleTypeMap, "inputDefault");
6424
6323
  applyConversion(inputsToConvert, convertedInputs, converter, "input");
6425
6324
  applyConversion(outputsToConvert, convertedOutputs, converter, "output");
6426
6325
  applyConversion(inputDefaultsToConvert, convertedInputDefaults, converter, "inputDefault");
@@ -6610,7 +6509,7 @@ function matchesPattern(value, pattern) {
6610
6509
  */
6611
6510
  function buildValueConverter(config) {
6612
6511
  return (converterConfig) => {
6613
- const { nodeId, handleId, value, type, nodeTypeId, handleDataType, runtimeTypeId, } = converterConfig;
6512
+ const { nodeId, handleId, value, type, nodeTypeId, handleDataType, runtimeTypeId } = converterConfig;
6614
6513
  const isValueTyped = isTyped(value);
6615
6514
  for (const mapping of config.mappings) {
6616
6515
  if (mapping.type && mapping.type !== type)
@@ -6653,8 +6552,7 @@ function buildValueConverter(config) {
6653
6552
  }
6654
6553
  }
6655
6554
  else {
6656
- if (typeof matchValue === "string" ||
6657
- typeof matchValue === "number") {
6555
+ if (typeof matchValue === "string" || typeof matchValue === "number") {
6658
6556
  const key = String(matchValue);
6659
6557
  if (key in mapping.valueMap) {
6660
6558
  newValue = mapping.valueMap[key];
@@ -6688,8 +6586,7 @@ function buildValueConverter(config) {
6688
6586
  }
6689
6587
  }
6690
6588
  else {
6691
- if (typeof innerValue === "string" ||
6692
- typeof innerValue === "number") {
6589
+ if (typeof innerValue === "string" || typeof innerValue === "number") {
6693
6590
  const key = String(innerValue);
6694
6591
  if (key in mapping.valueMap) {
6695
6592
  newValue = mapping.valueMap[key];
@@ -6716,8 +6613,7 @@ function buildValueConverter(config) {
6716
6613
  : firstSegment instanceof RegExp
6717
6614
  ? null
6718
6615
  : String(firstSegment);
6719
- if (firstSegmentStr !== "__spark_value" &&
6720
- firstSegmentStr !== "__spark_type") {
6616
+ if (firstSegmentStr !== "__spark_value" && firstSegmentStr !== "__spark_type") {
6721
6617
  pathSegments = ["__spark_value", ...pathSegments];
6722
6618
  }
6723
6619
  }
@@ -6737,8 +6633,7 @@ function buildValueConverter(config) {
6737
6633
  }
6738
6634
  }
6739
6635
  else {
6740
- if (typeof matchValue === "string" ||
6741
- typeof matchValue === "number") {
6636
+ if (typeof matchValue === "string" || typeof matchValue === "number") {
6742
6637
  const key = String(matchValue);
6743
6638
  if (key in mapping.valueMap) {
6744
6639
  newValue = mapping.valueMap[key];