@bian-womp/spark-graph 0.2.29 → 0.2.31
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 +96 -54
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +2 -28
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +96 -54
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +2 -28
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -573,57 +573,67 @@ class GraphRuntime {
|
|
|
573
573
|
return node?.outputs[output];
|
|
574
574
|
}
|
|
575
575
|
static buildEdgeConverters(srcDeclared, dstDeclared, registry) {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
return payload;
|
|
596
|
-
if (res.kind === "async")
|
|
597
|
-
return await res.convertAsync(payload, signal);
|
|
598
|
-
return res.convert(payload);
|
|
599
|
-
};
|
|
576
|
+
if (!dstDeclared || !srcDeclared) {
|
|
577
|
+
return {};
|
|
578
|
+
}
|
|
579
|
+
const isUnion = Array.isArray(srcDeclared);
|
|
580
|
+
const srcTypes = isUnion ? srcDeclared : [srcDeclared];
|
|
581
|
+
// Helper to get the coercion for a specific type
|
|
582
|
+
const getCoercion = (typeId) => {
|
|
583
|
+
return registry.resolveCoercion(typeId, dstDeclared);
|
|
584
|
+
};
|
|
585
|
+
// Resolve coercions for all source types
|
|
586
|
+
const coercions = srcTypes.map(getCoercion);
|
|
587
|
+
const hasAsync = coercions.some((r) => r?.kind === "async");
|
|
588
|
+
// Helper to extract and validate typed output for unions
|
|
589
|
+
const extractPayload = (v) => {
|
|
590
|
+
const typeId = getTypedOutputTypeId(v);
|
|
591
|
+
const payload = getTypedOutputValue(v);
|
|
592
|
+
if (isUnion) {
|
|
593
|
+
if (!typeId) {
|
|
594
|
+
throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
|
|
600
595
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const typeId = getTypedOutputTypeId(v);
|
|
604
|
-
if (!typeId)
|
|
605
|
-
throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
|
|
606
|
-
if (!srcTypes.includes(typeId))
|
|
607
|
-
throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
|
|
608
|
-
const payload = getTypedOutputValue(v);
|
|
609
|
-
const res = registry.resolveCoercion(typeId, dstDeclared);
|
|
610
|
-
if (!res)
|
|
611
|
-
return payload;
|
|
612
|
-
if (res.kind === "async")
|
|
613
|
-
throw new Error("Async coercion required but convert used");
|
|
614
|
-
return res.convert(payload);
|
|
615
|
-
};
|
|
596
|
+
if (!srcTypes.includes(typeId)) {
|
|
597
|
+
throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
|
|
616
598
|
}
|
|
617
599
|
}
|
|
618
|
-
else {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
convertAsync = res.convertAsync;
|
|
622
|
-
else if (res?.kind === "sync")
|
|
623
|
-
convert = res.convert;
|
|
600
|
+
else if (typeId) {
|
|
601
|
+
// Warn if typed output is used for non-union source
|
|
602
|
+
console.warn(`Typed output ${typeId} is fed even though source is not union: ${srcDeclared} -> ${dstDeclared}`);
|
|
624
603
|
}
|
|
604
|
+
return { typeId: typeId || srcTypes[0], payload };
|
|
605
|
+
};
|
|
606
|
+
if (hasAsync) {
|
|
607
|
+
return {
|
|
608
|
+
convertAsync: async (v, signal) => {
|
|
609
|
+
const { typeId, payload } = extractPayload(v);
|
|
610
|
+
const res = getCoercion(typeId);
|
|
611
|
+
if (!res)
|
|
612
|
+
return payload;
|
|
613
|
+
if (res.kind === "async") {
|
|
614
|
+
return await res.convertAsync(payload, signal);
|
|
615
|
+
}
|
|
616
|
+
return res.convert(payload);
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
// Sync path
|
|
621
|
+
const firstCoercion = coercions.find((r) => r?.kind === "sync");
|
|
622
|
+
if (!firstCoercion) {
|
|
623
|
+
return {};
|
|
625
624
|
}
|
|
626
|
-
return {
|
|
625
|
+
return {
|
|
626
|
+
convert: (v) => {
|
|
627
|
+
const { typeId, payload } = extractPayload(v);
|
|
628
|
+
const res = getCoercion(typeId);
|
|
629
|
+
if (!res)
|
|
630
|
+
return payload;
|
|
631
|
+
if (res.kind === "async") {
|
|
632
|
+
throw new Error("Async coercion required but convert used");
|
|
633
|
+
}
|
|
634
|
+
return res.convert(payload);
|
|
635
|
+
},
|
|
636
|
+
};
|
|
627
637
|
}
|
|
628
638
|
scheduleInputsChanged(nodeId) {
|
|
629
639
|
const node = this.nodes.get(nodeId);
|
|
@@ -827,6 +837,9 @@ class GraphRuntime {
|
|
|
827
837
|
const dstNode = this.nodes.get(e.target.nodeId);
|
|
828
838
|
if (!dstNode)
|
|
829
839
|
return;
|
|
840
|
+
// Skip writing to unresolved handles - wait for handle resolution
|
|
841
|
+
if (e.dstDeclared === undefined)
|
|
842
|
+
return;
|
|
830
843
|
const dstIsArray = typeof e.dstDeclared === "string" && e.dstDeclared.endsWith("[]");
|
|
831
844
|
let next = v;
|
|
832
845
|
// If target input is an array type, merge per-edge contributions deterministically
|
|
@@ -984,7 +997,7 @@ class GraphRuntime {
|
|
|
984
997
|
return def.edges.map((e) => {
|
|
985
998
|
const srcNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
|
|
986
999
|
const dstNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
|
|
987
|
-
let effectiveTypeId = e.typeId;
|
|
1000
|
+
let effectiveTypeId = e.typeId; // Start with original
|
|
988
1001
|
let srcDeclared;
|
|
989
1002
|
let dstDeclared;
|
|
990
1003
|
if (srcNode) {
|
|
@@ -993,6 +1006,7 @@ class GraphRuntime {
|
|
|
993
1006
|
srcDeclared = resolved.outputs[e.source.handle];
|
|
994
1007
|
}
|
|
995
1008
|
if (!effectiveTypeId) {
|
|
1009
|
+
// Infer if not explicitly set
|
|
996
1010
|
effectiveTypeId = Array.isArray(srcDeclared)
|
|
997
1011
|
? srcDeclared[0]
|
|
998
1012
|
: srcDeclared;
|
|
@@ -1007,7 +1021,8 @@ class GraphRuntime {
|
|
|
1007
1021
|
id: e.id,
|
|
1008
1022
|
source: { ...e.source },
|
|
1009
1023
|
target: { ...e.target },
|
|
1010
|
-
typeId:
|
|
1024
|
+
typeId: e.typeId, // Preserve original (may be undefined)
|
|
1025
|
+
effectiveTypeId: effectiveTypeId ?? "untyped", // Always present
|
|
1011
1026
|
convert,
|
|
1012
1027
|
convertAsync,
|
|
1013
1028
|
srcUnionTypes: Array.isArray(srcDeclared)
|
|
@@ -1031,18 +1046,19 @@ class GraphRuntime {
|
|
|
1031
1046
|
this.resolvedByNode.set(nodeId, handles);
|
|
1032
1047
|
// Recompute edge converter/type for edges where this node is source or target
|
|
1033
1048
|
for (const e of this.edges) {
|
|
1034
|
-
let srcDeclared = e.
|
|
1049
|
+
let srcDeclared = e.effectiveTypeId; // Use effectiveTypeId as fallback
|
|
1035
1050
|
let dstDeclared = e.dstDeclared;
|
|
1051
|
+
const oldDstDeclared = dstDeclared; // Track old value to detect resolution
|
|
1036
1052
|
if (e.source.nodeId === nodeId) {
|
|
1037
1053
|
const resolved = this.resolvedByNode.get(nodeId);
|
|
1038
1054
|
srcDeclared = resolved
|
|
1039
1055
|
? resolved.outputs[e.source.handle]
|
|
1040
1056
|
: srcDeclared;
|
|
1041
|
-
//
|
|
1057
|
+
// Update effectiveTypeId if original wasn't explicit
|
|
1042
1058
|
if (!e.typeId) {
|
|
1043
|
-
e.
|
|
1044
|
-
? srcDeclared?.[0]
|
|
1045
|
-
: srcDeclared;
|
|
1059
|
+
e.effectiveTypeId = Array.isArray(srcDeclared)
|
|
1060
|
+
? srcDeclared?.[0] ?? "untyped"
|
|
1061
|
+
: srcDeclared ?? "untyped";
|
|
1046
1062
|
}
|
|
1047
1063
|
}
|
|
1048
1064
|
if (e.target.nodeId === nodeId) {
|
|
@@ -1055,6 +1071,19 @@ class GraphRuntime {
|
|
|
1055
1071
|
const conv = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
|
|
1056
1072
|
e.convert = conv.convert;
|
|
1057
1073
|
e.convertAsync = conv.convertAsync;
|
|
1074
|
+
// If target handle was just resolved (was undefined, now has a type), re-propagate values
|
|
1075
|
+
if (e.target.nodeId === nodeId &&
|
|
1076
|
+
oldDstDeclared === undefined &&
|
|
1077
|
+
dstDeclared !== undefined) {
|
|
1078
|
+
const srcNode = this.nodes.get(e.source.nodeId);
|
|
1079
|
+
if (srcNode) {
|
|
1080
|
+
const srcValue = srcNode.outputs[e.source.handle];
|
|
1081
|
+
if (srcValue !== undefined) {
|
|
1082
|
+
// Re-propagate through the now-resolved edge converter
|
|
1083
|
+
this.propagate(e.source.nodeId, e.source.handle, srcValue);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1058
1087
|
}
|
|
1059
1088
|
// Invalidate downstream for this node so UI refreshes
|
|
1060
1089
|
this.invalidateDownstream(nodeId);
|
|
@@ -1165,10 +1194,23 @@ class GraphRuntime {
|
|
|
1165
1194
|
getGraphDef() {
|
|
1166
1195
|
const nodes = Array.from(this.nodes.values()).map((n) => {
|
|
1167
1196
|
const resolved = this.resolvedByNode.get(n.nodeId);
|
|
1197
|
+
// Collect user-provided inputs (inputs without inbound edges)
|
|
1198
|
+
const initialInputs = {};
|
|
1199
|
+
for (const [handle, value] of Object.entries(n.inputs)) {
|
|
1200
|
+
const hasInbound = this.edges.some((e) => e.target.nodeId === n.nodeId && e.target.handle === handle);
|
|
1201
|
+
if (!hasInbound && value !== undefined) {
|
|
1202
|
+
// Clone to avoid shared references
|
|
1203
|
+
initialInputs[handle] =
|
|
1204
|
+
typeof structuredClone === "function"
|
|
1205
|
+
? structuredClone(value)
|
|
1206
|
+
: JSON.parse(JSON.stringify(value));
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1168
1209
|
return {
|
|
1169
1210
|
nodeId: n.nodeId,
|
|
1170
1211
|
typeId: n.typeId,
|
|
1171
1212
|
params: n.params ? { ...n.params } : undefined,
|
|
1213
|
+
initialInputs: Object.keys(initialInputs).length > 0 ? initialInputs : undefined,
|
|
1172
1214
|
resolvedHandles: resolved ? { ...resolved } : undefined,
|
|
1173
1215
|
};
|
|
1174
1216
|
});
|
|
@@ -1176,7 +1218,7 @@ class GraphRuntime {
|
|
|
1176
1218
|
id: e.id,
|
|
1177
1219
|
source: { nodeId: e.source.nodeId, handle: e.source.handle },
|
|
1178
1220
|
target: { nodeId: e.target.nodeId, handle: e.target.handle },
|
|
1179
|
-
typeId: e.typeId
|
|
1221
|
+
typeId: e.typeId, // Only export original typeId (may be undefined)
|
|
1180
1222
|
}));
|
|
1181
1223
|
return { nodes, edges };
|
|
1182
1224
|
}
|