@bian-womp/spark-graph 0.2.16 → 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 +101 -173
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/esm/index.js +101 -173
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/builder/GraphBuilder.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphBuilder.d.ts","sourceRoot":"","sources":["../../../../src/builder/GraphBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"GraphBuilder.d.ts","sourceRoot":"","sources":["../../../../src/builder/GraphBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EAGf,kBAAkB,EAEnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AACD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ;IAEtC,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,gBAAgB;IAiRhD,KAAK,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,YAAY;IAI9D,UAAU,CACR,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC3D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC7D,EACD,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,GACnB,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;CAkC1C"}
|
package/lib/esm/index.js
CHANGED
|
@@ -1438,35 +1438,56 @@ class GraphBuilder {
|
|
|
1438
1438
|
const issues = [];
|
|
1439
1439
|
const nodeIds = new Set();
|
|
1440
1440
|
const edgeIds = new Set();
|
|
1441
|
+
// Precompute effective handle maps (registry statics merged with per-node resolvedHandles)
|
|
1442
|
+
const nodeById = new Map();
|
|
1443
|
+
const effByNodeId = new Map();
|
|
1444
|
+
const getEffectiveHandles = (n) => {
|
|
1445
|
+
if (!n)
|
|
1446
|
+
return { inputs: {}, outputs: {} };
|
|
1447
|
+
const desc = this.registry.nodes.get(n.typeId);
|
|
1448
|
+
const resolved = n.resolvedHandles || {};
|
|
1449
|
+
const inputs = { ...desc?.inputs, ...resolved.inputs };
|
|
1450
|
+
const outputs = { ...desc?.outputs, ...resolved.outputs };
|
|
1451
|
+
return { inputs, outputs };
|
|
1452
|
+
};
|
|
1453
|
+
const normOut = (decl) => Array.isArray(decl) ? decl : decl ? [decl] : [];
|
|
1454
|
+
const inferEdgeType = (srcDeclared, dstDeclared, explicit) => {
|
|
1455
|
+
if (explicit)
|
|
1456
|
+
return explicit;
|
|
1457
|
+
if (Array.isArray(srcDeclared) && dstDeclared)
|
|
1458
|
+
return dstDeclared;
|
|
1459
|
+
if (srcDeclared)
|
|
1460
|
+
return Array.isArray(srcDeclared) ? srcDeclared[0] : srcDeclared;
|
|
1461
|
+
return undefined;
|
|
1462
|
+
};
|
|
1463
|
+
const canFlow = (from, to) => {
|
|
1464
|
+
if (!to || !from)
|
|
1465
|
+
return true;
|
|
1466
|
+
const arr = Array.isArray(from) ? from : [from];
|
|
1467
|
+
return arr.every((s) => s === to || !!this.registry.canCoerce(s, to));
|
|
1468
|
+
};
|
|
1469
|
+
const pushIssue = (level, code, message, data) => {
|
|
1470
|
+
issues.push({ level, code, message, data });
|
|
1471
|
+
};
|
|
1441
1472
|
// nodes exist, ids unique, and categories registered
|
|
1442
1473
|
for (const n of def.nodes) {
|
|
1443
1474
|
if (nodeIds.has(n.nodeId)) {
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
code: "NODE_ID_DUP",
|
|
1447
|
-
message: `Duplicate nodeId ${n.nodeId}`,
|
|
1448
|
-
data: { nodeId: n.nodeId },
|
|
1475
|
+
pushIssue("error", "NODE_ID_DUP", `Duplicate nodeId ${n.nodeId}`, {
|
|
1476
|
+
nodeId: n.nodeId,
|
|
1449
1477
|
});
|
|
1450
1478
|
}
|
|
1451
1479
|
else {
|
|
1452
1480
|
nodeIds.add(n.nodeId);
|
|
1453
1481
|
}
|
|
1482
|
+
nodeById.set(n.nodeId, n);
|
|
1483
|
+
effByNodeId.set(n.nodeId, getEffectiveHandles(n));
|
|
1454
1484
|
const nodeType = this.registry.nodes.get(n.typeId);
|
|
1455
1485
|
if (!nodeType) {
|
|
1456
|
-
|
|
1457
|
-
level: "error",
|
|
1458
|
-
code: "NODE_TYPE_MISSING",
|
|
1459
|
-
message: `Unknown node type ${n.typeId}`,
|
|
1460
|
-
data: { typeId: n.typeId, nodeId: n.nodeId },
|
|
1461
|
-
});
|
|
1486
|
+
pushIssue("error", "NODE_TYPE_MISSING", `Unknown node type ${n.typeId}`, { typeId: n.typeId, nodeId: n.nodeId });
|
|
1462
1487
|
continue;
|
|
1463
1488
|
}
|
|
1464
1489
|
if (!this.registry.categories.has(nodeType.categoryId)) {
|
|
1465
|
-
|
|
1466
|
-
level: "error",
|
|
1467
|
-
code: "CATEGORY_MISSING",
|
|
1468
|
-
message: `Unknown category ${nodeType.categoryId} for node type ${n.typeId}`,
|
|
1469
|
-
});
|
|
1490
|
+
pushIssue("error", "CATEGORY_MISSING", `Unknown category ${nodeType.categoryId} for node type ${n.typeId}`);
|
|
1470
1491
|
}
|
|
1471
1492
|
}
|
|
1472
1493
|
// edges validation: nodes exist, handles exist, type exists
|
|
@@ -1475,186 +1496,96 @@ class GraphBuilder {
|
|
|
1475
1496
|
const inboundArrayOk = new Set();
|
|
1476
1497
|
for (const e of def.edges) {
|
|
1477
1498
|
if (edgeIds.has(e.id)) {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
code: "EDGE_ID_DUP",
|
|
1481
|
-
message: `Duplicate edge id ${e.id}`,
|
|
1482
|
-
data: { edgeId: e.id },
|
|
1499
|
+
pushIssue("error", "EDGE_ID_DUP", `Duplicate edge id ${e.id}`, {
|
|
1500
|
+
edgeId: e.id,
|
|
1483
1501
|
});
|
|
1484
1502
|
}
|
|
1485
1503
|
else {
|
|
1486
1504
|
edgeIds.add(e.id);
|
|
1487
1505
|
}
|
|
1488
|
-
const srcNode =
|
|
1489
|
-
const dstNode =
|
|
1506
|
+
const srcNode = nodeById.get(e.source.nodeId);
|
|
1507
|
+
const dstNode = nodeById.get(e.target.nodeId);
|
|
1490
1508
|
if (!srcNode)
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
code: "EDGE_SOURCE_MISSING",
|
|
1494
|
-
message: `Edge ${e.id} source node missing`,
|
|
1495
|
-
data: { edgeId: e.id },
|
|
1509
|
+
pushIssue("error", "EDGE_SOURCE_MISSING", `Edge ${e.id} source node missing`, {
|
|
1510
|
+
edgeId: e.id,
|
|
1496
1511
|
});
|
|
1497
1512
|
if (!dstNode)
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
code: "EDGE_TARGET_MISSING",
|
|
1501
|
-
message: `Edge ${e.id} target node missing`,
|
|
1502
|
-
data: { edgeId: e.id },
|
|
1513
|
+
pushIssue("error", "EDGE_TARGET_MISSING", `Edge ${e.id} target node missing`, {
|
|
1514
|
+
edgeId: e.id,
|
|
1503
1515
|
});
|
|
1504
|
-
//
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
if (!effectiveTypeId) {
|
|
1519
|
-
if (Array.isArray(_srcDeclared) && _dstDeclared) {
|
|
1520
|
-
// When source is a union and target input type is known, adopt the input type
|
|
1521
|
-
// so validation checks are performed against the target, not an arbitrary variant.
|
|
1522
|
-
effectiveTypeId = _dstDeclared;
|
|
1523
|
-
}
|
|
1524
|
-
else if (_srcDeclared) {
|
|
1525
|
-
effectiveTypeId = Array.isArray(_srcDeclared)
|
|
1526
|
-
? _srcDeclared[0]
|
|
1527
|
-
: _srcDeclared;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1516
|
+
// Effective handle declarations
|
|
1517
|
+
const srcEff = effByNodeId.get(e.source.nodeId) || {
|
|
1518
|
+
outputs: {},
|
|
1519
|
+
};
|
|
1520
|
+
const dstEff = effByNodeId.get(e.target.nodeId) || {
|
|
1521
|
+
inputs: {}};
|
|
1522
|
+
const _srcDeclared = srcNode
|
|
1523
|
+
? srcEff.outputs[e.source.handle]
|
|
1524
|
+
: undefined;
|
|
1525
|
+
const _dstDeclared = dstNode
|
|
1526
|
+
? getInputTypeId(dstEff.inputs, e.target.handle)
|
|
1527
|
+
: undefined;
|
|
1528
|
+
// Effective edge type
|
|
1529
|
+
const effectiveTypeId = inferEdgeType(_srcDeclared, _dstDeclared, e.typeId);
|
|
1530
1530
|
const type = effectiveTypeId
|
|
1531
1531
|
? this.registry.types.get(effectiveTypeId)
|
|
1532
1532
|
: undefined;
|
|
1533
1533
|
if (!type) {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
code: "TYPE_MISSING",
|
|
1537
|
-
message: `Edge ${e.id} type missing or unknown`,
|
|
1538
|
-
data: { edgeId: e.id },
|
|
1534
|
+
pushIssue("error", "TYPE_MISSING", `Edge ${e.id} type missing or unknown`, {
|
|
1535
|
+
edgeId: e.id,
|
|
1539
1536
|
});
|
|
1540
1537
|
}
|
|
1541
1538
|
if (srcNode) {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
issues.push({
|
|
1545
|
-
level: "error",
|
|
1546
|
-
code: "OUTPUT_MISSING",
|
|
1547
|
-
message: `Edge ${e.id} source output ${e.source.handle} missing on ${srcNode.typeId}`,
|
|
1548
|
-
data: {
|
|
1549
|
-
edgeId: e.id,
|
|
1550
|
-
nodeId: srcNode.nodeId,
|
|
1551
|
-
output: e.source.handle,
|
|
1552
|
-
},
|
|
1553
|
-
});
|
|
1539
|
+
if (!(e.source.handle in srcEff.outputs)) {
|
|
1540
|
+
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 });
|
|
1554
1541
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
issues.push({
|
|
1567
|
-
level: "error",
|
|
1568
|
-
code: "TYPE_MISMATCH_OUTPUT",
|
|
1569
|
-
message: `Edge ${e.id} type ${effectiveTypeId} mismatches source output ${srcNode.typeId}.${e.source.handle} (${s}) and no coercion exists`,
|
|
1570
|
-
data: {
|
|
1571
|
-
edgeId: e.id,
|
|
1572
|
-
nodeId: srcNode.nodeId,
|
|
1573
|
-
output: e.source.handle,
|
|
1574
|
-
declared: s,
|
|
1575
|
-
effectiveTypeId,
|
|
1576
|
-
},
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1542
|
+
const declaredArr = normOut(srcEff.outputs[e.source.handle]);
|
|
1543
|
+
if (declaredArr.length > 0 &&
|
|
1544
|
+
effectiveTypeId &&
|
|
1545
|
+
!canFlow(declaredArr, effectiveTypeId)) {
|
|
1546
|
+
pushIssue("error", "TYPE_MISMATCH_OUTPUT", `Edge ${e.id} type ${effectiveTypeId} mismatches source output ${srcNode.typeId}.${e.source.handle} (${declaredArr.join("|")}) and no coercion exists`, {
|
|
1547
|
+
edgeId: e.id,
|
|
1548
|
+
nodeId: srcNode.nodeId,
|
|
1549
|
+
output: e.source.handle,
|
|
1550
|
+
declared: declaredArr.join("|"),
|
|
1551
|
+
effectiveTypeId,
|
|
1552
|
+
});
|
|
1581
1553
|
}
|
|
1582
1554
|
}
|
|
1583
1555
|
if (dstNode) {
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
data: {
|
|
1591
|
-
edgeId: e.id,
|
|
1592
|
-
nodeId: dstNode.nodeId,
|
|
1593
|
-
input: e.target.handle,
|
|
1594
|
-
},
|
|
1595
|
-
});
|
|
1556
|
+
if (!(e.target.handle in dstEff.inputs)) {
|
|
1557
|
+
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 });
|
|
1558
|
+
}
|
|
1559
|
+
// Private inputs should not accept edges
|
|
1560
|
+
if (isInputPrivate(dstEff.inputs, e.target.handle)) {
|
|
1561
|
+
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 });
|
|
1596
1562
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1563
|
+
const declaredIn = getInputTypeId(dstEff.inputs, e.target.handle);
|
|
1564
|
+
if (declaredIn && effectiveTypeId) {
|
|
1565
|
+
if (srcNode) {
|
|
1566
|
+
const srcDeclared = srcEff.outputs[e.source.handle];
|
|
1567
|
+
const srcArr = normOut(srcDeclared).length
|
|
1568
|
+
? normOut(srcDeclared)
|
|
1569
|
+
: [effectiveTypeId];
|
|
1570
|
+
if (!canFlow(srcArr, declaredIn)) {
|
|
1571
|
+
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} output type ${srcArr.join("|")} not convertible to target input ${dstNode.typeId}.${e.target.handle} (${declaredIn})`, {
|
|
1605
1572
|
edgeId: e.id,
|
|
1606
1573
|
nodeId: dstNode.nodeId,
|
|
1607
1574
|
input: e.target.handle,
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
}
|
|
1611
|
-
const declaredIn = getInputTypeId(dstType.inputs, e.target.handle);
|
|
1612
|
-
if (declaredIn && effectiveTypeId) {
|
|
1613
|
-
// If source is a union, ensure each variant can reach declaredIn
|
|
1614
|
-
if (srcNode) {
|
|
1615
|
-
const srcType = this.registry.nodes.get(srcNode.typeId);
|
|
1616
|
-
const srcDeclared = srcType?.outputs[e.source.handle];
|
|
1617
|
-
const srcArr = Array.isArray(srcDeclared)
|
|
1618
|
-
? srcDeclared
|
|
1619
|
-
: srcDeclared
|
|
1620
|
-
? [srcDeclared]
|
|
1621
|
-
: effectiveTypeId
|
|
1622
|
-
? [effectiveTypeId]
|
|
1623
|
-
: [];
|
|
1624
|
-
for (const s of srcArr) {
|
|
1625
|
-
if (s !== declaredIn &&
|
|
1626
|
-
!this.registry.canCoerce(s, declaredIn)) {
|
|
1627
|
-
issues.push({
|
|
1628
|
-
level: "error",
|
|
1629
|
-
code: "TYPE_MISMATCH_INPUT",
|
|
1630
|
-
message: `Edge ${e.id} output type ${s} not convertible to target input ${dstNode.typeId}.${e.target.handle} (${declaredIn})`,
|
|
1631
|
-
data: {
|
|
1632
|
-
edgeId: e.id,
|
|
1633
|
-
nodeId: dstNode.nodeId,
|
|
1634
|
-
input: e.target.handle,
|
|
1635
|
-
declared: declaredIn,
|
|
1636
|
-
effectiveTypeId: s,
|
|
1637
|
-
},
|
|
1638
|
-
});
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
else if (declaredIn !== effectiveTypeId &&
|
|
1643
|
-
!this.registry.canCoerce(effectiveTypeId, declaredIn)) {
|
|
1644
|
-
issues.push({
|
|
1645
|
-
level: "error",
|
|
1646
|
-
code: "TYPE_MISMATCH_INPUT",
|
|
1647
|
-
message: `Edge ${e.id} type ${effectiveTypeId} mismatches target input ${dstNode.typeId}.${e.target.handle} (${declaredIn}) and no coercion exists`,
|
|
1648
|
-
data: {
|
|
1649
|
-
edgeId: e.id,
|
|
1650
|
-
nodeId: dstNode.nodeId,
|
|
1651
|
-
input: e.target.handle,
|
|
1652
|
-
declared: declaredIn,
|
|
1653
|
-
effectiveTypeId,
|
|
1654
|
-
},
|
|
1575
|
+
declared: declaredIn,
|
|
1576
|
+
effectiveTypeId: srcArr.join("|"),
|
|
1655
1577
|
});
|
|
1656
1578
|
}
|
|
1657
1579
|
}
|
|
1580
|
+
else if (!canFlow(effectiveTypeId, declaredIn)) {
|
|
1581
|
+
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} type ${effectiveTypeId} mismatches target input ${dstNode.typeId}.${e.target.handle} (${declaredIn}) and no coercion exists`, {
|
|
1582
|
+
edgeId: e.id,
|
|
1583
|
+
nodeId: dstNode.nodeId,
|
|
1584
|
+
input: e.target.handle,
|
|
1585
|
+
declared: declaredIn,
|
|
1586
|
+
effectiveTypeId,
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1658
1589
|
}
|
|
1659
1590
|
}
|
|
1660
1591
|
// Track multiple inbound edges targeting the same input handle
|
|
@@ -1662,10 +1593,7 @@ class GraphBuilder {
|
|
|
1662
1593
|
inboundCounts.set(inboundKey, (inboundCounts.get(inboundKey) ?? 0) + 1);
|
|
1663
1594
|
// If the target input is declared as an array type, allow multi-inbound (runtime will append)
|
|
1664
1595
|
if (dstNode) {
|
|
1665
|
-
const
|
|
1666
|
-
const declaredIn = dstType
|
|
1667
|
-
? getInputTypeId(dstType.inputs, e.target.handle)
|
|
1668
|
-
: undefined;
|
|
1596
|
+
const declaredIn = getInputTypeId((effByNodeId.get(dstNode.nodeId) || { inputs: {} }).inputs, e.target.handle);
|
|
1669
1597
|
if (typeof declaredIn === "string" && declaredIn.endsWith("[]")) {
|
|
1670
1598
|
inboundArrayOk.add(inboundKey);
|
|
1671
1599
|
}
|