@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.
@@ -1,4 +1,4 @@
1
- import type { RuntimeValue, RuntimeInvalidate, RuntimeError, RuntimeStats, ResolvedHandles, GraphDefinition, ExecutionPolicy } from "@bian-womp/spark-protocol";
1
+ import type { RuntimeValue, RuntimeInvalidate, RuntimeError, RuntimeStats, ResolvedHandles, GraphDefinition } from "@bian-womp/spark-protocol";
2
2
  import type { RuntimeNodeStats } from "../core/types";
3
3
  import { Registry } from "../builder/Registry";
4
4
  export type RuntimeEventListener<K extends RuntimeEventName> = (e: RuntimeEventMap[K]) => void;
@@ -50,33 +50,7 @@ export declare class GraphRuntime {
50
50
  } | undefined;
51
51
  getEnvironment(): Record<string, unknown>;
52
52
  setEnvironment(env: Record<string, unknown>): void;
53
- getGraphDef(): {
54
- nodes: {
55
- nodeId: string;
56
- typeId: string;
57
- params: {
58
- [x: string]: unknown;
59
- policy?: Partial<ExecutionPolicy>;
60
- } | undefined;
61
- resolvedHandles: {
62
- inputs: Record<string, import("@bian-womp/spark-protocol").InputHandleDescriptor>;
63
- outputs: Record<string, import("@bian-womp/spark-protocol").DataTypeId | import("@bian-womp/spark-protocol").DataTypeId[]>;
64
- inputDefaults: Record<string, unknown>;
65
- } | undefined;
66
- }[];
67
- edges: {
68
- id: string;
69
- source: {
70
- nodeId: string;
71
- handle: string;
72
- };
73
- target: {
74
- nodeId: string;
75
- handle: string;
76
- };
77
- typeId: string | undefined;
78
- }[];
79
- };
53
+ getGraphDef(): GraphDefinition;
80
54
  whenIdle(): Promise<void>;
81
55
  pause(): void;
