@bian-womp/spark-graph 0.2.14 → 0.2.16
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 +121 -75
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/types.d.ts +13 -0
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/misc/base.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +5 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +121 -75
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/types.d.ts +13 -0
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/misc/base.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +5 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -406,6 +406,8 @@ class GraphRuntime {
|
|
|
406
406
|
constructor() {
|
|
407
407
|
this.nodes = new Map();
|
|
408
408
|
this.edges = [];
|
|
409
|
+
// Current resolved handles per node (registry statics merged with per-node overrides)
|
|
410
|
+
this.resolvedByNode = new Map();
|
|
409
411
|
this.listeners = new Map();
|
|
410
412
|
this.environment = {};
|
|
411
413
|
this.paused = false;
|
|
@@ -433,6 +435,8 @@ class GraphRuntime {
|
|
|
433
435
|
static create(def, registry, opts) {
|
|
434
436
|
const gr = new GraphRuntime();
|
|
435
437
|
gr.environment = opts?.environment ?? {};
|
|
438
|
+
// Precompute per-node resolved handles (use def-provided overrides; do not compute dynamically here)
|
|
439
|
+
gr.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
|
|
436
440
|
// Instantiate nodes
|
|
437
441
|
for (const n of def.nodes) {
|
|
438
442
|
const desc = registry.nodes.get(n.typeId);
|
|
@@ -472,46 +476,7 @@ class GraphRuntime {
|
|
|
472
476
|
gr.nodes.set(n.nodeId, rn);
|
|
473
477
|
}
|
|
474
478
|
// Instantiate edges
|
|
475
|
-
gr.edges =
|
|
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
|
-
});
|
|
479
|
+
gr.edges = GraphRuntime.buildEdges(def, registry, gr.resolvedByNode);
|
|
515
480
|
// After nodes and edges exist, seed registry- and graph-level defaults
|
|
516
481
|
for (const n of def.nodes) {
|
|
517
482
|
const node = gr.nodes.get(n.nodeId);
|
|
@@ -941,6 +906,61 @@ class GraphRuntime {
|
|
|
941
906
|
}
|
|
942
907
|
}
|
|
943
908
|
}
|
|
909
|
+
// Helper: build map of resolved handles per node from def (prefer def.resolvedHandles, otherwise registry statics)
|
|
910
|
+
static computeResolvedHandleMap(def, registry) {
|
|
911
|
+
const out = new Map();
|
|
912
|
+
for (const n of def.nodes) {
|
|
913
|
+
const desc = registry.nodes.get(n.typeId);
|
|
914
|
+
if (!desc)
|
|
915
|
+
continue;
|
|
916
|
+
const overrideInputs = n.resolvedHandles?.inputs;
|
|
917
|
+
const overrideOutputs = n.resolvedHandles?.outputs;
|
|
918
|
+
// Merge base with overrides (allow partial resolvedHandles)
|
|
919
|
+
const inputs = { ...desc.inputs, ...overrideInputs };
|
|
920
|
+
const outputs = { ...desc.outputs, ...overrideOutputs };
|
|
921
|
+
out.set(n.nodeId, { inputs, outputs });
|
|
922
|
+
}
|
|
923
|
+
return out;
|
|
924
|
+
}
|
|
925
|
+
// Helper: build runtime edges with coercions using resolved handles
|
|
926
|
+
static buildEdges(def, registry, resolvedByNode) {
|
|
927
|
+
return def.edges.map((e) => {
|
|
928
|
+
const srcNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
|
|
929
|
+
const dstNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
|
|
930
|
+
let effectiveTypeId = e.typeId;
|
|
931
|
+
let srcDeclared;
|
|
932
|
+
let dstDeclared;
|
|
933
|
+
if (srcNode) {
|
|
934
|
+
const resolved = resolvedByNode.get(srcNode.nodeId);
|
|
935
|
+
if (resolved)
|
|
936
|
+
srcDeclared = resolved.outputs[e.source.handle];
|
|
937
|
+
}
|
|
938
|
+
if (!effectiveTypeId) {
|
|
939
|
+
effectiveTypeId = Array.isArray(srcDeclared)
|
|
940
|
+
? srcDeclared[0]
|
|
941
|
+
: srcDeclared;
|
|
942
|
+
}
|
|
943
|
+
if (dstNode) {
|
|
944
|
+
const resolved = resolvedByNode.get(dstNode.nodeId);
|
|
945
|
+
if (resolved)
|
|
946
|
+
dstDeclared = getInputTypeId(resolved.inputs, e.target.handle);
|
|
947
|
+
}
|
|
948
|
+
const { convert, convertAsync } = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
|
|
949
|
+
return {
|
|
950
|
+
id: e.id,
|
|
951
|
+
source: { ...e.source },
|
|
952
|
+
target: { ...e.target },
|
|
953
|
+
typeId: effectiveTypeId ?? "untyped",
|
|
954
|
+
convert,
|
|
955
|
+
convertAsync,
|
|
956
|
+
srcUnionTypes: Array.isArray(srcDeclared)
|
|
957
|
+
? [...srcDeclared]
|
|
958
|
+
: undefined,
|
|
959
|
+
dstDeclared,
|
|
960
|
+
stats: { runs: 0, inFlight: false, progress: 0 },
|
|
961
|
+
};
|
|
962
|
+
});
|
|
963
|
+
}
|
|
944
964
|
reemitNodeOutputs(nodeId) {
|
|
945
965
|
const node = this.nodes.get(nodeId);
|
|
946
966
|
if (!node)
|
|
@@ -949,6 +969,39 @@ class GraphRuntime {
|
|
|
949
969
|
this.propagate(nodeId, handle, value);
|
|
950
970
|
}
|
|
951
971
|
}
|
|
972
|
+
// Update resolved handles for a single node and refresh edge converters/types that touch it
|
|
973
|
+
updateNodeHandles(nodeId, handles, registry) {
|
|
974
|
+
this.resolvedByNode.set(nodeId, handles);
|
|
975
|
+
// Recompute edge converter/type for edges where this node is source or target
|
|
976
|
+
for (const e of this.edges) {
|
|
977
|
+
let srcDeclared = e.typeId;
|
|
978
|
+
let dstDeclared = e.dstDeclared;
|
|
979
|
+
if (e.source.nodeId === nodeId) {
|
|
980
|
+
const resolved = this.resolvedByNode.get(nodeId);
|
|
981
|
+
srcDeclared = resolved
|
|
982
|
+
? resolved.outputs[e.source.handle]
|
|
983
|
+
: srcDeclared;
|
|
984
|
+
// If edge had no explicit typeId, infer from updated src
|
|
985
|
+
if (!e.typeId) {
|
|
986
|
+
e.typeId = Array.isArray(srcDeclared)
|
|
987
|
+
? srcDeclared?.[0]
|
|
988
|
+
: srcDeclared;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (e.target.nodeId === nodeId) {
|
|
992
|
+
const resolved = this.resolvedByNode.get(nodeId);
|
|
993
|
+
if (resolved) {
|
|
994
|
+
dstDeclared = getInputTypeId(resolved.inputs, e.target.handle);
|
|
995
|
+
e.dstDeclared = dstDeclared;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
const conv = GraphRuntime.buildEdgeConverters(srcDeclared, dstDeclared, registry);
|
|
999
|
+
e.convert = conv.convert;
|
|
1000
|
+
e.convertAsync = conv.convertAsync;
|
|
1001
|
+
}
|
|
1002
|
+
// Invalidate downstream for this node so UI refreshes
|
|
1003
|
+
this.invalidateDownstream(nodeId);
|
|
1004
|
+
}
|
|
952
1005
|
launch() {
|
|
953
1006
|
// call onActivated for nodes that implement it
|
|
954
1007
|
for (const node of this.nodes.values()) {
|
|
@@ -1241,42 +1294,10 @@ class GraphRuntime {
|
|
|
1241
1294
|
tmap.set(e.source.handle, tset);
|
|
1242
1295
|
prevOutTargets.set(e.source.nodeId, tmap);
|
|
1243
1296
|
}
|
|
1297
|
+
// Precompute per-node resolved handles for updated graph
|
|
1298
|
+
this.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
|
|
1244
1299
|
// Rebuild edges mapping with coercions
|
|
1245
|
-
this.edges =
|
|
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
|
-
});
|
|
1300
|
+
this.edges = GraphRuntime.buildEdges(def, registry, this.resolvedByNode);
|
|
1280
1301
|
// Build new inbound map
|
|
1281
1302
|
const nextInbound = new Map();
|
|
1282
1303
|
for (const e of this.edges) {
|
|
@@ -2643,6 +2664,31 @@ function setupBasicGraphRegistry() {
|
|
|
2643
2664
|
return { Result: arr.slice(s, e) };
|
|
2644
2665
|
},
|
|
2645
2666
|
});
|
|
2667
|
+
// Compose array from dynamic item inputs
|
|
2668
|
+
registry.registerNode({
|
|
2669
|
+
id: "base.array.compose",
|
|
2670
|
+
categoryId: "compute",
|
|
2671
|
+
inputs: { Length: "base.float" },
|
|
2672
|
+
outputs: { Items: "base.object" },
|
|
2673
|
+
resolveHandles: ({ inputs }) => {
|
|
2674
|
+
const maxLen = 64;
|
|
2675
|
+
const raw = inputs?.Length ?? 0;
|
|
2676
|
+
const n = Math.max(0, Math.min(maxLen, Math.trunc(Number(raw ?? 0))));
|
|
2677
|
+
if (!Number.isFinite(n))
|
|
2678
|
+
return { inputs: {} };
|
|
2679
|
+
const dyn = {};
|
|
2680
|
+
for (let i = 0; i < n; i++)
|
|
2681
|
+
dyn[`Item${i}`] = { typeId: "base.object" };
|
|
2682
|
+
return { inputs: dyn };
|
|
2683
|
+
},
|
|
2684
|
+
inputDefaults: { Length: 0 },
|
|
2685
|
+
impl: (ins) => {
|
|
2686
|
+
const length = Math.max(0, Math.trunc(Number(ins.Length ?? 0)));
|
|
2687
|
+
if (!Number.isFinite(length))
|
|
2688
|
+
return { Items: [] };
|
|
2689
|
+
return { Items: Array.from({ length }, (_, i) => ins[`Item${i}`]) };
|
|
2690
|
+
},
|
|
2691
|
+
});
|
|
2646
2692
|
// Select
|
|
2647
2693
|
registry.registerNode({
|
|
2648
2694
|
id: "base.select",
|