@bian-womp/spark-graph 0.2.15 → 0.2.17
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 +142 -179
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/cjs/src/core/types.d.ts +2 -1
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +3 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +142 -179
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/esm/src/core/types.d.ts +2 -1
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +3 -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;
|
|
@@ -434,7 +436,7 @@ class GraphRuntime {
|
|
|
434
436
|
const gr = new GraphRuntime();
|
|
435
437
|
gr.environment = opts?.environment ?? {};
|
|
436
438
|
// Precompute per-node resolved handles (use def-provided overrides; do not compute dynamically here)
|
|
437
|
-
|
|
439
|
+
gr.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
|
|
438
440
|
// Instantiate nodes
|
|
439
441
|
for (const n of def.nodes) {
|
|
440
442
|
const desc = registry.nodes.get(n.typeId);
|
|
@@ -474,7 +476,7 @@ class GraphRuntime {
|
|
|
474
476
|
gr.nodes.set(n.nodeId, rn);
|
|
475
477
|
}
|
|
476
478
|
// Instantiate edges
|
|
477
|
-
gr.edges = GraphRuntime.buildEdges(def, registry, resolvedByNode);
|
|
479
|
+
gr.edges = GraphRuntime.buildEdges(def, registry, gr.resolvedByNode);
|
|
478
480
|
// After nodes and edges exist, seed registry- and graph-level defaults
|
|
479
481
|
for (const n of def.nodes) {
|
|
480
482
|
const node = gr.nodes.get(n.nodeId);
|
|
@@ -967,6 +969,39 @@ class GraphRuntime {
|
|
|
967
969
|
this.propagate(nodeId, handle, value);
|
|
968
970
|
}
|
|
969
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
|
+
}
|
|
970
1005
|
launch() {
|
|
971
1006
|
// call onActivated for nodes that implement it
|
|
972
1007
|
for (const node of this.nodes.values()) {
|
|
@@ -1260,9 +1295,9 @@ class GraphRuntime {
|
|
|
1260
1295
|
prevOutTargets.set(e.source.nodeId, tmap);
|
|
1261
1296
|
}
|
|
1262
1297
|
// Precompute per-node resolved handles for updated graph
|
|
1263
|
-
|
|
1298
|
+
this.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
|
|
1264
1299
|
// Rebuild edges mapping with coercions
|
|
1265
|
-
this.edges = GraphRuntime.buildEdges(def, registry, resolvedByNode);
|
|
1300
|
+
this.edges = GraphRuntime.buildEdges(def, registry, this.resolvedByNode);
|
|
1266
1301
|
// Build new inbound map
|
|
1267
1302
|
const nextInbound = new Map();
|
|
1268
1303
|
for (const e of this.edges) {
|
|
@@ -1405,35 +1440,56 @@ class GraphBuilder {
|
|
|
1405
1440
|
const issues = [];
|
|
1406
1441
|
const nodeIds = new Set();
|
|
1407
1442
|
const edgeIds = new Set();
|
|
1443
|
+
// Precompute effective handle maps (registry statics merged with per-node resolvedHandles)
|
|
1444
|
+
const nodeById = new Map();
|
|
1445
|
+
const effByNodeId = new Map();
|
|
1446
|
+
const getEffectiveHandles = (n) => {
|
|
1447
|
+
if (!n)
|
|
1448
|
+
return { inputs: {}, outputs: {} };
|
|
1449
|
+
const desc = this.registry.nodes.get(n.typeId);
|
|
1450
|
+
const resolved = n.resolvedHandles || {};
|
|
1451
|
+
const inputs = { ...desc?.inputs, ...resolved.inputs };
|
|
1452
|
+
const outputs = { ...desc?.outputs, ...resolved.outputs };
|
|
1453
|
+
return { inputs, outputs };
|
|
1454
|
+
};
|
|
1455
|
+
const normOut = (decl) => Array.isArray(decl) ? decl : decl ? [decl] : [];
|
|
1456
|
+
const inferEdgeType = (srcDeclared, dstDeclared, explicit) => {
|
|
1457
|
+
if (explicit)
|
|
1458
|
+
return explicit;
|
|
1459
|
+
if (Array.isArray(srcDeclared) && dstDeclared)
|
|
1460
|
+
return dstDeclared;
|
|
1461
|
+
if (srcDeclared)
|
|
1462
|
+
return Array.isArray(srcDeclared) ? srcDeclared[0] : srcDeclared;
|
|
1463
|
+
return undefined;
|
|
1464
|
+
};
|
|
1465
|
+
const canFlow = (from, to) => {
|
|
1466
|
+
if (!to || !from)
|
|
1467
|
+
return true;
|
|
1468
|
+
const arr = Array.isArray(from) ? from : [from];
|
|
1469
|
+
return arr.every((s) => s === to || !!this.registry.canCoerce(s, to));
|
|
1470
|
+
};
|
|
1471
|
+
const pushIssue = (level, code, message, data) => {
|
|
1472
|
+
issues.push({ level, code, message, data });
|
|
1473
|
+
};
|
|
1408
1474
|
// nodes exist, ids unique, and categories registered
|
|
1409
1475
|
for (const n of def.nodes) {
|
|
1410
1476
|
if (nodeIds.has(n.nodeId)) {
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
code: "NODE_ID_DUP",
|
|
1414
|
-
message: `Duplicate nodeId ${n.nodeId}`,
|
|
1415
|
-
data: { nodeId: n.nodeId },
|
|
1477
|
+
pushIssue("error", "NODE_ID_DUP", `Duplicate nodeId ${n.nodeId}`, {
|
|
1478
|
+
nodeId: n.nodeId,
|
|
1416
1479
|
});
|
|
1417
1480
|
}
|
|
1418
1481
|
else {
|
|
1419
1482
|
nodeIds.add(n.nodeId);
|
|
1420
1483
|
}
|
|
1484
|
+
nodeById.set(n.nodeId, n);
|
|
1485
|
+
effByNodeId.set(n.nodeId, getEffectiveHandles(n));
|
|
1421
1486
|
const nodeType = this.registry.nodes.get(n.typeId);
|
|
1422
1487
|
if (!nodeType) {
|
|
1423
|
-
|
|
1424
|
-
level: "error",
|
|
1425
|
-
code: "NODE_TYPE_MISSING",
|
|
1426
|
-
message: `Unknown node type ${n.typeId}`,
|
|
1427
|
-
data: { typeId: n.typeId, nodeId: n.nodeId },
|
|
1428
|
-
});
|
|
1488
|
+
pushIssue("error", "NODE_TYPE_MISSING", `Unknown node type ${n.typeId}`, { typeId: n.typeId, nodeId: n.nodeId });
|
|
1429
1489
|
continue;
|
|
1430
1490
|
}
|
|
1431
1491
|
if (!this.registry.categories.has(nodeType.categoryId)) {
|
|
1432
|
-
|
|
1433
|
-
level: "error",
|
|
1434
|
-
code: "CATEGORY_MISSING",
|
|
1435
|
-
message: `Unknown category ${nodeType.categoryId} for node type ${n.typeId}`,
|
|
1436
|
-
});
|
|
1492
|
+
pushIssue("error", "CATEGORY_MISSING", `Unknown category ${nodeType.categoryId} for node type ${n.typeId}`);
|
|
1437
1493
|
}
|
|
1438
1494
|
}
|
|
1439
1495
|
// edges validation: nodes exist, handles exist, type exists
|
|
@@ -1442,186 +1498,96 @@ class GraphBuilder {
|
|
|
1442
1498
|
const inboundArrayOk = new Set();
|
|
1443
1499
|
for (const e of def.edges) {
|
|
1444
1500
|
if (edgeIds.has(e.id)) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
code: "EDGE_ID_DUP",
|
|
1448
|
-
message: `Duplicate edge id ${e.id}`,
|
|
1449
|
-
data: { edgeId: e.id },
|
|
1501
|
+
pushIssue("error", "EDGE_ID_DUP", `Duplicate edge id ${e.id}`, {
|
|
1502
|
+
edgeId: e.id,
|
|
1450
1503
|
});
|
|
1451
1504
|
}
|
|
1452
1505
|
else {
|
|
1453
1506
|
edgeIds.add(e.id);
|
|
1454
1507
|
}
|
|
1455
|
-
const srcNode =
|
|
1456
|
-
const dstNode =
|
|
1508
|
+
const srcNode = nodeById.get(e.source.nodeId);
|
|
1509
|
+
const dstNode = nodeById.get(e.target.nodeId);
|
|
1457
1510
|
if (!srcNode)
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
code: "EDGE_SOURCE_MISSING",
|
|
1461
|
-
message: `Edge ${e.id} source node missing`,
|
|
1462
|
-
data: { edgeId: e.id },
|
|
1511
|
+
pushIssue("error", "EDGE_SOURCE_MISSING", `Edge ${e.id} source node missing`, {
|
|
1512
|
+
edgeId: e.id,
|
|
1463
1513
|
});
|
|
1464
1514
|
if (!dstNode)
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
code: "EDGE_TARGET_MISSING",
|
|
1468
|
-
message: `Edge ${e.id} target node missing`,
|
|
1469
|
-
data: { edgeId: e.id },
|
|
1515
|
+
pushIssue("error", "EDGE_TARGET_MISSING", `Edge ${e.id} target node missing`, {
|
|
1516
|
+
edgeId: e.id,
|
|
1470
1517
|
});
|
|
1471
|
-
//
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
if (!effectiveTypeId) {
|
|
1486
|
-
if (Array.isArray(_srcDeclared) && _dstDeclared) {
|
|
1487
|
-
// When source is a union and target input type is known, adopt the input type
|
|
1488
|
-
// so validation checks are performed against the target, not an arbitrary variant.
|
|
1489
|
-
effectiveTypeId = _dstDeclared;
|
|
1490
|
-
}
|
|
1491
|
-
else if (_srcDeclared) {
|
|
1492
|
-
effectiveTypeId = Array.isArray(_srcDeclared)
|
|
1493
|
-
? _srcDeclared[0]
|
|
1494
|
-
: _srcDeclared;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1518
|
+
// Effective handle declarations
|
|
1519
|
+
const srcEff = effByNodeId.get(e.source.nodeId) || {
|
|
1520
|
+
outputs: {},
|
|
1521
|
+
};
|
|
1522
|
+
const dstEff = effByNodeId.get(e.target.nodeId) || {
|
|
1523
|
+
inputs: {}};
|
|
1524
|
+
const _srcDeclared = srcNode
|
|
1525
|
+
? srcEff.outputs[e.source.handle]
|
|
1526
|
+
: undefined;
|
|
1527
|
+
const _dstDeclared = dstNode
|
|
1528
|
+
? getInputTypeId(dstEff.inputs, e.target.handle)
|
|
1529
|
+
: undefined;
|
|
1530
|
+
// Effective edge type
|
|
1531
|
+
const effectiveTypeId = inferEdgeType(_srcDeclared, _dstDeclared, e.typeId);
|
|
1497
1532
|
const type = effectiveTypeId
|
|
1498
1533
|
? this.registry.types.get(effectiveTypeId)
|
|
1499
1534
|
: undefined;
|
|
1500
1535
|
if (!type) {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
code: "TYPE_MISSING",
|
|
1504
|
-
message: `Edge ${e.id} type missing or unknown`,
|
|
1505
|
-
data: { edgeId: e.id },
|
|
1536
|
+
pushIssue("error", "TYPE_MISSING", `Edge ${e.id} type missing or unknown`, {
|
|
1537
|
+
edgeId: e.id,
|
|
1506
1538
|
});
|
|
1507
1539
|
}
|
|
1508
1540
|
if (srcNode) {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
issues.push({
|
|
1512
|
-
level: "error",
|
|
1513
|
-
code: "OUTPUT_MISSING",
|
|
1514
|
-
message: `Edge ${e.id} source output ${e.source.handle} missing on ${srcNode.typeId}`,
|
|
1515
|
-
data: {
|
|
1516
|
-
edgeId: e.id,
|
|
1517
|
-
nodeId: srcNode.nodeId,
|
|
1518
|
-
output: e.source.handle,
|
|
1519
|
-
},
|
|
1520
|
-
});
|
|
1541
|
+
if (!(e.source.handle in srcEff.outputs)) {
|
|
1542
|
+
pushIssue("error", "OUTPUT_MISSING", `Edge ${e.id} source output ${e.source.handle} missing on ${srcNode.typeId}`, { edgeId: e.id, nodeId: srcNode.nodeId, output: e.source.handle });
|
|
1521
1543
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
issues.push({
|
|
1534
|
-
level: "error",
|
|
1535
|
-
code: "TYPE_MISMATCH_OUTPUT",
|
|
1536
|
-
message: `Edge ${e.id} type ${effectiveTypeId} mismatches source output ${srcNode.typeId}.${e.source.handle} (${s}) and no coercion exists`,
|
|
1537
|
-
data: {
|
|
1538
|
-
edgeId: e.id,
|
|
1539
|
-
nodeId: srcNode.nodeId,
|
|
1540
|
-
output: e.source.handle,
|
|
1541
|
-
declared: s,
|
|
1542
|
-
effectiveTypeId,
|
|
1543
|
-
},
|
|
1544
|
-
});
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1544
|
+
const declaredArr = normOut(srcEff.outputs[e.source.handle]);
|
|
1545
|
+
if (declaredArr.length > 0 &&
|
|
1546
|
+
effectiveTypeId &&
|
|
1547
|
+
!canFlow(declaredArr, effectiveTypeId)) {
|
|
1548
|
+
pushIssue("error", "TYPE_MISMATCH_OUTPUT", `Edge ${e.id} type ${effectiveTypeId} mismatches source output ${srcNode.typeId}.${e.source.handle} (${declaredArr.join("|")}) and no coercion exists`, {
|
|
1549
|
+
edgeId: e.id,
|
|
1550
|
+
nodeId: srcNode.nodeId,
|
|
1551
|
+
output: e.source.handle,
|
|
1552
|
+
declared: declaredArr.join("|"),
|
|
1553
|
+
effectiveTypeId,
|
|
1554
|
+
});
|
|
1548
1555
|
}
|
|
1549
1556
|
}
|
|
1550
1557
|
if (dstNode) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
issues.push({
|
|
1554
|
-
level: "error",
|
|
1555
|
-
code: "INPUT_MISSING",
|
|
1556
|
-
message: `Edge ${e.id} target input ${e.target.handle} missing on ${dstNode.typeId}`,
|
|
1557
|
-
data: {
|
|
1558
|
-
edgeId: e.id,
|
|
1559
|
-
nodeId: dstNode.nodeId,
|
|
1560
|
-
input: e.target.handle,
|
|
1561
|
-
},
|
|
1562
|
-
});
|
|
1558
|
+
if (!(e.target.handle in dstEff.inputs)) {
|
|
1559
|
+
pushIssue("error", "INPUT_MISSING", `Edge ${e.id} target input ${e.target.handle} missing on ${dstNode.typeId}`, { edgeId: e.id, nodeId: dstNode.nodeId, input: e.target.handle });
|
|
1563
1560
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1561
|
+
// Private inputs should not accept edges
|
|
1562
|
+
if (isInputPrivate(dstEff.inputs, e.target.handle)) {
|
|
1563
|
+
pushIssue("error", "INPUT_PRIVATE", `Edge ${e.id} targets private input ${dstNode.typeId}.${e.target.handle}`, { edgeId: e.id, nodeId: dstNode.nodeId, input: e.target.handle });
|
|
1564
|
+
}
|
|
1565
|
+
const declaredIn = getInputTypeId(dstEff.inputs, e.target.handle);
|
|
1566
|
+
if (declaredIn && effectiveTypeId) {
|
|
1567
|
+
if (srcNode) {
|
|
1568
|
+
const srcDeclared = srcEff.outputs[e.source.handle];
|
|
1569
|
+
const srcArr = normOut(srcDeclared).length
|
|
1570
|
+
? normOut(srcDeclared)
|
|
1571
|
+
: [effectiveTypeId];
|
|
1572
|
+
if (!canFlow(srcArr, declaredIn)) {
|
|
1573
|
+
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} output type ${srcArr.join("|")} not convertible to target input ${dstNode.typeId}.${e.target.handle} (${declaredIn})`, {
|
|
1572
1574
|
edgeId: e.id,
|
|
1573
1575
|
nodeId: dstNode.nodeId,
|
|
1574
1576
|
input: e.target.handle,
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
}
|
|
1578
|
-
const declaredIn = getInputTypeId(dstType.inputs, e.target.handle);
|
|
1579
|
-
if (declaredIn && effectiveTypeId) {
|
|
1580
|
-
// If source is a union, ensure each variant can reach declaredIn
|
|
1581
|
-
if (srcNode) {
|
|
1582
|
-
const srcType = this.registry.nodes.get(srcNode.typeId);
|
|
1583
|
-
const srcDeclared = srcType?.outputs[e.source.handle];
|
|
1584
|
-
const srcArr = Array.isArray(srcDeclared)
|
|
1585
|
-
? srcDeclared
|
|
1586
|
-
: srcDeclared
|
|
1587
|
-
? [srcDeclared]
|
|
1588
|
-
: effectiveTypeId
|
|
1589
|
-
? [effectiveTypeId]
|
|
1590
|
-
: [];
|
|
1591
|
-
for (const s of srcArr) {
|
|
1592
|
-
if (s !== declaredIn &&
|
|
1593
|
-
!this.registry.canCoerce(s, declaredIn)) {
|
|
1594
|
-
issues.push({
|
|
1595
|
-
level: "error",
|
|
1596
|
-
code: "TYPE_MISMATCH_INPUT",
|
|
1597
|
-
message: `Edge ${e.id} output type ${s} not convertible to target input ${dstNode.typeId}.${e.target.handle} (${declaredIn})`,
|
|
1598
|
-
data: {
|
|
1599
|
-
edgeId: e.id,
|
|
1600
|
-
nodeId: dstNode.nodeId,
|
|
1601
|
-
input: e.target.handle,
|
|
1602
|
-
declared: declaredIn,
|
|
1603
|
-
effectiveTypeId: s,
|
|
1604
|
-
},
|
|
1605
|
-
});
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
else if (declaredIn !== effectiveTypeId &&
|
|
1610
|
-
!this.registry.canCoerce(effectiveTypeId, declaredIn)) {
|
|
1611
|
-
issues.push({
|
|
1612
|
-
level: "error",
|
|
1613
|
-
code: "TYPE_MISMATCH_INPUT",
|
|
1614
|
-
message: `Edge ${e.id} type ${effectiveTypeId} mismatches target input ${dstNode.typeId}.${e.target.handle} (${declaredIn}) and no coercion exists`,
|
|
1615
|
-
data: {
|
|
1616
|
-
edgeId: e.id,
|
|
1617
|
-
nodeId: dstNode.nodeId,
|
|
1618
|
-
input: e.target.handle,
|
|
1619
|
-
declared: declaredIn,
|
|
1620
|
-
effectiveTypeId,
|
|
1621
|
-
},
|
|
1577
|
+
declared: declaredIn,
|
|
1578
|
+
effectiveTypeId: srcArr.join("|"),
|
|
1622
1579
|
});
|
|
1623
1580
|
}
|
|
1624
1581
|
}
|
|
1582
|
+
else if (!canFlow(effectiveTypeId, declaredIn)) {
|
|
1583
|
+
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} type ${effectiveTypeId} mismatches target input ${dstNode.typeId}.${e.target.handle} (${declaredIn}) and no coercion exists`, {
|
|
1584
|
+
edgeId: e.id,
|
|
1585
|
+
nodeId: dstNode.nodeId,
|
|
1586
|
+
input: e.target.handle,
|
|
1587
|
+
declared: declaredIn,
|
|
1588
|
+
effectiveTypeId,
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1625
1591
|
}
|
|
1626
1592
|
}
|
|
1627
1593
|
// Track multiple inbound edges targeting the same input handle
|
|
@@ -1629,10 +1595,7 @@ class GraphBuilder {
|
|
|
1629
1595
|
inboundCounts.set(inboundKey, (inboundCounts.get(inboundKey) ?? 0) + 1);
|
|
1630
1596
|
// If the target input is declared as an array type, allow multi-inbound (runtime will append)
|
|
1631
1597
|
if (dstNode) {
|
|
1632
|
-
const
|
|
1633
|
-
const declaredIn = dstType
|
|
1634
|
-
? getInputTypeId(dstType.inputs, e.target.handle)
|
|
1635
|
-
: undefined;
|
|
1598
|
+
const declaredIn = getInputTypeId((effByNodeId.get(dstNode.nodeId) || { inputs: {} }).inputs, e.target.handle);
|
|
1636
1599
|
if (typeof declaredIn === "string" && declaredIn.endsWith("[]")) {
|
|
1637
1600
|
inboundArrayOk.add(inboundKey);
|
|
1638
1601
|
}
|
|
@@ -2635,9 +2598,9 @@ function setupBasicGraphRegistry() {
|
|
|
2635
2598
|
categoryId: "compute",
|
|
2636
2599
|
inputs: { Length: "base.float" },
|
|
2637
2600
|
outputs: { Items: "base.object" },
|
|
2638
|
-
resolveHandles: ({
|
|
2601
|
+
resolveHandles: ({ inputs }) => {
|
|
2639
2602
|
const maxLen = 64;
|
|
2640
|
-
const raw =
|
|
2603
|
+
const raw = inputs?.Length ?? 0;
|
|
2641
2604
|
const n = Math.max(0, Math.min(maxLen, Math.trunc(Number(raw ?? 0))));
|
|
2642
2605
|
if (!Number.isFinite(n))
|
|
2643
2606
|
return { inputs: {} };
|