@bian-womp/spark-graph 0.2.14 → 0.2.15

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 CHANGED
@@ -433,6 +433,8 @@ class GraphRuntime {
433
433
  static create(def, registry, opts) {
434
434
  const gr = new GraphRuntime();
435
435
  gr.environment = opts?.environment ?? {};
436
+ // Precompute per-node resolved handles (use def-provided overrides; do not compute dynamically here)
437
+ const resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
436
438
  // Instantiate nodes
437
439
  for (const n of def.nodes) {
438
440
  const desc = registry.nodes.get(n.typeId);
@@ -472,46 +474,7 @@ class GraphRuntime {
472
474
  gr.nodes.set(n.nodeId, rn);
473
475
  }
474
476
  // Instantiate edges
475
- gr.edges = def.edges.map((e) => {
476
- // infer type from source output if missing
477
- const srcNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
478
- const dstNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
479
- let effectiveTypeId = e.typeId;
480
- let srcDeclared;
481
- let dstDeclared;
482
- if (srcNode) {
483
- const srcDesc = registry.nodes.get(srcNode.typeId);
484
- if (srcDesc) {
485
- srcDeclared = srcDesc.outputs[e.source.handle];
486
- }
487
- }
488
- if (!effectiveTypeId) {
489
- effectiveTypeId = Array.isArray(srcDeclared)
490
- ? srcDeclared[0]
491
- : srcDeclared;
492
- }
493
- if (dstNode) {
494
- const dstDesc = registry.nodes.get(dstNode.typeId);
495
- if (dstDesc) {
496
- dstDeclared = getInputTypeId(dstDesc.inputs, e.target.handle);
497
- }
498
- }
499
- // Attach dynamic convert/convertAsync aware of union sources and typed outputs
500
- const { convert, convertAsync } = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
501
- return {
502
- id: e.id,
503
- source: { ...e.source },
504
- target: { ...e.target },
505
- typeId: effectiveTypeId ?? "untyped",
506
- convert,
507
- convertAsync,
508
- srcUnionTypes: Array.isArray(srcDeclared)
509
- ? [...srcDeclared]
510
- : undefined,
511
- dstDeclared,
512
- stats: { runs: 0, inFlight: false, progress: 0 },
513
- };
514
- });
477
+ gr.edges = GraphRuntime.buildEdges(def, registry, resolvedByNode);
515
478
  // After nodes and edges exist, seed registry- and graph-level defaults
516
479
  for (const n of def.nodes) {
517
480
  const node = gr.nodes.get(n.nodeId);
@@ -941,6 +904,61 @@ class GraphRuntime {
941
904
  }
942
905
  }
943
906
  }
907
+ // Helper: build map of resolved handles per node from def (prefer def.resolvedHandles, otherwise registry statics)
908
+ static computeResolvedHandleMap(def, registry) {
909
+ const out = new Map();
910
+ for (const n of def.nodes) {
911
+ const desc = registry.nodes.get(n.typeId);
912
+ if (!desc)
913
+ continue;
914
+ const overrideInputs = n.resolvedHandles?.inputs;
915
+ const overrideOutputs = n.resolvedHandles?.outputs;
916
+ // Merge base with overrides (allow partial resolvedHandles)
917
+ const inputs = { ...desc.inputs, ...overrideInputs };
918
+ const outputs = { ...desc.outputs, ...overrideOutputs };
919
+ out.set(n.nodeId, { inputs, outputs });
920
+ }
921
+ return out;
922
+ }
923
+ // Helper: build runtime edges with coercions using resolved handles
924
+ static buildEdges(def, registry, resolvedByNode) {
925
+ return def.edges.map((e) => {
926
+ const srcNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
927
+ const dstNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
928
+ let effectiveTypeId = e.typeId;
929
+ let srcDeclared;
930
+ let dstDeclared;
931
+ if (srcNode) {
932
+ const resolved = resolvedByNode.get(srcNode.nodeId);
933
+ if (resolved)
934
+ srcDeclared = resolved.outputs[e.source.handle];
935
+ }
936
+ if (!effectiveTypeId) {
937
+ effectiveTypeId = Array.isArray(srcDeclared)
938
+ ? srcDeclared[0]
939
+ : srcDeclared;
940
+ }
941
+ if (dstNode) {
942
+ const resolved = resolvedByNode.get(dstNode.nodeId);
943
+ if (resolved)
944
+ dstDeclared = getInputTypeId(resolved.inputs, e.target.handle);
945
+ }
946
+ const { convert, convertAsync } = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
947
+ return {
948
+ id: e.id,
949
+ source: { ...e.source },
950
+ target: { ...e.target },
951
+ typeId: effectiveTypeId ?? "untyped",
952
+ convert,
953
+ convertAsync,
954
+ srcUnionTypes: Array.isArray(srcDeclared)
955
+ ? [...srcDeclared]
956
+ : undefined,
957
+ dstDeclared,
958
+ stats: { runs: 0, inFlight: false, progress: 0 },
959
+ };
960
+ });
961
+ }
944
962
  reemitNodeOutputs(nodeId) {
945
963
  const node = this.nodes.get(nodeId);
946
964
  if (!node)
@@ -1241,42 +1259,10 @@ class GraphRuntime {
1241
1259
  tmap.set(e.source.handle, tset);
1242
1260
  prevOutTargets.set(e.source.nodeId, tmap);
1243
1261
  }
1262
+ // Precompute per-node resolved handles for updated graph
1263
+ const resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
1244
1264
  // Rebuild edges mapping with coercions
1245
- this.edges = def.edges.map((e) => {
1246
- const srcNode = def.nodes.find((nn) => nn.nodeId === e.source.nodeId);
1247
- const dstNode = def.nodes.find((nn) => nn.nodeId === e.target.nodeId);
1248
- let effectiveTypeId = e.typeId;
1249
- let srcDeclared;
1250
- let dstDeclared;
1251
- if (srcNode) {
1252
- const srcDesc = registry.nodes.get(srcNode.typeId);
1253
- if (srcDesc) {
1254
- srcDeclared = srcDesc.outputs[e.source.handle];
1255
- }
1256
- }
1257
- if (!effectiveTypeId) {
1258
- effectiveTypeId = Array.isArray(srcDeclared)
1259
- ? srcDeclared[0]
1260
- : srcDeclared;
1261
- }
1262
- if (dstNode) {
1263
- const dstDesc = registry.nodes.get(dstNode.typeId);
1264
- if (dstDesc) {
1265
- dstDeclared = getInputTypeId(dstDesc.inputs, e.target.handle);
1266
- }
1267
- }
1268
- const { convert, convertAsync } = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
1269
- return {
1270
- id: e.id,
1271
- source: { ...e.source },
1272
- target: { ...e.target },
1273
- typeId: effectiveTypeId ?? "untyped",
1274
- convert,
1275
- convertAsync,
1276
- dstDeclared,
1277
- stats: { runs: 0, inFlight: false, progress: 0 },
1278
- };
1279
- });
1265
+ this.edges = GraphRuntime.buildEdges(def, registry, resolvedByNode);
1280
1266
  // Build new inbound map
1281
1267
  const nextInbound = new Map();
1282
1268
  for (const e of this.edges) {
@@ -2643,6 +2629,31 @@ function setupBasicGraphRegistry() {
2643
2629
  return { Result: arr.slice(s, e) };
2644
2630
  },
2645
2631
  });
2632
+ // Compose array from dynamic item inputs
2633
+ registry.registerNode({
2634
+ id: "base.array.compose",
2635
+ categoryId: "compute",
2636
+ inputs: { Length: "base.float" },
2637
+ outputs: { Items: "base.object" },
2638
+ resolveHandles: ({ params }) => {
2639
+ const maxLen = 64;
2640
+ const raw = params?.Length;
2641
+ const n = Math.max(0, Math.min(maxLen, Math.trunc(Number(raw ?? 0))));
2642
+ if (!Number.isFinite(n))
2643
+ return { inputs: {} };
2644
+ const dyn = {};
2645
+ for (let i = 0; i < n; i++)
2646
+ dyn[`Item${i}`] = { typeId: "base.object" };
2647
+ return { inputs: dyn };
2648
+ },
2649
+ inputDefaults: { Length: 0 },
2650
+ impl: (ins) => {
2651
+ const length = Math.max(0, Math.trunc(Number(ins.Length ?? 0)));
2652
+ if (!Number.isFinite(length))
2653
+ return { Items: [] };
2654
+ return { Items: Array.from({ length }, (_, i) => ins[`Item${i}`]) };
2655
+ },
2656
+ });
2646
2657
  // Select
2647
2658
  registry.registerNode({
2648
2659
  id: "base.select",