@bian-womp/spark-graph 0.3.31 → 0.3.33
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 +338 -133
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/builder/GraphBuilder.d.ts +0 -1
- package/lib/cjs/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/cjs/src/builder/Registry.d.ts +6 -0
- package/lib/cjs/src/builder/Registry.d.ts.map +1 -1
- package/lib/cjs/src/core/type-utils.d.ts +11 -0
- package/lib/cjs/src/core/type-utils.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +4 -1
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/utils/merge.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +16 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/Graph.d.ts +1 -2
- package/lib/cjs/src/runtime/components/Graph.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/HandleResolver.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/RuntimeValidatorManager.d.ts +31 -0
- package/lib/cjs/src/runtime/components/RuntimeValidatorManager.d.ts.map +1 -0
- package/lib/cjs/src/runtime/components/graph-utils.d.ts +15 -4
- package/lib/cjs/src/runtime/components/graph-utils.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/interfaces.d.ts +24 -1
- package/lib/cjs/src/runtime/components/interfaces.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/types.d.ts +1 -2
- package/lib/cjs/src/runtime/components/types.d.ts.map +1 -1
- package/lib/esm/index.js +336 -134
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/builder/GraphBuilder.d.ts +0 -1
- package/lib/esm/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/esm/src/builder/Registry.d.ts +6 -0
- package/lib/esm/src/builder/Registry.d.ts.map +1 -1
- package/lib/esm/src/core/type-utils.d.ts +11 -0
- package/lib/esm/src/core/type-utils.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +4 -1
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/utils/merge.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +16 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/Graph.d.ts +1 -2
- package/lib/esm/src/runtime/components/Graph.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/HandleResolver.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/RuntimeValidatorManager.d.ts +31 -0
- package/lib/esm/src/runtime/components/RuntimeValidatorManager.d.ts.map +1 -0
- package/lib/esm/src/runtime/components/graph-utils.d.ts +15 -4
- package/lib/esm/src/runtime/components/graph-utils.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/interfaces.d.ts +24 -1
- package/lib/esm/src/runtime/components/interfaces.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/types.d.ts +1 -2
- package/lib/esm/src/runtime/components/types.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -18,19 +18,42 @@ function getTypedOutputValue(v) {
|
|
|
18
18
|
return v.__spark_value;
|
|
19
19
|
return v;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Get the full declared type(s) for an input handle (supports union types)
|
|
23
|
+
* Returns the typeId as-is: string for single type, string[] for union types
|
|
24
|
+
*/
|
|
25
|
+
function getInputDeclaredTypes(inputs, handle) {
|
|
22
26
|
const v = inputs ? inputs[handle] : undefined;
|
|
23
27
|
if (!v)
|
|
24
28
|
return undefined;
|
|
25
|
-
|
|
29
|
+
if (typeof v === "string")
|
|
30
|
+
return v;
|
|
31
|
+
if (Array.isArray(v))
|
|
32
|
+
return v;
|
|
33
|
+
return v.typeId;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get the primary (first) type ID for an input handle.
|
|
37
|
+
* For union types, returns the first type in the array.
|
|
38
|
+
* This maintains backward compatibility for code that expects a single type.
|
|
39
|
+
*/
|
|
40
|
+
function getInputTypeId(inputs, handle) {
|
|
41
|
+
const decl = getInputDeclaredTypes(inputs, handle);
|
|
42
|
+
if (!decl)
|
|
43
|
+
return undefined;
|
|
44
|
+
return Array.isArray(decl) ? decl[0] : decl;
|
|
26
45
|
}
|
|
27
46
|
function isInputPrivate(inputs, handle) {
|
|
28
47
|
const v = inputs ? inputs[handle] : undefined;
|
|
29
|
-
|
|
48
|
+
if (!v || typeof v === "string" || Array.isArray(v))
|
|
49
|
+
return false;
|
|
50
|
+
// At this point, v must be an object with optional private/metadata fields
|
|
51
|
+
return !!(typeof v === "object" && v !== null && "private" in v && v.private);
|
|
30
52
|
}
|
|
31
53
|
/**
|
|
32
54
|
* Merge two InputHandleDescriptor values, with dynamic taking precedence.
|
|
33
55
|
* If both have metadata, merge the metadata objects (dynamic overrides static).
|
|
56
|
+
* Supports union types (arrays) in both static and dynamic descriptors.
|
|
34
57
|
*/
|
|
35
58
|
function mergeInputHandleDescriptors(staticDesc, dynamicDesc) {
|
|
36
59
|
// If only one exists, return it
|
|
@@ -38,12 +61,17 @@ function mergeInputHandleDescriptors(staticDesc, dynamicDesc) {
|
|
|
38
61
|
return dynamicDesc;
|
|
39
62
|
if (!dynamicDesc)
|
|
40
63
|
return staticDesc;
|
|
41
|
-
// If both are
|
|
42
|
-
if (typeof staticDesc === "string"
|
|
64
|
+
// If both are primitive (string or array), dynamic wins
|
|
65
|
+
if ((typeof staticDesc === "string" || Array.isArray(staticDesc)) &&
|
|
66
|
+
(typeof dynamicDesc === "string" || Array.isArray(dynamicDesc))) {
|
|
43
67
|
return dynamicDesc;
|
|
44
68
|
}
|
|
45
|
-
const staticObj = typeof staticDesc === "string"
|
|
46
|
-
|
|
69
|
+
const staticObj = typeof staticDesc === "string" || Array.isArray(staticDesc)
|
|
70
|
+
? { typeId: staticDesc }
|
|
71
|
+
: staticDesc;
|
|
72
|
+
const dynamicObj = typeof dynamicDesc === "string" || Array.isArray(dynamicDesc)
|
|
73
|
+
? { typeId: dynamicDesc }
|
|
74
|
+
: dynamicDesc;
|
|
47
75
|
// Merge: dynamic takes precedence, but merge metadata objects
|
|
48
76
|
const merged = {
|
|
49
77
|
typeId: dynamicObj.typeId ?? staticObj.typeId,
|
|
@@ -64,7 +92,7 @@ function mergeInputHandleDescriptors(staticDesc, dynamicDesc) {
|
|
|
64
92
|
*/
|
|
65
93
|
function getInputHandleMetadata(inputs, handle) {
|
|
66
94
|
const v = inputs ? inputs[handle] : undefined;
|
|
67
|
-
if (!v || typeof v === "string")
|
|
95
|
+
if (!v || typeof v === "string" || Array.isArray(v))
|
|
68
96
|
return undefined;
|
|
69
97
|
return v.metadata;
|
|
70
98
|
}
|
|
@@ -259,7 +287,11 @@ class Registry {
|
|
|
259
287
|
if (cached)
|
|
260
288
|
return cached;
|
|
261
289
|
if (fromTypeId === toTypeId) {
|
|
262
|
-
const res = {
|
|
290
|
+
const res = {
|
|
291
|
+
kind: "sync",
|
|
292
|
+
convert: (v) => v,
|
|
293
|
+
cost: { edges: 0, async: 0 },
|
|
294
|
+
};
|
|
263
295
|
this.resolvedCache.set(cacheKey, res);
|
|
264
296
|
return res;
|
|
265
297
|
}
|
|
@@ -269,6 +301,7 @@ class Registry {
|
|
|
269
301
|
const res = {
|
|
270
302
|
kind: "sync",
|
|
271
303
|
convert: directSync.convert,
|
|
304
|
+
cost: { edges: 1, async: 0 },
|
|
272
305
|
};
|
|
273
306
|
this.resolvedCache.set(cacheKey, res);
|
|
274
307
|
return res;
|
|
@@ -278,6 +311,7 @@ class Registry {
|
|
|
278
311
|
const res = {
|
|
279
312
|
kind: "async",
|
|
280
313
|
convertAsync: directAsync.convertAsync,
|
|
314
|
+
cost: { edges: 1, async: 1 },
|
|
281
315
|
};
|
|
282
316
|
this.resolvedCache.set(cacheKey, res);
|
|
283
317
|
return res;
|
|
@@ -334,6 +368,10 @@ class Registry {
|
|
|
334
368
|
const cur = queue.shift();
|
|
335
369
|
if (cur.node === toTypeId) {
|
|
336
370
|
// Compose
|
|
371
|
+
const cost = {
|
|
372
|
+
edges: cur.cost.edges,
|
|
373
|
+
async: cur.cost.async,
|
|
374
|
+
};
|
|
337
375
|
const hasAsync = cur.path.some((s) => s.kind === "async");
|
|
338
376
|
if (!hasAsync) {
|
|
339
377
|
const convert = (value) => {
|
|
@@ -344,7 +382,11 @@ class Registry {
|
|
|
344
382
|
}
|
|
345
383
|
return acc;
|
|
346
384
|
};
|
|
347
|
-
const res = {
|
|
385
|
+
const res = {
|
|
386
|
+
kind: "sync",
|
|
387
|
+
convert,
|
|
388
|
+
cost,
|
|
389
|
+
};
|
|
348
390
|
this.resolvedCache.set(cacheKey, res);
|
|
349
391
|
return res;
|
|
350
392
|
}
|
|
@@ -361,7 +403,11 @@ class Registry {
|
|
|
361
403
|
}
|
|
362
404
|
return acc;
|
|
363
405
|
};
|
|
364
|
-
const res = {
|
|
406
|
+
const res = {
|
|
407
|
+
kind: "async",
|
|
408
|
+
convertAsync,
|
|
409
|
+
cost,
|
|
410
|
+
};
|
|
365
411
|
this.resolvedCache.set(cacheKey, res);
|
|
366
412
|
return res;
|
|
367
413
|
}
|
|
@@ -818,9 +864,6 @@ class Graph {
|
|
|
818
864
|
const edge = this.edges.find((e) => e.id === edgeId);
|
|
819
865
|
if (!edge)
|
|
820
866
|
return;
|
|
821
|
-
if (updates.effectiveTypeId !== undefined) {
|
|
822
|
-
edge.effectiveTypeId = updates.effectiveTypeId;
|
|
823
|
-
}
|
|
824
867
|
if (updates.dstDeclared !== undefined) {
|
|
825
868
|
edge.dstDeclared = updates.dstDeclared;
|
|
826
869
|
}
|
|
@@ -1414,14 +1457,13 @@ function buildEdges(def, registry, resolvedByNode) {
|
|
|
1414
1457
|
return def.edges.map((e) => {
|
|
1415
1458
|
const srcNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
|
|
1416
1459
|
const dstNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
|
|
1417
|
-
const { srcDeclared, dstDeclared
|
|
1460
|
+
const { srcDeclared, dstDeclared } = extractEdgeTypes(e.source.nodeId, e.source.handle, e.target.nodeId, e.target.handle, resolvedByNode);
|
|
1418
1461
|
const { convert, convertAsync } = buildEdgeConverters(srcDeclared, dstDeclared, registry, `buildEdges: ${srcNode?.typeId || ""}.${e.source.nodeId}.${e.source.handle} -> ${dstNode?.typeId || ""}.${e.target.nodeId}.${e.target.handle}`);
|
|
1419
1462
|
return {
|
|
1420
1463
|
id: e.id,
|
|
1421
1464
|
source: { ...e.source },
|
|
1422
1465
|
target: { ...e.target },
|
|
1423
1466
|
typeId: e.typeId, // Preserve original (may be undefined)
|
|
1424
|
-
effectiveTypeId, // Always present
|
|
1425
1467
|
convert,
|
|
1426
1468
|
convertAsync,
|
|
1427
1469
|
srcUnionTypes: Array.isArray(srcDeclared) ? [...srcDeclared] : undefined,
|
|
@@ -1433,37 +1475,79 @@ function buildEdges(def, registry, resolvedByNode) {
|
|
|
1433
1475
|
/**
|
|
1434
1476
|
* Extract edge type information from resolved handles
|
|
1435
1477
|
* Used by both buildEdges and updateNodeHandles to avoid duplication
|
|
1478
|
+
* Now supports union types on both source (output) and destination (input) handles
|
|
1436
1479
|
*/
|
|
1437
|
-
function extractEdgeTypes(sourceNodeId, sourceHandle, targetNodeId, targetHandle, resolvedByNode
|
|
1480
|
+
function extractEdgeTypes(sourceNodeId, sourceHandle, targetNodeId, targetHandle, resolvedByNode) {
|
|
1438
1481
|
const srcResolved = resolvedByNode.get(sourceNodeId);
|
|
1439
1482
|
const dstResolved = resolvedByNode.get(targetNodeId);
|
|
1440
1483
|
const srcDeclared = srcResolved
|
|
1441
1484
|
? srcResolved.outputs[sourceHandle]
|
|
1442
1485
|
: undefined;
|
|
1443
1486
|
const dstDeclared = dstResolved
|
|
1444
|
-
?
|
|
1487
|
+
? getInputDeclaredTypes(dstResolved.inputs, targetHandle)
|
|
1445
1488
|
: undefined;
|
|
1446
|
-
let effectiveTypeId = explicitTypeId;
|
|
1447
|
-
if (!effectiveTypeId) {
|
|
1448
|
-
// Infer if not explicitly set
|
|
1449
|
-
effectiveTypeId = Array.isArray(srcDeclared) ? srcDeclared[0] : srcDeclared;
|
|
1450
|
-
}
|
|
1451
1489
|
return {
|
|
1452
1490
|
srcDeclared,
|
|
1453
1491
|
dstDeclared,
|
|
1454
|
-
effectiveTypeId: effectiveTypeId ?? "untyped",
|
|
1455
1492
|
};
|
|
1456
1493
|
}
|
|
1457
1494
|
// Static helper: build edge converters for type coercion
|
|
1495
|
+
// Now supports union types on both source (output) and destination (input) handles
|
|
1458
1496
|
function buildEdgeConverters(srcDeclared, dstDeclared, registry, edgeLabel) {
|
|
1459
1497
|
if (!dstDeclared || !srcDeclared) {
|
|
1460
1498
|
return {};
|
|
1461
1499
|
}
|
|
1462
|
-
const
|
|
1463
|
-
const srcTypes =
|
|
1464
|
-
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1500
|
+
const isSrcUnion = Array.isArray(srcDeclared);
|
|
1501
|
+
const srcTypes = isSrcUnion ? srcDeclared : [srcDeclared];
|
|
1502
|
+
const isDstUnion = Array.isArray(dstDeclared);
|
|
1503
|
+
const dstTypes = isDstUnion ? dstDeclared : [dstDeclared];
|
|
1504
|
+
// Helper to compare coercion costs (sync preferred, then fewer steps)
|
|
1505
|
+
const compareCost = (a, b) => {
|
|
1506
|
+
// Prefer sync over async
|
|
1507
|
+
if (a.kind === "sync" && b.kind === "async")
|
|
1508
|
+
return -1;
|
|
1509
|
+
if (a.kind === "async" && b.kind === "sync")
|
|
1510
|
+
return 1;
|
|
1511
|
+
// If same kind, prefer fewer edges
|
|
1512
|
+
if (a.cost.edges !== b.cost.edges)
|
|
1513
|
+
return a.cost.edges - b.cost.edges;
|
|
1514
|
+
// If same edges, prefer fewer async steps
|
|
1515
|
+
return a.cost.async - b.cost.async;
|
|
1516
|
+
};
|
|
1517
|
+
// Helper to find the best coercion from a source type to any destination type
|
|
1518
|
+
const getCoercion = (srcTypeId) => {
|
|
1519
|
+
const candidates = [];
|
|
1520
|
+
// Try all destination types and collect valid coercions
|
|
1521
|
+
for (const dstTypeId of dstTypes) {
|
|
1522
|
+
const coercion = registry.resolveCoercion(srcTypeId, dstTypeId);
|
|
1523
|
+
if (coercion) {
|
|
1524
|
+
candidates.push({
|
|
1525
|
+
dstType: dstTypeId,
|
|
1526
|
+
coercion,
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (candidates.length === 0)
|
|
1531
|
+
return null;
|
|
1532
|
+
// Select best by cost: sync preferred, then fewer edges, then fewer async steps
|
|
1533
|
+
const best = candidates.reduce((best, cur) => {
|
|
1534
|
+
return compareCost(cur.coercion, best.coercion) < 0 ? cur : best;
|
|
1535
|
+
});
|
|
1536
|
+
if (best.coercion.kind === "sync") {
|
|
1537
|
+
return {
|
|
1538
|
+
kind: "sync",
|
|
1539
|
+
convert: best.coercion.convert,
|
|
1540
|
+
dstType: best.dstType,
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
else {
|
|
1544
|
+
return {
|
|
1545
|
+
kind: "async",
|
|
1546
|
+
convert: (v) => v, // placeholder, not used for async
|
|
1547
|
+
convertAsync: best.coercion.convertAsync,
|
|
1548
|
+
dstType: best.dstType,
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1467
1551
|
};
|
|
1468
1552
|
// Resolve coercions for all source types
|
|
1469
1553
|
const coercions = srcTypes.map(getCoercion);
|
|
@@ -1472,7 +1556,7 @@ function buildEdgeConverters(srcDeclared, dstDeclared, registry, edgeLabel) {
|
|
|
1472
1556
|
const extractPayload = (v) => {
|
|
1473
1557
|
const typeId = getTypedOutputTypeId(v);
|
|
1474
1558
|
const payload = getTypedOutputValue(v);
|
|
1475
|
-
if (
|
|
1559
|
+
if (isSrcUnion) {
|
|
1476
1560
|
if (!typeId) {
|
|
1477
1561
|
throw new Error(`Typed output required for union source (${edgeLabel}); allowed: ${srcTypes.join("|")}`);
|
|
1478
1562
|
}
|
|
@@ -1486,17 +1570,27 @@ function buildEdgeConverters(srcDeclared, dstDeclared, registry, edgeLabel) {
|
|
|
1486
1570
|
}
|
|
1487
1571
|
return { typeId: typeId || srcTypes[0], payload };
|
|
1488
1572
|
};
|
|
1573
|
+
const wrapIfDstUnion = (dstType, val) => {
|
|
1574
|
+
if (!isDstUnion || !dstType)
|
|
1575
|
+
return val;
|
|
1576
|
+
return typed(dstType, val);
|
|
1577
|
+
};
|
|
1489
1578
|
if (hasAsync) {
|
|
1490
1579
|
return {
|
|
1491
1580
|
convertAsync: async (v, signal) => {
|
|
1492
1581
|
const { typeId, payload } = extractPayload(v);
|
|
1493
1582
|
const res = getCoercion(typeId);
|
|
1494
|
-
if (!res)
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1583
|
+
if (!res) {
|
|
1584
|
+
const fallbackType = isDstUnion && typeId && dstTypes.includes(typeId)
|
|
1585
|
+
? typeId
|
|
1586
|
+
: undefined;
|
|
1587
|
+
return wrapIfDstUnion(fallbackType, payload);
|
|
1498
1588
|
}
|
|
1499
|
-
|
|
1589
|
+
if (res.kind === "async" && res.convertAsync) {
|
|
1590
|
+
const converted = await res.convertAsync(payload, signal);
|
|
1591
|
+
return wrapIfDstUnion(res.dstType, converted);
|
|
1592
|
+
}
|
|
1593
|
+
return wrapIfDstUnion(res.dstType, res.convert(payload));
|
|
1500
1594
|
},
|
|
1501
1595
|
};
|
|
1502
1596
|
}
|
|
@@ -1509,15 +1603,69 @@ function buildEdgeConverters(srcDeclared, dstDeclared, registry, edgeLabel) {
|
|
|
1509
1603
|
convert: (v) => {
|
|
1510
1604
|
const { typeId, payload } = extractPayload(v);
|
|
1511
1605
|
const res = getCoercion(typeId);
|
|
1512
|
-
if (!res)
|
|
1513
|
-
|
|
1606
|
+
if (!res) {
|
|
1607
|
+
const fallbackType = isDstUnion && typeId && dstTypes.includes(typeId)
|
|
1608
|
+
? typeId
|
|
1609
|
+
: undefined;
|
|
1610
|
+
return wrapIfDstUnion(fallbackType, payload);
|
|
1611
|
+
}
|
|
1514
1612
|
if (res.kind === "async") {
|
|
1515
1613
|
throw new Error(`Async coercion required but convert used (${edgeLabel})`);
|
|
1516
1614
|
}
|
|
1517
|
-
|
|
1615
|
+
const converted = res.convert(payload);
|
|
1616
|
+
return wrapIfDstUnion(res.dstType, converted);
|
|
1518
1617
|
},
|
|
1519
1618
|
};
|
|
1520
1619
|
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Compute effective inputs for a node by merging real inputs with defaults.
|
|
1622
|
+
* This is a shared utility used by both NodeExecutor and runtime validators.
|
|
1623
|
+
*
|
|
1624
|
+
* @param nodeId - The node ID to compute effective inputs for
|
|
1625
|
+
* @param graph - Graph component to access node and handle information
|
|
1626
|
+
* @param registry - Registry to access node type descriptors and defaults
|
|
1627
|
+
* @returns Record of effective input values (real inputs merged with defaults)
|
|
1628
|
+
*/
|
|
1629
|
+
function getEffectiveInputs(nodeId, graph, registry) {
|
|
1630
|
+
const node = graph.getNode(nodeId);
|
|
1631
|
+
if (!node)
|
|
1632
|
+
return {};
|
|
1633
|
+
const desc = registry.nodes.get(node.typeId);
|
|
1634
|
+
if (!desc)
|
|
1635
|
+
return {};
|
|
1636
|
+
const resolved = graph.getResolvedHandles(nodeId);
|
|
1637
|
+
const regDefaults = desc.inputDefaults ?? {};
|
|
1638
|
+
const dynDefaults = resolved?.inputDefaults ?? {};
|
|
1639
|
+
// Identify which handles are dynamically resolved (not in registry statics)
|
|
1640
|
+
const staticHandles = new Set(Object.keys(desc.inputs ?? {}));
|
|
1641
|
+
const dynamicHandles = new Set(Object.keys(resolved?.inputs ?? {}).filter((h) => !staticHandles.has(h)));
|
|
1642
|
+
// Precedence: dynamic > registry
|
|
1643
|
+
const mergedDefaults = {
|
|
1644
|
+
...regDefaults,
|
|
1645
|
+
...dynDefaults,
|
|
1646
|
+
};
|
|
1647
|
+
// Start with real inputs only (no defaults)
|
|
1648
|
+
const effective = { ...node.inputs };
|
|
1649
|
+
// Build set of inbound handles (wired inputs)
|
|
1650
|
+
const inboundEdges = graph.getInboundEdges(nodeId);
|
|
1651
|
+
const inbound = new Set(inboundEdges.map((e) => e.target.handle));
|
|
1652
|
+
// Apply defaults only for:
|
|
1653
|
+
// 1. Unbound handles that have no explicit value
|
|
1654
|
+
// 2. Static handles (not dynamically resolved)
|
|
1655
|
+
for (const [handle, defaultValue] of Object.entries(mergedDefaults)) {
|
|
1656
|
+
if (defaultValue === undefined)
|
|
1657
|
+
continue;
|
|
1658
|
+
if (inbound.has(handle))
|
|
1659
|
+
continue; // Don't override wired inputs
|
|
1660
|
+
if (effective[handle] !== undefined)
|
|
1661
|
+
continue; // Already has value
|
|
1662
|
+
if (dynamicHandles.has(handle))
|
|
1663
|
+
continue; // Skip defaults for dynamic handles
|
|
1664
|
+
// Clone to avoid shared references
|
|
1665
|
+
effective[handle] = structuredClone(defaultValue);
|
|
1666
|
+
}
|
|
1667
|
+
return effective;
|
|
1668
|
+
}
|
|
1521
1669
|
|
|
1522
1670
|
/**
|
|
1523
1671
|
* HandleResolver component - manages dynamic handle resolution
|
|
@@ -1617,12 +1765,11 @@ class HandleResolver {
|
|
|
1617
1765
|
const dstNode = this.graph.getNode(e.target.nodeId);
|
|
1618
1766
|
const oldDstDeclared = e.dstDeclared;
|
|
1619
1767
|
// Extract edge types using shared helper (handles both source and target updates)
|
|
1620
|
-
const { srcDeclared, dstDeclared
|
|
1768
|
+
const { srcDeclared, dstDeclared } = extractEdgeTypes(e.source.nodeId, e.source.handle, e.target.nodeId, e.target.handle, resolvedByNode);
|
|
1621
1769
|
// Update converters
|
|
1622
1770
|
const conv = buildEdgeConverters(srcDeclared, dstDeclared, registry, `updateNodeHandles: ${srcNode?.typeId || ""}.${e.source.nodeId}.${e.source.handle} -> ${dstNode?.typeId || ""}.${e.target.nodeId}.${e.target.handle}`);
|
|
1623
1771
|
// Update edge properties via Graph
|
|
1624
1772
|
this.graph.updateEdgeProperties(e.id, {
|
|
1625
|
-
effectiveTypeId: !e.typeId ? effectiveTypeId : undefined,
|
|
1626
1773
|
dstDeclared,
|
|
1627
1774
|
srcUnionTypes: Array.isArray(srcDeclared)
|
|
1628
1775
|
? [...srcDeclared]
|
|
@@ -2155,47 +2302,10 @@ class NodeExecutor {
|
|
|
2155
2302
|
* Compute effective inputs for a node by merging real inputs with defaults
|
|
2156
2303
|
*/
|
|
2157
2304
|
getEffectiveInputs(nodeId) {
|
|
2158
|
-
const node = this.graph.getNode(nodeId);
|
|
2159
|
-
if (!node)
|
|
2160
|
-
return {};
|
|
2161
2305
|
const registry = this.graph.getRegistry();
|
|
2162
2306
|
if (!registry)
|
|
2163
2307
|
return {};
|
|
2164
|
-
|
|
2165
|
-
if (!desc)
|
|
2166
|
-
return {};
|
|
2167
|
-
const resolved = this.graph.getResolvedHandles(nodeId);
|
|
2168
|
-
const regDefaults = desc.inputDefaults ?? {};
|
|
2169
|
-
const dynDefaults = resolved?.inputDefaults ?? {};
|
|
2170
|
-
// Identify which handles are dynamically resolved (not in registry statics)
|
|
2171
|
-
const staticHandles = new Set(Object.keys(desc.inputs ?? {}));
|
|
2172
|
-
const dynamicHandles = new Set(Object.keys(resolved?.inputs ?? {}).filter((h) => !staticHandles.has(h)));
|
|
2173
|
-
// Precedence: dynamic > registry
|
|
2174
|
-
const mergedDefaults = {
|
|
2175
|
-
...regDefaults,
|
|
2176
|
-
...dynDefaults,
|
|
2177
|
-
};
|
|
2178
|
-
// Start with real inputs only (no defaults)
|
|
2179
|
-
const effective = { ...node.inputs };
|
|
2180
|
-
// Build set of inbound handles (wired inputs)
|
|
2181
|
-
const inboundEdges = this.graph.getInboundEdges(nodeId);
|
|
2182
|
-
const inbound = new Set(inboundEdges.map((e) => e.target.handle));
|
|
2183
|
-
// Apply defaults only for:
|
|
2184
|
-
// 1. Unbound handles that have no explicit value
|
|
2185
|
-
// 2. Static handles (not dynamically resolved)
|
|
2186
|
-
for (const [handle, defaultValue] of Object.entries(mergedDefaults)) {
|
|
2187
|
-
if (defaultValue === undefined)
|
|
2188
|
-
continue;
|
|
2189
|
-
if (inbound.has(handle))
|
|
2190
|
-
continue; // Don't override wired inputs
|
|
2191
|
-
if (effective[handle] !== undefined)
|
|
2192
|
-
continue; // Already has value
|
|
2193
|
-
if (dynamicHandles.has(handle))
|
|
2194
|
-
continue; // Skip defaults for dynamic handles
|
|
2195
|
-
// Clone to avoid shared references
|
|
2196
|
-
effective[handle] = structuredClone(defaultValue);
|
|
2197
|
-
}
|
|
2198
|
-
return effective;
|
|
2308
|
+
return getEffectiveInputs(nodeId, this.graph, registry);
|
|
2199
2309
|
}
|
|
2200
2310
|
/**
|
|
2201
2311
|
* Create an execution context for a node
|
|
@@ -2301,6 +2411,20 @@ class NodeExecutor {
|
|
|
2301
2411
|
// Early validation for auto-mode paused state
|
|
2302
2412
|
if (this.runtime.isPaused())
|
|
2303
2413
|
return;
|
|
2414
|
+
// Check runtime validators (check current state, not just graph definition)
|
|
2415
|
+
const runtimeValidationError = this.runtime.hasRuntimeValidationBlock(nodeId);
|
|
2416
|
+
if (runtimeValidationError) {
|
|
2417
|
+
this.eventEmitter.emit("error", {
|
|
2418
|
+
kind: "system",
|
|
2419
|
+
message: runtimeValidationError.message,
|
|
2420
|
+
code: runtimeValidationError.code || "RUNTIME_VALIDATION_BLOCKED",
|
|
2421
|
+
details: {
|
|
2422
|
+
nodeId,
|
|
2423
|
+
...runtimeValidationError.details,
|
|
2424
|
+
},
|
|
2425
|
+
});
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2304
2428
|
// Attach run-context IDs if provided - do this BEFORE checking for pending resolution
|
|
2305
2429
|
// so that handle resolution can track these run contexts
|
|
2306
2430
|
if (runContextIds) {
|
|
@@ -2759,6 +2883,61 @@ class NodeExecutor {
|
|
|
2759
2883
|
}
|
|
2760
2884
|
}
|
|
2761
2885
|
|
|
2886
|
+
/**
|
|
2887
|
+
* RuntimeValidatorManager component - manages runtime validators
|
|
2888
|
+
*/
|
|
2889
|
+
class RuntimeValidatorManager {
|
|
2890
|
+
constructor(graph, registry) {
|
|
2891
|
+
this.graph = graph;
|
|
2892
|
+
this.registry = registry;
|
|
2893
|
+
this.validators = [];
|
|
2894
|
+
}
|
|
2895
|
+
/**
|
|
2896
|
+
* Set the registry (called when registry changes)
|
|
2897
|
+
*/
|
|
2898
|
+
setRegistry(registry) {
|
|
2899
|
+
this.registry = registry;
|
|
2900
|
+
}
|
|
2901
|
+
/**
|
|
2902
|
+
* Register a runtime validator that will be called before node execution.
|
|
2903
|
+
* Validators are called in registration order - if any returns true, execution is blocked.
|
|
2904
|
+
*/
|
|
2905
|
+
registerValidator(validator) {
|
|
2906
|
+
this.validators.push(validator);
|
|
2907
|
+
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Unregister a runtime validator.
|
|
2910
|
+
*/
|
|
2911
|
+
unregisterValidator(validator) {
|
|
2912
|
+
const index = this.validators.indexOf(validator);
|
|
2913
|
+
if (index >= 0) {
|
|
2914
|
+
this.validators.splice(index, 1);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* Check if any runtime validator blocks execution for this node.
|
|
2919
|
+
* Returns RuntimeValidationError if execution should be blocked, null otherwise.
|
|
2920
|
+
*/
|
|
2921
|
+
hasBlock(nodeId) {
|
|
2922
|
+
if (!this.registry)
|
|
2923
|
+
return null;
|
|
2924
|
+
for (const validator of this.validators) {
|
|
2925
|
+
try {
|
|
2926
|
+
const result = validator(nodeId, this.graph, this.registry);
|
|
2927
|
+
if (result !== false) {
|
|
2928
|
+
// Validator returned an error object
|
|
2929
|
+
return result;
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
catch (err) {
|
|
2933
|
+
// Don't let validator errors break execution - log and continue
|
|
2934
|
+
console.error(`Runtime validator error for node ${nodeId}:`, err);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
return null;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2762
2941
|
// Types are now imported from components/types.ts (re-exported above)
|
|
2763
2942
|
class GraphRuntime {
|
|
2764
2943
|
constructor() {
|
|
@@ -2775,6 +2954,8 @@ class GraphRuntime {
|
|
|
2775
2954
|
this.edgePropagator = new EdgePropagator(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this);
|
|
2776
2955
|
// Create NodeExecutor with EdgePropagator and HandleResolver
|
|
2777
2956
|
this.nodeExecutor = new NodeExecutor(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this, this);
|
|
2957
|
+
// Create RuntimeValidatorManager
|
|
2958
|
+
this.runtimeValidatorManager = new RuntimeValidatorManager(this.graph);
|
|
2778
2959
|
}
|
|
2779
2960
|
static create(def, registry, opts) {
|
|
2780
2961
|
const gr = new GraphRuntime();
|
|
@@ -2786,6 +2967,7 @@ class GraphRuntime {
|
|
|
2786
2967
|
gr.handleResolver.setRegistry(registry);
|
|
2787
2968
|
gr.handleResolver.setEnvironment(gr.environment);
|
|
2788
2969
|
gr.nodeExecutor.setEnvironment(gr.environment);
|
|
2970
|
+
gr.runtimeValidatorManager.setRegistry(registry);
|
|
2789
2971
|
// Precompute per-node resolved handles (use def-provided overrides; do not compute dynamically here)
|
|
2790
2972
|
const initial = gr.isPaused()
|
|
2791
2973
|
? {
|
|
@@ -3003,6 +3185,26 @@ class GraphRuntime {
|
|
|
3003
3185
|
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
3004
3186
|
}
|
|
3005
3187
|
}
|
|
3188
|
+
/**
|
|
3189
|
+
* Register a runtime validator that will be called before node execution.
|
|
3190
|
+
* Validators are called in registration order - if any returns true, execution is blocked.
|
|
3191
|
+
*/
|
|
3192
|
+
registerRuntimeValidator(validator) {
|
|
3193
|
+
this.runtimeValidatorManager.registerValidator(validator);
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Unregister a runtime validator.
|
|
3197
|
+
*/
|
|
3198
|
+
unregisterRuntimeValidator(validator) {
|
|
3199
|
+
this.runtimeValidatorManager.unregisterValidator(validator);
|
|
3200
|
+
}
|
|
3201
|
+
/**
|
|
3202
|
+
* Check if any runtime validator blocks execution for this node.
|
|
3203
|
+
* Returns RuntimeValidationError if execution should be blocked, null otherwise.
|
|
3204
|
+
*/
|
|
3205
|
+
hasRuntimeValidationBlock(nodeId) {
|
|
3206
|
+
return this.runtimeValidatorManager.hasBlock(nodeId);
|
|
3207
|
+
}
|
|
3006
3208
|
getGraphDef() {
|
|
3007
3209
|
const nodes = [];
|
|
3008
3210
|
this.graph.forEachNode((n) => {
|
|
@@ -3468,20 +3670,12 @@ class GraphBuilder {
|
|
|
3468
3670
|
return { inputs, outputs };
|
|
3469
3671
|
};
|
|
3470
3672
|
const normOut = (decl) => Array.isArray(decl) ? decl : decl ? [decl] : [];
|
|
3471
|
-
const inferEdgeType = (srcDeclared, dstDeclared, explicit) => {
|
|
3472
|
-
if (explicit)
|
|
3473
|
-
return explicit;
|
|
3474
|
-
if (Array.isArray(srcDeclared) && dstDeclared)
|
|
3475
|
-
return dstDeclared;
|
|
3476
|
-
if (srcDeclared)
|
|
3477
|
-
return Array.isArray(srcDeclared) ? srcDeclared[0] : srcDeclared;
|
|
3478
|
-
return undefined;
|
|
3479
|
-
};
|
|
3480
3673
|
const canFlow = (from, to) => {
|
|
3481
3674
|
if (!to || !from)
|
|
3482
3675
|
return true;
|
|
3483
|
-
const
|
|
3484
|
-
|
|
3676
|
+
const srcTypes = Array.isArray(from) ? from : [from];
|
|
3677
|
+
const dstTypes = Array.isArray(to) ? to : [to];
|
|
3678
|
+
return srcTypes.some((s) => dstTypes.some((t) => s === t || !!this.registry.canCoerce(s, t)));
|
|
3485
3679
|
};
|
|
3486
3680
|
// Helper to validate enum value
|
|
3487
3681
|
const validateEnumValue = (typeId, value, nodeId, handle) => {
|
|
@@ -3592,37 +3786,29 @@ class GraphBuilder {
|
|
|
3592
3786
|
};
|
|
3593
3787
|
const dstEff = effByNodeId.get(e.target.nodeId) || {
|
|
3594
3788
|
inputs: {}};
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
// Effective edge type
|
|
3602
|
-
const effectiveTypeId = inferEdgeType(_srcDeclared, _dstDeclared, e.typeId);
|
|
3603
|
-
const type = effectiveTypeId
|
|
3604
|
-
? this.registry.types.get(effectiveTypeId)
|
|
3605
|
-
: undefined;
|
|
3606
|
-
if (!type) {
|
|
3607
|
-
pushIssue("error", "TYPE_MISSING", `Edge ${e.id} type missing or unknown`, {
|
|
3608
|
-
edgeId: e.id,
|
|
3609
|
-
});
|
|
3789
|
+
// Validate explicit type if provided
|
|
3790
|
+
if (e.typeId) {
|
|
3791
|
+
const type = this.registry.types.get(e.typeId);
|
|
3792
|
+
if (!type) {
|
|
3793
|
+
pushIssue("error", "TYPE_MISSING", `Edge ${e.id} explicit type ${e.typeId} is missing or unknown`, { edgeId: e.id });
|
|
3794
|
+
}
|
|
3610
3795
|
}
|
|
3611
3796
|
if (srcNode) {
|
|
3612
3797
|
if (!(e.source.handle in srcEff.outputs)) {
|
|
3613
3798
|
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 });
|
|
3614
3799
|
}
|
|
3615
3800
|
const declaredArr = normOut(srcEff.outputs[e.source.handle]);
|
|
3616
|
-
if (declaredArr.length > 0
|
|
3617
|
-
|
|
3618
|
-
!canFlow(declaredArr,
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3801
|
+
if (declaredArr.length > 0) {
|
|
3802
|
+
// Check if explicit type matches source output
|
|
3803
|
+
if (e.typeId && !canFlow(declaredArr, e.typeId)) {
|
|
3804
|
+
pushIssue("error", "TYPE_MISMATCH_OUTPUT", `Edge ${e.id} explicit type ${e.typeId} mismatches source output ${srcNode.typeId}.${e.source.handle} (${declaredArr.join("|")}) and no coercion exists`, {
|
|
3805
|
+
edgeId: e.id,
|
|
3806
|
+
nodeId: srcNode.nodeId,
|
|
3807
|
+
output: e.source.handle,
|
|
3808
|
+
declared: declaredArr.join("|"),
|
|
3809
|
+
typeId: e.typeId,
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3626
3812
|
}
|
|
3627
3813
|
}
|
|
3628
3814
|
if (dstNode) {
|
|
@@ -3633,30 +3819,30 @@ class GraphBuilder {
|
|
|
3633
3819
|
if (isInputPrivate(dstEff.inputs, e.target.handle)) {
|
|
3634
3820
|
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 });
|
|
3635
3821
|
}
|
|
3636
|
-
const declaredIn =
|
|
3637
|
-
|
|
3822
|
+
const declaredIn = getInputDeclaredTypes(dstEff.inputs, e.target.handle);
|
|
3823
|
+
const declaredInArr = normOut(declaredIn);
|
|
3824
|
+
if (declaredInArr.length > 0) {
|
|
3638
3825
|
if (srcNode) {
|
|
3639
3826
|
const srcDeclared = srcEff.outputs[e.source.handle];
|
|
3640
|
-
const srcArr = normOut(srcDeclared)
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
if (!canFlow(srcArr, declaredIn)) {
|
|
3644
|
-
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} output type ${srcArr.join("|")} not convertible to target input ${dstNode.typeId}.${e.target.handle} (${declaredIn})`, {
|
|
3827
|
+
const srcArr = normOut(srcDeclared);
|
|
3828
|
+
if (srcArr.length > 0 && !canFlow(srcArr, declaredInArr)) {
|
|
3829
|
+
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} output type ${srcArr.join("|")} not convertible to target input ${dstNode.typeId}.${e.target.handle} (${declaredInArr.join("|")})`, {
|
|
3645
3830
|
edgeId: e.id,
|
|
3646
3831
|
nodeId: dstNode.nodeId,
|
|
3647
3832
|
input: e.target.handle,
|
|
3648
|
-
declared:
|
|
3649
|
-
|
|
3833
|
+
declared: declaredInArr.join("|"),
|
|
3834
|
+
typeId: srcArr.join("|"),
|
|
3650
3835
|
});
|
|
3651
3836
|
}
|
|
3652
3837
|
}
|
|
3653
|
-
else if (!canFlow(
|
|
3654
|
-
|
|
3838
|
+
else if (e.typeId && !canFlow([e.typeId], declaredInArr)) {
|
|
3839
|
+
// External source with explicit type
|
|
3840
|
+
pushIssue("error", "TYPE_MISMATCH_INPUT", `Edge ${e.id} explicit type ${e.typeId} mismatches target input ${dstNode.typeId}.${e.target.handle} (${declaredInArr.join("|")}) and no coercion exists`, {
|
|
3655
3841
|
edgeId: e.id,
|
|
3656
3842
|
nodeId: dstNode.nodeId,
|
|
3657
3843
|
input: e.target.handle,
|
|
3658
|
-
declared:
|
|
3659
|
-
|
|
3844
|
+
declared: declaredInArr.join("|"),
|
|
3845
|
+
typeId: e.typeId,
|
|
3660
3846
|
});
|
|
3661
3847
|
}
|
|
3662
3848
|
}
|
|
@@ -5592,9 +5778,17 @@ function buildTypeMaps(def) {
|
|
|
5592
5778
|
const nodeOutputTypes = new Map();
|
|
5593
5779
|
if (node.resolvedHandles?.inputs) {
|
|
5594
5780
|
for (const [handleId, handleDesc] of Object.entries(node.resolvedHandles.inputs)) {
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5781
|
+
let typeId;
|
|
5782
|
+
if (typeof handleDesc === "string") {
|
|
5783
|
+
typeId = handleDesc;
|
|
5784
|
+
}
|
|
5785
|
+
else if (Array.isArray(handleDesc)) {
|
|
5786
|
+
typeId = handleDesc[0]; // Use first type for type map (backward compat)
|
|
5787
|
+
}
|
|
5788
|
+
else {
|
|
5789
|
+
const descTypeId = handleDesc.typeId;
|
|
5790
|
+
typeId = Array.isArray(descTypeId) ? descTypeId[0] : descTypeId;
|
|
5791
|
+
}
|
|
5598
5792
|
if (typeId)
|
|
5599
5793
|
nodeInputTypes.set(handleId, typeId);
|
|
5600
5794
|
}
|
|
@@ -5617,9 +5811,17 @@ function buildTypeMaps(def) {
|
|
|
5617
5811
|
if (!nodeInputTypes.has(handleId) && node.resolvedHandles?.inputs) {
|
|
5618
5812
|
const inputDesc = node.resolvedHandles.inputs[handleId];
|
|
5619
5813
|
if (inputDesc) {
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5814
|
+
let typeId;
|
|
5815
|
+
if (typeof inputDesc === "string") {
|
|
5816
|
+
typeId = inputDesc;
|
|
5817
|
+
}
|
|
5818
|
+
else if (Array.isArray(inputDesc)) {
|
|
5819
|
+
typeId = inputDesc[0]; // Use first type for type map (backward compat)
|
|
5820
|
+
}
|
|
5821
|
+
else {
|
|
5822
|
+
const descTypeId = inputDesc.typeId;
|
|
5823
|
+
typeId = Array.isArray(descTypeId) ? descTypeId[0] : descTypeId;
|
|
5824
|
+
}
|
|
5623
5825
|
if (typeId)
|
|
5624
5826
|
nodeInputTypes.set(handleId, typeId);
|
|
5625
5827
|
}
|
|
@@ -6060,6 +6262,7 @@ exports.BaseLogicOperation = BaseLogicOperation;
|
|
|
6060
6262
|
exports.BaseMathOperation = BaseMathOperation;
|
|
6061
6263
|
exports.CompositeCategory = CompositeCategory;
|
|
6062
6264
|
exports.ComputeCategory = ComputeCategory;
|
|
6265
|
+
exports.Graph = Graph;
|
|
6063
6266
|
exports.GraphBuilder = GraphBuilder;
|
|
6064
6267
|
exports.GraphRuntime = GraphRuntime;
|
|
6065
6268
|
exports.LevelLogger = LevelLogger;
|
|
@@ -6078,6 +6281,8 @@ exports.createValidationGraphDef = createValidationGraphDef;
|
|
|
6078
6281
|
exports.createValidationGraphRegistry = createValidationGraphRegistry;
|
|
6079
6282
|
exports.findMatchingPaths = findMatchingPaths;
|
|
6080
6283
|
exports.generateId = generateId;
|
|
6284
|
+
exports.getEffectiveInputs = getEffectiveInputs;
|
|
6285
|
+
exports.getInputDeclaredTypes = getInputDeclaredTypes;
|
|
6081
6286
|
exports.getInputHandleMetadata = getInputHandleMetadata;
|
|
6082
6287
|
exports.getInputTypeId = getInputTypeId;
|
|
6083
6288
|
exports.getTypedOutputTypeId = getTypedOutputTypeId;
|