82
56
  resume(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"GraphRuntime.d.ts","sourceRoot":"","sources":["../../../../src/runtime/GraphRuntime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GraphRuntime.d.ts","sourceRoot":"","sources":["../../../../src/runtime/GraphRuntime.ts"],"names":[],"mappings":"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"}
package/lib/esm/index.js CHANGED
@@ -571,57 +571,67 @@ class GraphRuntime {
571
571
  return node?.outputs[output];
572
572
  }
573
573
  static buildEdgeConverters(srcDeclared, dstDeclared, registry) {
574
- let convert;
575
- let convertAsync;
576
- if (dstDeclared && srcDeclared) {
577
- if (Array.isArray(srcDeclared)) {
578
- const srcTypes = srcDeclared;
579
- const anyAsync = srcTypes.some((s) => {
580
- const res = registry.resolveCoercion(s, dstDeclared);
581
- return res?.kind === "async";
582
- });
583
- if (anyAsync) {
584
- convertAsync = async (v, signal) => {
585
- const typeId = getTypedOutputTypeId(v);
586
- if (!typeId)
587
- throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
588
- if (!srcTypes.includes(typeId))
589
- throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
590
- const payload = getTypedOutputValue(v);
591
- const res = registry.resolveCoercion(typeId, dstDeclared);
592
- if (!res)
593
- return payload;
594
- if (res.kind === "async")
595
- return await res.convertAsync(payload, signal);
596
- return res.convert(payload);
597
- };
574
+ if (!dstDeclared || !srcDeclared) {
575
+ return {};
576
+ }
577
+ const isUnion = Array.isArray(srcDeclared);
578
+ const srcTypes = isUnion ? srcDeclared : [srcDeclared];
579
+ // Helper to get the coercion for a specific type
580
+ const getCoercion = (typeId) => {
581
+ return registry.resolveCoercion(typeId, dstDeclared);
582
+ };
583
+ // Resolve coercions for all source types
584
+ const coercions = srcTypes.map(getCoercion);
585
+ const hasAsync = coercions.some((r) => r?.kind === "async");
586
+ // Helper to extract and validate typed output for unions
587
+ const extractPayload = (v) => {
588
+ const typeId = getTypedOutputTypeId(v);
589
+ const payload = getTypedOutputValue(v);
590
+ if (isUnion) {
591
+ if (!typeId) {
592
+ throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
598
593
  }
599
- else {
600
- convert = (v) => {
601
- const typeId = getTypedOutputTypeId(v);
602
- if (!typeId)
603
- throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
604
- if (!srcTypes.includes(typeId))
605
- throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
606
- const payload = getTypedOutputValue(v);
607
- const res = registry.resolveCoercion(typeId, dstDeclared);
608
- if (!res)
609
- return payload;
610
- if (res.kind === "async")
611
- throw new Error("Async coercion required but convert used");
612
- return res.convert(payload);
613
- };
594
+ if (!srcTypes.includes(typeId)) {
595
+ throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
614
596
  }
615
597
  }
616
- else {
617
- const res = registry.resolveCoercion(srcDeclared, dstDeclared);
618
- if (res?.kind === "async")
619
- convertAsync = res.convertAsync;
620
- else if (res?.kind === "sync")
621
- convert = res.convert;
598
+ else if (typeId) {
599
+ // Warn if typed output is used for non-union source
600
+ console.warn(`Typed output ${typeId} is fed even though source is not union: ${srcDeclared} -> ${dstDeclared}`);
622
601
  }
602
+ return { typeId: typeId || srcTypes[0], payload };
603
+ };
604
+ if (hasAsync) {
605
+ return {
606
+ convertAsync: async (v, signal) => {
607
+ const { typeId, payload } = extractPayload(v);
608
+ const res = getCoercion(typeId);
609
+ if (!res)
610
+ return payload;
611
+ if (res.kind === "async") {
612
+ return await res.convertAsync(payload, signal);
613
+ }
614
+ return res.convert(payload);
615
+ },
616
+ };
617
+ }
618
+ // Sync path
619
+ const firstCoercion = coercions.find((r) => r?.kind === "sync");
620
+ if (!firstCoercion) {
621
+ return {};
623
622
  }
624
- return { convert, convertAsync };
623
+ return {
624
+ convert: (v) => {
625
+ const { typeId, payload } = extractPayload(v);
626
+ const res = getCoercion(typeId);
627
+ if (!res)
628
+ return payload;
629
+ if (res.kind === "async") {
630
+ throw new Error("Async coercion required but convert used");
631
+ }
632
+ return res.convert(payload);
633
+ },
634
+ };
625
635
  }
626
636
  scheduleInputsChanged(nodeId) {
627
637
  const node = this.nodes.get(nodeId);
@@ -825,6 +835,9 @@ class GraphRuntime {
825
835
  const dstNode = this.nodes.get(e.target.nodeId);
826
836
  if (!dstNode)
827
837
  return;
838
+ // Skip writing to unresolved handles - wait for handle resolution
839
+ if (e.dstDeclared === undefined)
840
+ return;
828
841
  const dstIsArray = typeof e.dstDeclared === "string" && e.dstDeclared.endsWith("[]");
829
842
  let next = v;
830
843
  // If target input is an array type, merge per-edge contributions deterministically
@@ -982,7 +995,7 @@ class GraphRuntime {
982
995
  return def.edges.map((e) => {
983
996
  const srcNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
984
997
  const dstNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
985
- let effectiveTypeId = e.typeId;
998
+ let effectiveTypeId = e.typeId; // Start with original
986
999
  let srcDeclared;
987
1000
  let dstDeclared;
988
1001
  if (srcNode) {
@@ -991,6 +1004,7 @@ class GraphRuntime {
991
1004
  srcDeclared = resolved.outputs[e.source.handle];
992
1005
  }
993
1006
  if (!effectiveTypeId) {
1007
+ // Infer if not explicitly set
994
1008
  effectiveTypeId = Array.isArray(srcDeclared)
995
1009
  ? srcDeclared[0]
996
1010
  : srcDeclared;
@@ -1005,7 +1019,8 @@ class GraphRuntime {
1005
1019
  id: e.id,
1006
1020
  source: { ...e.source },
1007
1021
  target: { ...e.target },
1008
- typeId: effectiveTypeId ?? "untyped",
1022
+ typeId: e.typeId, // Preserve original (may be undefined)
1023
+ effectiveTypeId: effectiveTypeId ?? "untyped", // Always present
1009
1024
  convert,
1010
1025
  convertAsync,
1011
1026
  srcUnionTypes: Array.isArray(srcDeclared)
@@ -1029,18 +1044,19 @@ class GraphRuntime {
1029
1044
  this.resolvedByNode.set(nodeId, handles);
1030
1045
  // Recompute edge converter/type for edges where this node is source or target
1031
1046
  for (const e of this.edges) {
1032
- let srcDeclared = e.typeId;
1047
+ let srcDeclared = e.effectiveTypeId; // Use effectiveTypeId as fallback
1033
1048
  let dstDeclared = e.dstDeclared;
1049
+ const oldDstDeclared = dstDeclared; // Track old value to detect resolution
1034
1050
  if (e.source.nodeId === nodeId) {
1035
1051
  const resolved = this.resolvedByNode.get(nodeId);
1036
1052
  srcDeclared = resolved
1037
1053
  ? resolved.outputs[e.source.handle]
1038
1054
  : srcDeclared;
1039
- // If edge had no explicit typeId, infer from updated src
1055
+ // Update effectiveTypeId if original wasn't explicit
1040
1056
  if (!e.typeId) {
1041
- e.typeId = Array.isArray(srcDeclared)
1042
- ? srcDeclared?.[0]
1043
- : srcDeclared;
1057
+ e.effectiveTypeId = Array.isArray(srcDeclared)
1058
+ ? srcDeclared?.[0] ?? "untyped"
1059
+ : srcDeclared ?? "untyped";
1044
1060
  }
1045
1061
  }
1046
1062
  if (e.target.nodeId === nodeId) {
@@ -1053,6 +1069,19 @@ class GraphRuntime {
1053
1069
  const conv = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
1054
1070
  e.convert = conv.convert;
1055
1071
  e.convertAsync = conv.convertAsync;
1072
+ // If target handle was just resolved (was undefined, now has a type), re-propagate values
1073
+ if (e.target.nodeId === nodeId &&
1074
+ oldDstDeclared === undefined &&
1075
+ dstDeclared !== undefined) {
1076
+ const srcNode = this.nodes.get(e.source.nodeId);
1077
+ if (srcNode) {
1078
+ const srcValue = srcNode.outputs[e.source.handle];
1079
+ if (srcValue !== undefined) {
1080
+ // Re-propagate through the now-resolved edge converter
1081
+ this.propagate(e.source.nodeId, e.source.handle, srcValue);
1082
+ }
1083
+ }
1084
+ }
1056
1085
  }
1057
1086
  // Invalidate downstream for this node so UI refreshes
1058
1087
  this.invalidateDownstream(nodeId);
@@ -1163,10 +1192,23 @@ class GraphRuntime {
1163
1192
  getGraphDef() {
1164
1193
  const nodes = Array.from(this.nodes.values()).map((n) => {
1165
1194
  const resolved = this.resolvedByNode.get(n.nodeId);
1195
+ // Collect user-provided inputs (inputs without inbound edges)
1196
+ const initialInputs = {};
1197
+ for (const [handle, value] of Object.entries(n.inputs)) {
1198
+ const hasInbound = this.edges.some((e) => e.target.nodeId === n.nodeId && e.target.handle === handle);
1199
+ if (!hasInbound && value !== undefined) {
1200
+ // Clone to avoid shared references
1201
+ initialInputs[handle] =
1202
+ typeof structuredClone === "function"
1203
+ ? structuredClone(value)
1204
+ : JSON.parse(JSON.stringify(value));
1205
+ }
1206
+ }
1166
1207
  return {
1167
1208
  nodeId: n.nodeId,
1168
1209
  typeId: n.typeId,
1169
1210
  params: n.params ? { ...n.params } : undefined,
1211
+ initialInputs: Object.keys(initialInputs).length > 0 ? initialInputs : undefined,
1170
1212
  resolvedHandles: resolved ? { ...resolved } : undefined,
1171
1213
  };
1172
1214
  });
@@ -1174,7 +1216,7 @@ class GraphRuntime {
1174
1216
  id: e.id,
1175
1217
  source: { nodeId: e.source.nodeId, handle: e.source.handle },
1176
1218
  target: { nodeId: e.target.nodeId, handle: e.target.handle },
1177
- typeId: e.typeId && e.typeId !== "untyped" ? e.typeId : undefined,
1219
+ typeId: e.typeId, // Only export original typeId (may be undefined)
1178
1220
  }));
1179
1221
  return { nodes, edges };
1180
1222
  }