@bian-womp/spark-graph 0.2.9 → 0.2.11
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/lib/cjs/index.cjs +83 -6
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/cjs/src/builder/Registry.d.ts +1 -1
- package/lib/cjs/src/builder/Registry.d.ts.map +1 -1
- package/lib/cjs/src/core/types.d.ts +1 -1
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/examples/run.d.ts.map +1 -1
- package/lib/cjs/src/examples/snapshot.d.ts +4 -0
- package/lib/cjs/src/examples/snapshot.d.ts.map +1 -0
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +6 -0
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +83 -6
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/esm/src/builder/Registry.d.ts +1 -1
- package/lib/esm/src/builder/Registry.d.ts.map +1 -1
- package/lib/esm/src/core/types.d.ts +1 -1
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/examples/run.d.ts.map +1 -1
- package/lib/esm/src/examples/snapshot.d.ts +4 -0
- package/lib/esm/src/examples/snapshot.d.ts.map +1 -0
- package/lib/esm/src/runtime/GraphRuntime.d.ts +6 -0
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -504,6 +504,7 @@ class GraphRuntime {
|
|
|
504
504
|
srcUnionTypes: Array.isArray(srcDeclared)
|
|
505
505
|
? [...srcDeclared]
|
|
506
506
|
: undefined,
|
|
507
|
+
dstDeclared,
|
|
507
508
|
stats: { runs: 0, inFlight: false, progress: 0 },
|
|
508
509
|
};
|
|
509
510
|
});
|
|
@@ -839,17 +840,26 @@ class GraphRuntime {
|
|
|
839
840
|
const dstNode = this.nodes.get(e.target.nodeId);
|
|
840
841
|
if (!dstNode)
|
|
841
842
|
return;
|
|
843
|
+
const dstIsArray = typeof e.dstDeclared === "string" && e.dstDeclared.endsWith("[]");
|
|
844
|
+
let next = v;
|
|
845
|
+
// If target input is an array type, append incoming values instead of last-write wins
|
|
846
|
+
if (dstIsArray) {
|
|
847
|
+
const toArray = (x) => Array.isArray(x) ? x : x === undefined ? [] : [x];
|
|
848
|
+
const prev = dstNode.inputs[e.target.handle];
|
|
849
|
+
const merged = [...toArray(prev), ...toArray(v)];
|
|
850
|
+
next = merged;
|
|
851
|
+
}
|
|
842
852
|
const prev = dstNode.inputs[e.target.handle];
|
|
843
|
-
const same = this.valuesEqual(prev,
|
|
853
|
+
const same = this.valuesEqual(prev, next);
|
|
844
854
|
if (!same) {
|
|
845
|
-
dstNode.inputs[e.target.handle] =
|
|
855
|
+
dstNode.inputs[e.target.handle] = next;
|
|
846
856
|
// Emit value event for input updates
|
|
847
857
|
this.emit("value", {
|
|
848
858
|
nodeId: e.target.nodeId,
|
|
849
859
|
handle: e.target.handle,
|
|
850
|
-
value:
|
|
860
|
+
value: next,
|
|
851
861
|
io: "input",
|
|
852
|
-
runtimeTypeId: getTypedOutputTypeId(
|
|
862
|
+
runtimeTypeId: getTypedOutputTypeId(next),
|
|
853
863
|
});
|
|
854
864
|
if (!this.paused && this.allInboundHaveValue(e.target.nodeId))
|
|
855
865
|
this.scheduleInputsChanged(e.target.nodeId);
|
|
@@ -1044,6 +1054,60 @@ class GraphRuntime {
|
|
|
1044
1054
|
__unsafe_scheduleInputsChanged(nodeId) {
|
|
1045
1055
|
this.scheduleInputsChanged(nodeId);
|
|
1046
1056
|
}
|
|
1057
|
+
// Hydrate inputs/outputs without triggering computation; optionally re-emit outputs downstream
|
|
1058
|
+
hydrate(payload, opts) {
|
|
1059
|
+
const prevPaused = this.paused;
|
|
1060
|
+
this.paused = true;
|
|
1061
|
+
try {
|
|
1062
|
+
const ins = payload?.inputs || {};
|
|
1063
|
+
for (const [nodeId, map] of Object.entries(ins)) {
|
|
1064
|
+
const node = this.nodes.get(nodeId);
|
|
1065
|
+
if (!node)
|
|
1066
|
+
continue;
|
|
1067
|
+
for (const [h, v] of Object.entries(map || {})) {
|
|
1068
|
+
node.inputs[h] =
|
|
1069
|
+
typeof structuredClone === "function"
|
|
1070
|
+
? structuredClone(v)
|
|
1071
|
+
: JSON.parse(JSON.stringify(v));
|
|
1072
|
+
// emit input value event
|
|
1073
|
+
this.emit("value", {
|
|
1074
|
+
nodeId,
|
|
1075
|
+
handle: h,
|
|
1076
|
+
value: node.inputs[h],
|
|
1077
|
+
io: "input",
|
|
1078
|
+
runtimeTypeId: getTypedOutputTypeId(node.inputs[h]),
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
const outs = payload?.outputs || {};
|
|
1083
|
+
for (const [nodeId, map] of Object.entries(outs)) {
|
|
1084
|
+
const node = this.nodes.get(nodeId);
|
|
1085
|
+
if (!node)
|
|
1086
|
+
continue;
|
|
1087
|
+
for (const [h, v] of Object.entries(map || {})) {
|
|
1088
|
+
node.outputs[h] =
|
|
1089
|
+
typeof structuredClone === "function"
|
|
1090
|
+
? structuredClone(v)
|
|
1091
|
+
: JSON.parse(JSON.stringify(v));
|
|
1092
|
+
// emit output value event
|
|
1093
|
+
this.emit("value", {
|
|
1094
|
+
nodeId,
|
|
1095
|
+
handle: h,
|
|
1096
|
+
value: node.outputs[h],
|
|
1097
|
+
io: "output",
|
|
1098
|
+
runtimeTypeId: getTypedOutputTypeId(node.outputs[h]),
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
if (opts?.reemit) {
|
|
1103
|
+
for (const nodeId of this.nodes.keys())
|
|
1104
|
+
this.reemitNodeOutputs(nodeId);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
finally {
|
|
1108
|
+
this.paused = prevPaused;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1047
1111
|
// Incrementally update nodes/edges to match new definition without full rebuild
|
|
1048
1112
|
update(def, registry) {
|
|
1049
1113
|
// Handle node additions and removals
|
|
@@ -1182,6 +1246,7 @@ class GraphRuntime {
|
|
|
1182
1246
|
typeId: effectiveTypeId ?? "untyped",
|
|
1183
1247
|
convert,
|
|
1184
1248
|
convertAsync,
|
|
1249
|
+
dstDeclared,
|
|
1185
1250
|
stats: { runs: 0, inFlight: false, progress: 0 },
|
|
1186
1251
|
};
|
|
1187
1252
|
});
|
|
@@ -1327,6 +1392,8 @@ class GraphBuilder {
|
|
|
1327
1392
|
}
|
|
1328
1393
|
// edges validation: nodes exist, handles exist, type exists
|
|
1329
1394
|
const inboundCounts = new Map();
|
|
1395
|
+
// Track which inbound (nodeId::handle) are declared as array inputs, to allow multi-inbound without warning
|
|
1396
|
+
const inboundArrayOk = new Set();
|
|
1330
1397
|
for (const e of def.edges) {
|
|
1331
1398
|
if (edgeIds.has(e.id)) {
|
|
1332
1399
|
issues.push({
|
|
@@ -1367,7 +1434,7 @@ class GraphBuilder {
|
|
|
1367
1434
|
if (dstNode) {
|
|
1368
1435
|
const dstType = this.registry.nodes.get(dstNode.typeId);
|
|
1369
1436
|
if (dstType)
|
|
1370
|
-
_dstDeclared = dstType.inputs
|
|
1437
|
+
_dstDeclared = getInputTypeId(dstType.inputs, e.target.handle);
|
|
1371
1438
|
}
|
|
1372
1439
|
if (!effectiveTypeId) {
|
|
1373
1440
|
if (Array.isArray(_srcDeclared) && _dstDeclared) {
|
|
@@ -1514,9 +1581,19 @@ class GraphBuilder {
|
|
|
1514
1581
|
// Track multiple inbound edges targeting the same input handle
|
|
1515
1582
|
const inboundKey = `${e.target.nodeId}::${e.target.handle}`;
|
|
1516
1583
|
inboundCounts.set(inboundKey, (inboundCounts.get(inboundKey) ?? 0) + 1);
|
|
1584
|
+
// If the target input is declared as an array type, allow multi-inbound (runtime will append)
|
|
1585
|
+
if (dstNode) {
|
|
1586
|
+
const dstType = this.registry.nodes.get(dstNode.typeId);
|
|
1587
|
+
const declaredIn = dstType
|
|
1588
|
+
? getInputTypeId(dstType.inputs, e.target.handle)
|
|
1589
|
+
: undefined;
|
|
1590
|
+
if (typeof declaredIn === "string" && declaredIn.endsWith("[]")) {
|
|
1591
|
+
inboundArrayOk.add(inboundKey);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1517
1594
|
}
|
|
1518
1595
|
for (const [key, count] of inboundCounts) {
|
|
1519
|
-
if (count > 1) {
|
|
1596
|
+
if (count > 1 && !inboundArrayOk.has(key)) {
|
|
1520
1597
|
issues.push({
|
|
1521
1598
|
level: "warning",
|
|
1522
1599
|
code: "MULTI_INBOUND",
|