@bian-womp/spark-graph 0.3.50 → 0.3.52
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 +645 -361
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/types.d.ts +1 -0
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +3 -3
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/{src/runtime/utils.d.ts → cjs/src/misc/utils/LevelLogger.d.ts} +1 -16
- package/lib/cjs/src/misc/utils/LevelLogger.d.ts.map +1 -0
- package/lib/cjs/src/misc/utils/json.d.ts +12 -0
- package/lib/cjs/src/misc/utils/json.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +7 -2
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalEngine.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/EdgePropagator.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/NodeExecutor.d.ts +8 -4
- package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/interfaces.d.ts +5 -1
- package/lib/cjs/src/runtime/components/interfaces.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/types.d.ts +1 -0
- package/lib/cjs/src/runtime/components/types.d.ts.map +1 -1
- package/lib/cjs/src/runtime/utils.d.ts +0 -51
- package/lib/cjs/src/runtime/utils.d.ts.map +1 -1
- package/lib/esm/index.js +644 -362
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/types.d.ts +1 -0
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +3 -3
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/utils/LevelLogger.d.ts +52 -0
- package/lib/esm/src/misc/utils/LevelLogger.d.ts.map +1 -0
- package/lib/esm/src/misc/utils/json.d.ts +12 -0
- package/lib/esm/src/misc/utils/json.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +7 -2
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalEngine.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/EdgePropagator.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/NodeExecutor.d.ts +8 -4
- package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/interfaces.d.ts +5 -1
- package/lib/esm/src/runtime/components/interfaces.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/types.d.ts +1 -0
- package/lib/esm/src/runtime/components/types.d.ts.map +1 -1
- package/lib/esm/src/runtime/utils.d.ts +0 -51
- package/lib/esm/src/runtime/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/lib/src/builder/GraphBuilder.d.ts +0 -43
- package/lib/src/builder/GraphBuilder.d.ts.map +0 -1
- package/lib/src/builder/GraphBuilder.js +0 -284
- package/lib/src/builder/GraphBuilder.js.map +0 -1
- package/lib/src/builder/Registry.d.ts +0 -93
- package/lib/src/builder/Registry.d.ts.map +0 -1
- package/lib/src/builder/Registry.js +0 -393
- package/lib/src/builder/Registry.js.map +0 -1
- package/lib/src/core/categories.d.ts +0 -22
- package/lib/src/core/categories.d.ts.map +0 -1
- package/lib/src/core/categories.js +0 -2
- package/lib/src/core/categories.js.map +0 -1
- package/lib/src/core/order.d.ts +0 -7
- package/lib/src/core/order.d.ts.map +0 -1
- package/lib/src/core/order.js +0 -66
- package/lib/src/core/order.js.map +0 -1
- package/lib/src/core/type-utils.d.ts +0 -29
- package/lib/src/core/type-utils.d.ts.map +0 -1
- package/lib/src/core/type-utils.js +0 -97
- package/lib/src/core/type-utils.js.map +0 -1
- package/lib/src/core/types.d.ts +0 -92
- package/lib/src/core/types.d.ts.map +0 -1
- package/lib/src/core/types.js +0 -2
- package/lib/src/core/types.js.map +0 -1
- package/lib/src/examples/arrays.d.ts +0 -5
- package/lib/src/examples/arrays.d.ts.map +0 -1
- package/lib/src/examples/arrays.js +0 -49
- package/lib/src/examples/arrays.js.map +0 -1
- package/lib/src/examples/async.d.ts +0 -5
- package/lib/src/examples/async.d.ts.map +0 -1
- package/lib/src/examples/async.js +0 -91
- package/lib/src/examples/async.js.map +0 -1
- package/lib/src/examples/progress.d.ts +0 -5
- package/lib/src/examples/progress.d.ts.map +0 -1
- package/lib/src/examples/progress.js +0 -51
- package/lib/src/examples/progress.js.map +0 -1
- package/lib/src/examples/run.d.ts +0 -2
- package/lib/src/examples/run.d.ts.map +0 -1
- package/lib/src/examples/run.js +0 -32
- package/lib/src/examples/run.js.map +0 -1
- package/lib/src/examples/runMode.d.ts +0 -2
- package/lib/src/examples/runMode.d.ts.map +0 -1
- package/lib/src/examples/runMode.js +0 -223
- package/lib/src/examples/runMode.js.map +0 -1
- package/lib/src/examples/shared.d.ts +0 -5
- package/lib/src/examples/shared.d.ts.map +0 -1
- package/lib/src/examples/shared.js +0 -49
- package/lib/src/examples/shared.js.map +0 -1
- package/lib/src/examples/simple.d.ts +0 -5
- package/lib/src/examples/simple.d.ts.map +0 -1
- package/lib/src/examples/simple.js +0 -79
- package/lib/src/examples/simple.js.map +0 -1
- package/lib/src/examples/snapshot.d.ts +0 -4
- package/lib/src/examples/snapshot.d.ts.map +0 -1
- package/lib/src/examples/snapshot.js +0 -58
- package/lib/src/examples/snapshot.js.map +0 -1
- package/lib/src/examples/validation.d.ts +0 -5
- package/lib/src/examples/validation.d.ts.map +0 -1
- package/lib/src/examples/validation.js +0 -105
- package/lib/src/examples/validation.js.map +0 -1
- package/lib/src/index.d.ts +0 -27
- package/lib/src/index.d.ts.map +0 -1
- package/lib/src/index.js +0 -19
- package/lib/src/index.js.map +0 -1
- package/lib/src/misc/base.d.ts +0 -51
- package/lib/src/misc/base.d.ts.map +0 -1
- package/lib/src/misc/base.js +0 -1122
- package/lib/src/misc/base.js.map +0 -1
- package/lib/src/misc/utils/json.d.ts +0 -22
- package/lib/src/misc/utils/json.d.ts.map +0 -1
- package/lib/src/misc/utils/json.js +0 -239
- package/lib/src/misc/utils/json.js.map +0 -1
- package/lib/src/misc/utils/merge.d.ts +0 -51
- package/lib/src/misc/utils/merge.d.ts.map +0 -1
- package/lib/src/misc/utils/merge.js +0 -600
- package/lib/src/misc/utils/merge.js.map +0 -1
- package/lib/src/plugins/composite.d.ts +0 -22
- package/lib/src/plugins/composite.d.ts.map +0 -1
- package/lib/src/plugins/composite.js +0 -59
- package/lib/src/plugins/composite.js.map +0 -1
- package/lib/src/plugins/compute.d.ts +0 -5
- package/lib/src/plugins/compute.d.ts.map +0 -1
- package/lib/src/plugins/compute.js +0 -39
- package/lib/src/plugins/compute.js.map +0 -1
- package/lib/src/runtime/Engine.d.ts +0 -28
- package/lib/src/runtime/Engine.d.ts.map +0 -1
- package/lib/src/runtime/Engine.js +0 -2
- package/lib/src/runtime/Engine.js.map +0 -1
- package/lib/src/runtime/GraphLifecycleApi.d.ts +0 -46
- package/lib/src/runtime/GraphLifecycleApi.d.ts.map +0 -1
- package/lib/src/runtime/GraphLifecycleApi.js +0 -2
- package/lib/src/runtime/GraphLifecycleApi.js.map +0 -1
- package/lib/src/runtime/GraphRuntime.d.ts +0 -94
- package/lib/src/runtime/GraphRuntime.d.ts.map +0 -1
- package/lib/src/runtime/GraphRuntime.js +0 -729
- package/lib/src/runtime/GraphRuntime.js.map +0 -1
- package/lib/src/runtime/LocalEngine.d.ts +0 -45
- package/lib/src/runtime/LocalEngine.d.ts.map +0 -1
- package/lib/src/runtime/LocalEngine.js +0 -89
- package/lib/src/runtime/LocalEngine.js.map +0 -1
- package/lib/src/runtime/components/EdgePropagator.d.ts +0 -101
- package/lib/src/runtime/components/EdgePropagator.d.ts.map +0 -1
- package/lib/src/runtime/components/EdgePropagator.js +0 -372
- package/lib/src/runtime/components/EdgePropagator.js.map +0 -1
- package/lib/src/runtime/components/EventEmitter.d.ts +0 -12
- package/lib/src/runtime/components/EventEmitter.d.ts.map +0 -1
- package/lib/src/runtime/components/EventEmitter.js +0 -33
- package/lib/src/runtime/components/EventEmitter.js.map +0 -1
- package/lib/src/runtime/components/Graph.d.ts +0 -211
- package/lib/src/runtime/components/Graph.d.ts.map +0 -1
- package/lib/src/runtime/components/Graph.js +0 -468
- package/lib/src/runtime/components/Graph.js.map +0 -1
- package/lib/src/runtime/components/HandleResolver.d.ts +0 -36
- package/lib/src/runtime/components/HandleResolver.d.ts.map +0 -1
- package/lib/src/runtime/components/HandleResolver.js +0 -231
- package/lib/src/runtime/components/HandleResolver.js.map +0 -1
- package/lib/src/runtime/components/NodeExecutor.d.ts +0 -110
- package/lib/src/runtime/components/NodeExecutor.d.ts.map +0 -1
- package/lib/src/runtime/components/NodeExecutor.js +0 -659
- package/lib/src/runtime/components/NodeExecutor.js.map +0 -1
- package/lib/src/runtime/components/RunContextManager.d.ts +0 -86
- package/lib/src/runtime/components/RunContextManager.d.ts.map +0 -1
- package/lib/src/runtime/components/RunContextManager.js +0 -302
- package/lib/src/runtime/components/RunContextManager.js.map +0 -1
- package/lib/src/runtime/components/RuntimeValidatorManager.d.ts +0 -31
- package/lib/src/runtime/components/RuntimeValidatorManager.d.ts.map +0 -1
- package/lib/src/runtime/components/RuntimeValidatorManager.js +0 -55
- package/lib/src/runtime/components/RuntimeValidatorManager.js.map +0 -1
- package/lib/src/runtime/components/graph-utils.d.ts +0 -33
- package/lib/src/runtime/components/graph-utils.d.ts.map +0 -1
- package/lib/src/runtime/components/graph-utils.js +0 -292
- package/lib/src/runtime/components/graph-utils.js.map +0 -1
- package/lib/src/runtime/components/interfaces.d.ts +0 -54
- package/lib/src/runtime/components/interfaces.d.ts.map +0 -1
- package/lib/src/runtime/components/interfaces.js +0 -2
- package/lib/src/runtime/components/interfaces.js.map +0 -1
- package/lib/src/runtime/components/types.d.ts +0 -55
- package/lib/src/runtime/components/types.d.ts.map +0 -1
- package/lib/src/runtime/components/types.js +0 -2
- package/lib/src/runtime/components/types.js.map +0 -1
- package/lib/src/runtime/utils.d.ts.map +0 -1
- package/lib/src/runtime/utils.js +0 -137
- package/lib/src/runtime/utils.js.map +0 -1
package/lib/esm/index.js
CHANGED
|
@@ -996,39 +996,494 @@ const LOG_LEVEL_VALUES = {
|
|
|
996
996
|
silent: 4,
|
|
997
997
|
};
|
|
998
998
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
999
|
+
function parseJsonPath(path) {
|
|
1000
|
+
if (typeof path === "string") {
|
|
1001
|
+
return path.split(".").flatMap((segment) => {
|
|
1002
|
+
const arrayMatch = segment.match(/^(.+)\[(\d+)\]$/);
|
|
1003
|
+
if (arrayMatch) {
|
|
1004
|
+
const index = parseInt(arrayMatch[2], 10);
|
|
1005
|
+
return arrayMatch[1] ? [arrayMatch[1], index] : [index];
|
|
1006
|
+
}
|
|
1007
|
+
return [segment];
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
return path;
|
|
1007
1011
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1012
|
+
function getValueAtPath(obj, pathSegments) {
|
|
1013
|
+
if (pathSegments.length === 0) {
|
|
1014
|
+
return { value: obj, parent: null, key: "" };
|
|
1015
|
+
}
|
|
1016
|
+
let current = obj;
|
|
1017
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
1018
|
+
const segment = pathSegments[i];
|
|
1019
|
+
if (current === null ||
|
|
1020
|
+
current === undefined ||
|
|
1021
|
+
typeof current !== "object") {
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
if (typeof segment === "string") {
|
|
1025
|
+
if (Array.isArray(current)) {
|
|
1026
|
+
const index = parseInt(segment, 10);
|
|
1027
|
+
if (isNaN(index))
|
|
1028
|
+
return null;
|
|
1029
|
+
current = current[index];
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
current = current[segment];
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
else if (typeof segment === "number") {
|
|
1036
|
+
if (Array.isArray(current)) {
|
|
1037
|
+
if (segment >= 0 && segment < current.length) {
|
|
1038
|
+
current = current[segment];
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
else if (segment instanceof RegExp) {
|
|
1049
|
+
if (Array.isArray(current)) {
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
const obj = current;
|
|
1053
|
+
const matchingKey = Object.keys(obj).find((key) => segment.test(key));
|
|
1054
|
+
if (!matchingKey)
|
|
1055
|
+
return null;
|
|
1056
|
+
current = obj[matchingKey];
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
1063
|
+
if (typeof lastSegment === "string") {
|
|
1064
|
+
if (Array.isArray(current)) {
|
|
1065
|
+
const index = parseInt(lastSegment, 10);
|
|
1066
|
+
if (isNaN(index))
|
|
1067
|
+
return null;
|
|
1068
|
+
return { value: current[index], parent: current, key: index };
|
|
1069
|
+
}
|
|
1070
|
+
else if (current !== null &&
|
|
1071
|
+
current !== undefined &&
|
|
1072
|
+
typeof current === "object") {
|
|
1073
|
+
return {
|
|
1074
|
+
value: current[lastSegment],
|
|
1075
|
+
parent: current,
|
|
1076
|
+
key: lastSegment,
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
else if (typeof lastSegment === "number") {
|
|
1081
|
+
if (Array.isArray(current)) {
|
|
1082
|
+
if (lastSegment >= 0 && lastSegment < current.length) {
|
|
1083
|
+
return {
|
|
1084
|
+
value: current[lastSegment],
|
|
1085
|
+
parent: current,
|
|
1086
|
+
key: lastSegment,
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return null;
|
|
1091
|
+
}
|
|
1092
|
+
else if (lastSegment instanceof RegExp) {
|
|
1093
|
+
if (Array.isArray(current)) {
|
|
1094
|
+
return null;
|
|
1095
|
+
}
|
|
1096
|
+
const obj = current;
|
|
1097
|
+
const matchingKey = Object.keys(obj).find((key) => lastSegment.test(key));
|
|
1098
|
+
if (!matchingKey)
|
|
1099
|
+
return null;
|
|
1100
|
+
return { value: obj[matchingKey], parent: current, key: matchingKey };
|
|
1101
|
+
}
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
function setValueAtPath(obj, pathSegments, newValue) {
|
|
1105
|
+
const result = getValueAtPath(obj, pathSegments);
|
|
1106
|
+
if (!result || result.parent === null)
|
|
1107
|
+
return false;
|
|
1108
|
+
if (Array.isArray(result.parent)) {
|
|
1109
|
+
result.parent[result.key] = newValue;
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
result.parent[result.key] = newValue;
|
|
1113
|
+
}
|
|
1114
|
+
return true;
|
|
1013
1115
|
}
|
|
1014
1116
|
/**
|
|
1015
|
-
*
|
|
1117
|
+
* Sets a value at a path, creating intermediate objects as needed.
|
|
1118
|
+
* Mutates the root object in place.
|
|
1119
|
+
* @param root - The root object to modify (must be an object, will be initialized if needed)
|
|
1120
|
+
* @param pathSegments - The path segments to traverse
|
|
1121
|
+
* @param value - The value to set, or null to delete the path
|
|
1122
|
+
* @throws Error if path cannot be created (e.g., array indices not supported, invalid parent types)
|
|
1016
1123
|
*/
|
|
1017
|
-
function
|
|
1018
|
-
if (
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (a && b && typeof a === "object") {
|
|
1023
|
-
try {
|
|
1024
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
1124
|
+
function setValueAtPathWithCreation(root, pathSegments, value) {
|
|
1125
|
+
if (value === null) {
|
|
1126
|
+
const result = getValueAtPath(root, pathSegments);
|
|
1127
|
+
if (result && result.parent !== null && !Array.isArray(result.parent)) {
|
|
1128
|
+
delete result.parent[result.key];
|
|
1025
1129
|
}
|
|
1026
|
-
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (!root || typeof root !== "object" || Array.isArray(root)) {
|
|
1133
|
+
throw new Error("Root must be an object");
|
|
1134
|
+
}
|
|
1135
|
+
let current = root;
|
|
1136
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
1137
|
+
const segment = pathSegments[i];
|
|
1138
|
+
if (typeof segment === "string") {
|
|
1139
|
+
if (!current ||
|
|
1140
|
+
typeof current !== "object" ||
|
|
1141
|
+
Array.isArray(current) ||
|
|
1142
|
+
!(segment in current) ||
|
|
1143
|
+
typeof current[segment] !== "object" ||
|
|
1144
|
+
current[segment] === null ||
|
|
1145
|
+
Array.isArray(current[segment])) {
|
|
1146
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
1147
|
+
throw new Error(`Cannot create path: parent at segment ${i} is not an object`);
|
|
1148
|
+
}
|
|
1149
|
+
current[segment] = {};
|
|
1150
|
+
}
|
|
1151
|
+
current = current[segment];
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
throw new Error("Array indices not supported in extData paths");
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
1158
|
+
if (typeof lastSegment === "string") {
|
|
1159
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
1160
|
+
throw new Error(`Cannot set value: parent at final segment is not an object`);
|
|
1161
|
+
}
|
|
1162
|
+
current[lastSegment] = value;
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
throw new Error("Array indices not supported in extData paths");
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function findMatchingPaths(obj, pathSegments, currentPath = []) {
|
|
1169
|
+
if (pathSegments.length === 0) {
|
|
1170
|
+
return [{ path: currentPath, value: obj }];
|
|
1171
|
+
}
|
|
1172
|
+
const [currentSegment, ...remainingSegments] = pathSegments;
|
|
1173
|
+
const results = [];
|
|
1174
|
+
if (currentSegment === undefined) {
|
|
1175
|
+
return results;
|
|
1176
|
+
}
|
|
1177
|
+
if (typeof currentSegment === "string") {
|
|
1178
|
+
if (Array.isArray(obj)) {
|
|
1179
|
+
const index = parseInt(currentSegment, 10);
|
|
1180
|
+
if (!isNaN(index) && index >= 0 && index < obj.length) {
|
|
1181
|
+
results.push(...findMatchingPaths(obj[index], remainingSegments, [
|
|
1182
|
+
...currentPath,
|
|
1183
|
+
index,
|
|
1184
|
+
]));
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
else if (obj !== null && obj !== undefined && typeof obj === "object") {
|
|
1188
|
+
const objRecord = obj;
|
|
1189
|
+
if (currentSegment in objRecord) {
|
|
1190
|
+
results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [
|
|
1191
|
+
...currentPath,
|
|
1192
|
+
currentSegment,
|
|
1193
|
+
]));
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
else if (typeof currentSegment === "number") {
|
|
1198
|
+
if (Array.isArray(obj)) {
|
|
1199
|
+
if (currentSegment >= 0 && currentSegment < obj.length) {
|
|
1200
|
+
results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [
|
|
1201
|
+
...currentPath,
|
|
1202
|
+
currentSegment,
|
|
1203
|
+
]));
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
else if (currentSegment instanceof RegExp) {
|
|
1208
|
+
if (Array.isArray(obj)) {
|
|
1209
|
+
for (let i = 0; i < obj.length; i++) {
|
|
1210
|
+
results.push(...findMatchingPaths(obj[i], remainingSegments, [...currentPath, i]));
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
else if (obj !== null && obj !== undefined && typeof obj === "object") {
|
|
1214
|
+
const objRecord = obj;
|
|
1215
|
+
for (const key of Object.keys(objRecord)) {
|
|
1216
|
+
if (currentSegment.test(key)) {
|
|
1217
|
+
results.push(...findMatchingPaths(objRecord[key], remainingSegments, [
|
|
1218
|
+
...currentPath,
|
|
1219
|
+
key,
|
|
1220
|
+
]));
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
return results;
|
|
1226
|
+
}
|
|
1227
|
+
function stringifyJson(obj, oneLiner) {
|
|
1228
|
+
// No formatting requested: behave exactly like native JSON.stringify.
|
|
1229
|
+
if (!oneLiner)
|
|
1230
|
+
return JSON.stringify(obj);
|
|
1231
|
+
const indentSize = Number.isFinite(oneLiner.indent)
|
|
1232
|
+
? Math.max(0, Math.floor(oneLiner.indent))
|
|
1233
|
+
: 2;
|
|
1234
|
+
const maxDepth = typeof oneLiner.maxDepth === "number" && Number.isFinite(oneLiner.maxDepth)
|
|
1235
|
+
? oneLiner.maxDepth
|
|
1236
|
+
: undefined;
|
|
1237
|
+
const patterns = (oneLiner.paths || []).filter(Boolean);
|
|
1238
|
+
// Preserve JSON.stringify semantics for things like toJSON(), dropping functions/undefined, etc.
|
|
1239
|
+
// Note: this still throws on circular structures (same as JSON.stringify).
|
|
1240
|
+
const base = JSON.stringify(obj);
|
|
1241
|
+
if (base === undefined)
|
|
1242
|
+
return base;
|
|
1243
|
+
const value = JSON.parse(base);
|
|
1244
|
+
const pathMatchers = patterns.map((p) => compilePathMatcher(String(p)));
|
|
1245
|
+
const formatKey = (k) => JSON.stringify(k);
|
|
1246
|
+
const shouldInline = (path, key, v, depth) => {
|
|
1247
|
+
if (maxDepth !== undefined && depth > maxDepth)
|
|
1248
|
+
return true;
|
|
1249
|
+
if (oneLiner.criteria?.({ path, key, value: v, depth }))
|
|
1250
|
+
return true;
|
|
1251
|
+
if (!pathMatchers.length)
|
|
1027
1252
|
return false;
|
|
1253
|
+
const tokensWithRoot = tokenizePath(path);
|
|
1254
|
+
const tokensNoRoot = tokensWithRoot[0] === "$" ? tokensWithRoot.slice(1) : tokensWithRoot;
|
|
1255
|
+
return pathMatchers.some((m) => m(tokensWithRoot, tokensNoRoot));
|
|
1256
|
+
};
|
|
1257
|
+
const stringifyInline = (v, depth, path) => {
|
|
1258
|
+
if (v === null)
|
|
1259
|
+
return "null";
|
|
1260
|
+
const t = typeof v;
|
|
1261
|
+
if (t === "string")
|
|
1262
|
+
return JSON.stringify(v);
|
|
1263
|
+
if (t === "number" || t === "boolean")
|
|
1264
|
+
return String(v);
|
|
1265
|
+
if (Array.isArray(v)) {
|
|
1266
|
+
if (!v.length)
|
|
1267
|
+
return "[]";
|
|
1268
|
+
const parts = v.map((vv, i) => stringifyInline(vv));
|
|
1269
|
+
return `[ ${parts.join(", ")} ]`;
|
|
1270
|
+
}
|
|
1271
|
+
if (t === "object") {
|
|
1272
|
+
const keys = Object.keys(v);
|
|
1273
|
+
if (!keys.length)
|
|
1274
|
+
return "{}";
|
|
1275
|
+
const parts = keys.map((k) => `${formatKey(k)}: ${stringifyInline(v[k])}`);
|
|
1276
|
+
return `{ ${parts.join(", ")} }`;
|
|
1277
|
+
}
|
|
1278
|
+
// Shouldn't happen after JSON.parse(JSON.stringify(...)), but keep output valid JSON.
|
|
1279
|
+
return "null";
|
|
1280
|
+
};
|
|
1281
|
+
const stringifyPretty = (v, depth, path, key) => {
|
|
1282
|
+
if (shouldInline(path, key, v, depth))
|
|
1283
|
+
return stringifyInline(v);
|
|
1284
|
+
if (v === null)
|
|
1285
|
+
return "null";
|
|
1286
|
+
const t = typeof v;
|
|
1287
|
+
if (t === "string")
|
|
1288
|
+
return JSON.stringify(v);
|
|
1289
|
+
if (t === "number" || t === "boolean")
|
|
1290
|
+
return String(v);
|
|
1291
|
+
const indentCur = " ".repeat(indentSize * depth);
|
|
1292
|
+
const indentInner = " ".repeat(indentSize * (depth + 1));
|
|
1293
|
+
if (Array.isArray(v)) {
|
|
1294
|
+
if (!v.length)
|
|
1295
|
+
return "[]";
|
|
1296
|
+
// Compact array style: `[{...}, {...}]` while still allowing multi-line objects within.
|
|
1297
|
+
const parts = v.map((vv, i) => stringifyPretty(vv, depth + 1, `${path}[${i}]`, String(i)));
|
|
1298
|
+
return `[ ${parts.join(", ")} ]`;
|
|
1299
|
+
}
|
|
1300
|
+
if (t === "object") {
|
|
1301
|
+
const keys = Object.keys(v);
|
|
1302
|
+
if (!keys.length)
|
|
1303
|
+
return "{}";
|
|
1304
|
+
const lines = keys.map((k, idx) => {
|
|
1305
|
+
const childPath = `${path}.${k}`;
|
|
1306
|
+
const rendered = stringifyPretty(v[k], depth + 1, childPath, k);
|
|
1307
|
+
const comma = idx === keys.length - 1 ? "" : ",";
|
|
1308
|
+
return `${indentInner}${formatKey(k)}: ${rendered}${comma}`;
|
|
1309
|
+
});
|
|
1310
|
+
return `{\n${lines.join("\n")}\n${indentCur}}`;
|
|
1028
1311
|
}
|
|
1312
|
+
return "null";
|
|
1313
|
+
};
|
|
1314
|
+
return stringifyPretty(value, 0, "$", "$");
|
|
1315
|
+
}
|
|
1316
|
+
function tokenizePath(path) {
|
|
1317
|
+
// Path format we generate: `$`, `$.a.b[0].c`
|
|
1318
|
+
// Tokens: ["$", "a", "b", "[0]", "c"]
|
|
1319
|
+
const tokens = [];
|
|
1320
|
+
let i = 0;
|
|
1321
|
+
if (path.startsWith("$")) {
|
|
1322
|
+
tokens.push("$");
|
|
1323
|
+
i = 1;
|
|
1324
|
+
}
|
|
1325
|
+
while (i < path.length) {
|
|
1326
|
+
const ch = path[i];
|
|
1327
|
+
if (ch === ".") {
|
|
1328
|
+
i++;
|
|
1329
|
+
const start = i;
|
|
1330
|
+
while (i < path.length && path[i] !== "." && path[i] !== "[")
|
|
1331
|
+
i++;
|
|
1332
|
+
if (i > start)
|
|
1333
|
+
tokens.push(path.slice(start, i));
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
if (ch === "[") {
|
|
1337
|
+
const end = path.indexOf("]", i + 1);
|
|
1338
|
+
if (end < 0) {
|
|
1339
|
+
tokens.push(path.slice(i));
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
tokens.push(path.slice(i, end + 1));
|
|
1343
|
+
i = end + 1;
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
// Unexpected char; skip.
|
|
1347
|
+
i++;
|
|
1029
1348
|
}
|
|
1030
|
-
return
|
|
1349
|
+
return tokens.filter((t) => t.length);
|
|
1031
1350
|
}
|
|
1351
|
+
function tokenizePattern(pattern) {
|
|
1352
|
+
// Pattern format: `$`, `$.a.*.b`, `**.graph.**`, `arr[2].z`
|
|
1353
|
+
// Tokens: ["$", "a", "*", "b"] etc. Brackets become their own token: ["arr","[2]","z"]
|
|
1354
|
+
const tokens = [];
|
|
1355
|
+
let i = 0;
|
|
1356
|
+
if (pattern.startsWith("$")) {
|
|
1357
|
+
tokens.push("$");
|
|
1358
|
+
i = 1;
|
|
1359
|
+
}
|
|
1360
|
+
while (i < pattern.length) {
|
|
1361
|
+
const ch = pattern[i];
|
|
1362
|
+
if (ch === ".") {
|
|
1363
|
+
i++;
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
if (ch === "[") {
|
|
1367
|
+
const end = pattern.indexOf("]", i + 1);
|
|
1368
|
+
if (end < 0) {
|
|
1369
|
+
tokens.push(pattern.slice(i));
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
tokens.push(pattern.slice(i, end + 1));
|
|
1373
|
+
i = end + 1;
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const start = i;
|
|
1377
|
+
while (i < pattern.length && pattern[i] !== "." && pattern[i] !== "[")
|
|
1378
|
+
i++;
|
|
1379
|
+
if (i > start)
|
|
1380
|
+
tokens.push(pattern.slice(start, i));
|
|
1381
|
+
}
|
|
1382
|
+
return tokens.filter((t) => t.length);
|
|
1383
|
+
}
|
|
1384
|
+
function compilePathMatcher(pattern) {
|
|
1385
|
+
// Wildcard semantics (case-insensitive):
|
|
1386
|
+
// - `*` matches exactly 1 path segment (key or `[index]`)
|
|
1387
|
+
// - `**` matches 0 or more segments
|
|
1388
|
+
// - `#` matches exactly 1 numeric segment (e.g. `"0"` or `[0]`)
|
|
1389
|
+
// - `##` matches 0 or more numeric segments
|
|
1390
|
+
// - `[*]` matches exactly 1 index segment
|
|
1391
|
+
const pat = tokenizePattern(pattern);
|
|
1392
|
+
const expectsRoot = pat[0] === "$";
|
|
1393
|
+
const eq = (a, b) => a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0;
|
|
1394
|
+
const isIndex = (t) => t.startsWith("[") && t.endsWith("]");
|
|
1395
|
+
const isNumericKey = (t) => /^[0-9]+$/.test(t);
|
|
1396
|
+
const isNumericSegment = (t) => isNumericKey(t) || (isIndex(t) && /^[0-9]+$/.test(t.slice(1, -1)));
|
|
1397
|
+
const match = (patTokens, pathTokens) => {
|
|
1398
|
+
const memo = new Map();
|
|
1399
|
+
const go = (pi, ti) => {
|
|
1400
|
+
const key = `${pi},${ti}`;
|
|
1401
|
+
const cached = memo.get(key);
|
|
1402
|
+
if (cached !== undefined)
|
|
1403
|
+
return cached;
|
|
1404
|
+
let res = false;
|
|
1405
|
+
if (pi === patTokens.length) {
|
|
1406
|
+
res = ti === pathTokens.length;
|
|
1407
|
+
}
|
|
1408
|
+
else {
|
|
1409
|
+
const p = patTokens[pi];
|
|
1410
|
+
if (p === "**") {
|
|
1411
|
+
// Match any number of segments, including zero.
|
|
1412
|
+
for (let k = ti; k <= pathTokens.length; k++) {
|
|
1413
|
+
if (go(pi + 1, k)) {
|
|
1414
|
+
res = true;
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
else if (p === "##") {
|
|
1420
|
+
// Match any number of numeric segments, including zero.
|
|
1421
|
+
for (let k = ti; k <= pathTokens.length; k++) {
|
|
1422
|
+
let ok = true;
|
|
1423
|
+
for (let j = ti; j < k; j++) {
|
|
1424
|
+
if (!isNumericSegment(pathTokens[j])) {
|
|
1425
|
+
ok = false;
|
|
1426
|
+
break;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
if (ok && go(pi + 1, k)) {
|
|
1430
|
+
res = true;
|
|
1431
|
+
break;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
else if (p === "*") {
|
|
1436
|
+
res = ti < pathTokens.length && go(pi + 1, ti + 1);
|
|
1437
|
+
}
|
|
1438
|
+
else if (p === "#") {
|
|
1439
|
+
res =
|
|
1440
|
+
ti < pathTokens.length &&
|
|
1441
|
+
isNumericSegment(pathTokens[ti]) &&
|
|
1442
|
+
go(pi + 1, ti + 1);
|
|
1443
|
+
}
|
|
1444
|
+
else if (p === "[*]") {
|
|
1445
|
+
res =
|
|
1446
|
+
ti < pathTokens.length &&
|
|
1447
|
+
isIndex(pathTokens[ti]) &&
|
|
1448
|
+
go(pi + 1, ti + 1);
|
|
1449
|
+
}
|
|
1450
|
+
else {
|
|
1451
|
+
res =
|
|
1452
|
+
ti < pathTokens.length &&
|
|
1453
|
+
eq(p.toLowerCase(), pathTokens[ti].toLowerCase()) &&
|
|
1454
|
+
go(pi + 1, ti + 1);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
memo.set(key, res);
|
|
1458
|
+
return res;
|
|
1459
|
+
};
|
|
1460
|
+
return go(0, 0);
|
|
1461
|
+
};
|
|
1462
|
+
return (tokensWithRoot, tokensNoRoot) => match(pat, expectsRoot ? tokensWithRoot : tokensNoRoot);
|
|
1463
|
+
}
|
|
1464
|
+
function stringifySceneAndOps(obj) {
|
|
1465
|
+
return stringifyJson(obj, {
|
|
1466
|
+
indent: 2,
|
|
1467
|
+
paths: [
|
|
1468
|
+
"**.camera.animation.*",
|
|
1469
|
+
"**.animations.*",
|
|
1470
|
+
"**.nodes.#.*",
|
|
1471
|
+
"**.graphs.#.*",
|
|
1472
|
+
"**.instances.#.*",
|
|
1473
|
+
"**.shaders.#.*",
|
|
1474
|
+
"**.materials.#.*",
|
|
1475
|
+
"**.ext.#",
|
|
1476
|
+
"**.ext.*.#.*",
|
|
1477
|
+
"**.perUserExt.*.#",
|
|
1478
|
+
"**.perUserExt.*.*.#",
|
|
1479
|
+
],
|
|
1480
|
+
criteria: ({ value, key }) => (typeof value === "object" &&
|
|
1481
|
+
value &&
|
|
1482
|
+
Object.keys(value).sort().join(",") === "x,y,z") ||
|
|
1483
|
+
key === "value",
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1032
1487
|
/**
|
|
1033
1488
|
* A reusable logger class that supports configurable log levels and prefixes.
|
|
1034
1489
|
* Can be instantiated with a default log level and optionally override per call.
|
|
@@ -1106,7 +1561,7 @@ class LevelLogger {
|
|
|
1106
1561
|
const contextStr = rest && Object.keys(rest).length > 0
|
|
1107
1562
|
? `${formatJson ? "\n" : " "}${Object.entries(rest)
|
|
1108
1563
|
.map(([k, v]) => formatJson
|
|
1109
|
-
? `${k}=${
|
|
1564
|
+
? `${k}=${stringifySceneAndOps(v)}`
|
|
1110
1565
|
: `${k}=${JSON.stringify(v)}`)
|
|
1111
1566
|
.join(formatJson ? "\n" : " ")}`
|
|
1112
1567
|
: "";
|
|
@@ -1436,6 +1891,40 @@ class RunContextManager {
|
|
|
1436
1891
|
}
|
|
1437
1892
|
}
|
|
1438
1893
|
|
|
1894
|
+
/**
|
|
1895
|
+
* Shared utility functions for runtime components
|
|
1896
|
+
*/
|
|
1897
|
+
/**
|
|
1898
|
+
* Type guard to check if a value is a Promise
|
|
1899
|
+
*/
|
|
1900
|
+
function isPromise(value) {
|
|
1901
|
+
return !!value && typeof value.then === "function";
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Unwrap a value that might be a Promise
|
|
1905
|
+
*/
|
|
1906
|
+
async function unwrapMaybePromise(value) {
|
|
1907
|
+
return isPromise(value) ? await value : value;
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Shallow/deep-ish equality check to avoid unnecessary runs on identical values
|
|
1911
|
+
*/
|
|
1912
|
+
function valuesEqual(a, b) {
|
|
1913
|
+
if (a === b)
|
|
1914
|
+
return true;
|
|
1915
|
+
if (typeof a !== typeof b)
|
|
1916
|
+
return false;
|
|
1917
|
+
if (a && b && typeof a === "object") {
|
|
1918
|
+
try {
|
|
1919
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
1920
|
+
}
|
|
1921
|
+
catch {
|
|
1922
|
+
return false;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
return false;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1439
1928
|
function tryHandleResolving(def, registry, environment) {
|
|
1440
1929
|
const out = new Map();
|
|
1441
1930
|
const pending = new Set();
|
|
@@ -2152,7 +2641,7 @@ class EdgePropagator {
|
|
|
2152
2641
|
// Set input value (respecting skipPropagateValues)
|
|
2153
2642
|
const shouldSetValue = this.shouldSetInputValue(effectiveRunContexts);
|
|
2154
2643
|
if (shouldSetValue && valueChanged) {
|
|
2155
|
-
this.setTargetInput(edge,
|
|
2644
|
+
this.setTargetInput(edge, processedValue);
|
|
2156
2645
|
}
|
|
2157
2646
|
else if (shouldSetValue && !valueChanged) {
|
|
2158
2647
|
// Even if value didn't change, update timestamp if we're forcing execution
|
|
@@ -2211,7 +2700,7 @@ class EdgePropagator {
|
|
|
2211
2700
|
/**
|
|
2212
2701
|
* Set target input value and emit event
|
|
2213
2702
|
*/
|
|
2214
|
-
setTargetInput(edge,
|
|
2703
|
+
setTargetInput(edge, value) {
|
|
2215
2704
|
this.graph.updateNodeInput(edge.target.nodeId, edge.target.handle, value);
|
|
2216
2705
|
this.handleResolver.scheduleRecomputeHandles(edge.target.nodeId);
|
|
2217
2706
|
}
|
|
@@ -2222,7 +2711,10 @@ class EdgePropagator {
|
|
|
2222
2711
|
// Determine if we should propagate
|
|
2223
2712
|
const shouldPropagate = this.shouldPropagateExecution(effectiveRunContexts);
|
|
2224
2713
|
if (shouldPropagate && this.graph.allInboundHaveValue(targetNodeId)) {
|
|
2225
|
-
this.nodeExecutor.execute(targetNodeId,
|
|
2714
|
+
this.nodeExecutor.execute(targetNodeId, {
|
|
2715
|
+
runContextIds: effectiveRunContexts,
|
|
2716
|
+
reason: "executeDownstream",
|
|
2717
|
+
});
|
|
2226
2718
|
}
|
|
2227
2719
|
}
|
|
2228
2720
|
/**
|
|
@@ -2352,7 +2844,7 @@ class NodeExecutor {
|
|
|
2352
2844
|
/**
|
|
2353
2845
|
* Create an execution context for a node
|
|
2354
2846
|
*/
|
|
2355
|
-
createExecutionContext(nodeId,
|
|
2847
|
+
createExecutionContext(nodeId, inputs, runId, abortSignal, runContextIds, options) {
|
|
2356
2848
|
const emitHandler = options?.emitHandler ??
|
|
2357
2849
|
((handle, value) => {
|
|
2358
2850
|
this.edgePropagator.propagate(nodeId, handle, value, runContextIds);
|
|
@@ -2364,8 +2856,9 @@ class NodeExecutor {
|
|
|
2364
2856
|
});
|
|
2365
2857
|
});
|
|
2366
2858
|
// Create log function that respects node's logLevel using LevelLogger
|
|
2367
|
-
const
|
|
2368
|
-
const
|
|
2859
|
+
const node = this.graph.getNode(nodeId);
|
|
2860
|
+
const nodeLogLevel = node?.logLevel ?? "info";
|
|
2861
|
+
const logger = new LevelLogger(nodeLogLevel, `[node:${runId || nodeId}:${node?.typeId ?? ""}]`);
|
|
2369
2862
|
const log = (level, message, context) => {
|
|
2370
2863
|
switch (level) {
|
|
2371
2864
|
case "debug":
|
|
@@ -2384,7 +2877,7 @@ class NodeExecutor {
|
|
|
2384
2877
|
};
|
|
2385
2878
|
return {
|
|
2386
2879
|
nodeId,
|
|
2387
|
-
state: node
|
|
2880
|
+
state: node?.state,
|
|
2388
2881
|
setState: (next) => this.graph.updateNodeState(nodeId, next),
|
|
2389
2882
|
emit: emitHandler,
|
|
2390
2883
|
invalidateDownstream: () => {
|
|
@@ -2399,7 +2892,10 @@ class NodeExecutor {
|
|
|
2399
2892
|
this.runContextManager.createRunContext(nodeId, opts),
|
|
2400
2893
|
]);
|
|
2401
2894
|
}
|
|
2402
|
-
this.execute(nodeId,
|
|
2895
|
+
this.execute(nodeId, {
|
|
2896
|
+
runContextIds: runContextIdsToUse,
|
|
2897
|
+
reason: opts?.reason ?? "executeFromContext",
|
|
2898
|
+
});
|
|
2403
2899
|
}
|
|
2404
2900
|
},
|
|
2405
2901
|
getInput: (handle) => inputs[handle],
|
|
@@ -2411,7 +2907,7 @@ class NodeExecutor {
|
|
|
2411
2907
|
this.eventEmitter.emit("stats", {
|
|
2412
2908
|
kind: "node-custom-data",
|
|
2413
2909
|
nodeId,
|
|
2414
|
-
typeId: node
|
|
2910
|
+
typeId: node?.typeId ?? "",
|
|
2415
2911
|
runId,
|
|
2416
2912
|
data,
|
|
2417
2913
|
});
|
|
@@ -2422,13 +2918,14 @@ class NodeExecutor {
|
|
|
2422
2918
|
/**
|
|
2423
2919
|
* Internal method for executing inputs changed (also used by GraphRuntime)
|
|
2424
2920
|
*/
|
|
2425
|
-
execute(nodeId,
|
|
2921
|
+
execute(nodeId, opts) {
|
|
2922
|
+
let { runContextIds, canSkipHandleResolution, reason = "" } = opts ?? {};
|
|
2426
2923
|
const node = this.graph.getNode(nodeId);
|
|
2427
2924
|
if (!node)
|
|
2428
2925
|
return;
|
|
2429
2926
|
const runMode = this.runtime.getRunMode();
|
|
2430
2927
|
if (!runMode) {
|
|
2431
|
-
console.trace(
|
|
2928
|
+
console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: no runMode, skipping execution`);
|
|
2432
2929
|
return;
|
|
2433
2930
|
}
|
|
2434
2931
|
// In manual mode, require runContextIds unless autoRun policy is set
|
|
@@ -2440,12 +2937,12 @@ class NodeExecutor {
|
|
|
2440
2937
|
]);
|
|
2441
2938
|
}
|
|
2442
2939
|
else {
|
|
2443
|
-
console.trace(
|
|
2940
|
+
console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: no runContextIds provided in manual mode, skipping execution`);
|
|
2444
2941
|
return;
|
|
2445
2942
|
}
|
|
2446
2943
|
}
|
|
2447
2944
|
if (runMode === "auto" && runContextIds && runContextIds.size > 0) {
|
|
2448
|
-
console.trace(
|
|
2945
|
+
console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: runContextIds provided in auto mode, ignoring`);
|
|
2449
2946
|
runContextIds = undefined;
|
|
2450
2947
|
}
|
|
2451
2948
|
// Early validation for auto-mode paused state
|
|
@@ -2488,7 +2985,11 @@ class NodeExecutor {
|
|
|
2488
2985
|
// Re-check node still exists and conditions
|
|
2489
2986
|
const nodeAfter = this.graph.getNode(nodeId);
|
|
2490
2987
|
if (nodeAfter) {
|
|
2491
|
-
this.execute(nodeId,
|
|
2988
|
+
this.execute(nodeId, {
|
|
2989
|
+
runContextIds,
|
|
2990
|
+
canSkipHandleResolution: true,
|
|
2991
|
+
reason: opts?.reason,
|
|
2992
|
+
});
|
|
2492
2993
|
}
|
|
2493
2994
|
if (runContextIds && runContextIds.size > 0) {
|
|
2494
2995
|
for (const id of runContextIds) {
|
|
@@ -2500,19 +3001,22 @@ class NodeExecutor {
|
|
|
2500
3001
|
}
|
|
2501
3002
|
// Handle debouncing
|
|
2502
3003
|
const now = Date.now();
|
|
2503
|
-
if (this.shouldDebounce(
|
|
2504
|
-
this.handleDebouncedSchedule(
|
|
3004
|
+
if (this.shouldDebounce(nodeId, now)) {
|
|
3005
|
+
this.handleDebouncedSchedule(nodeId, now, runContextIds, reason);
|
|
2505
3006
|
return;
|
|
2506
3007
|
}
|
|
2507
3008
|
// Prepare execution plan
|
|
2508
|
-
const executionPlan = this.prepareExecutionPlan(
|
|
3009
|
+
const executionPlan = this.prepareExecutionPlan(nodeId, runContextIds, now, reason);
|
|
2509
3010
|
// Route to appropriate concurrency handler
|
|
2510
|
-
this.routeToConcurrencyHandler(
|
|
3011
|
+
this.routeToConcurrencyHandler(nodeId, executionPlan);
|
|
2511
3012
|
}
|
|
2512
3013
|
/**
|
|
2513
3014
|
* Check if execution should be debounced
|
|
2514
3015
|
*/
|
|
2515
|
-
shouldDebounce(
|
|
3016
|
+
shouldDebounce(nodeId, now) {
|
|
3017
|
+
const node = this.graph.getNode(nodeId);
|
|
3018
|
+
if (!node)
|
|
3019
|
+
return false;
|
|
2516
3020
|
const policy = node.policy ?? {};
|
|
2517
3021
|
const lastScheduledAt = node.lastScheduledAt;
|
|
2518
3022
|
return !!(policy.debounceMs &&
|
|
@@ -2522,80 +3026,78 @@ class NodeExecutor {
|
|
|
2522
3026
|
/**
|
|
2523
3027
|
* Handle debounced scheduling by replacing the latest queued item
|
|
2524
3028
|
*/
|
|
2525
|
-
handleDebouncedSchedule(
|
|
3029
|
+
handleDebouncedSchedule(nodeId, now, runContextIds, reason) {
|
|
3030
|
+
const node = this.graph.getNode(nodeId);
|
|
3031
|
+
if (!node)
|
|
3032
|
+
return;
|
|
2526
3033
|
// Decrement pendingQueued for any existing queued items before replacing
|
|
2527
3034
|
if (node.queue.length > 0) {
|
|
2528
3035
|
this.decrementQueuedForPlans(node.queue, nodeId);
|
|
2529
3036
|
}
|
|
2530
|
-
const
|
|
2531
|
-
|
|
2532
|
-
const runId = `${nodeId}:${runSeq}:${now}`;
|
|
2533
|
-
const policySnapshot = node.policy ? { ...node.policy } : undefined;
|
|
2534
|
-
const plan = {
|
|
2535
|
-
runId,
|
|
2536
|
-
effectiveInputs,
|
|
2537
|
-
runContextIdsForRun: runContextIds,
|
|
2538
|
-
timestamp: now,
|
|
2539
|
-
policy: policySnapshot,
|
|
2540
|
-
};
|
|
2541
|
-
this.graph.replaceNodeQueue(nodeId, [plan]);
|
|
3037
|
+
const executionPlan = this.prepareExecutionPlan(nodeId, runContextIds, now, reason);
|
|
3038
|
+
this.graph.replaceNodeQueue(nodeId, [executionPlan]);
|
|
2542
3039
|
}
|
|
2543
3040
|
/**
|
|
2544
3041
|
* Prepare execution plan with all necessary information
|
|
2545
3042
|
*/
|
|
2546
|
-
prepareExecutionPlan(
|
|
3043
|
+
prepareExecutionPlan(nodeId, runContextIds, now, reason) {
|
|
3044
|
+
const node = this.graph.getNode(nodeId);
|
|
2547
3045
|
this.graph.setNodeLastScheduledAt(nodeId, now);
|
|
2548
3046
|
const runSeq = this.graph.incrementNodeRunSeq(nodeId);
|
|
2549
3047
|
const runId = `${nodeId}:${runSeq}:${now}`;
|
|
2550
3048
|
this.graph.setNodeLatestRunId(nodeId, runId);
|
|
2551
3049
|
const effectiveInputs = this.getEffectiveInputs(nodeId);
|
|
2552
3050
|
// Take a shallow snapshot of the current policy for this run
|
|
2553
|
-
const policySnapshot = node
|
|
3051
|
+
const policySnapshot = node?.policy ? { ...node.policy } : undefined;
|
|
2554
3052
|
return {
|
|
2555
3053
|
runId,
|
|
2556
3054
|
effectiveInputs,
|
|
2557
3055
|
runContextIdsForRun: runContextIds,
|
|
2558
3056
|
timestamp: now,
|
|
2559
3057
|
policy: policySnapshot,
|
|
3058
|
+
reason,
|
|
2560
3059
|
};
|
|
2561
3060
|
}
|
|
2562
3061
|
/**
|
|
2563
3062
|
* Route execution to appropriate concurrency handler
|
|
2564
3063
|
*/
|
|
2565
|
-
routeToConcurrencyHandler(
|
|
3064
|
+
routeToConcurrencyHandler(nodeId, plan) {
|
|
2566
3065
|
const mode = plan.policy?.asyncConcurrency ?? "switch";
|
|
2567
3066
|
switch (mode) {
|
|
2568
3067
|
case "drop":
|
|
2569
|
-
this.handleDropMode(
|
|
3068
|
+
this.handleDropMode(nodeId, plan);
|
|
2570
3069
|
break;
|
|
2571
3070
|
case "queue":
|
|
2572
|
-
this.handleQueueMode(
|
|
3071
|
+
this.handleQueueMode(nodeId, plan);
|
|
2573
3072
|
break;
|
|
2574
3073
|
case "switch":
|
|
2575
3074
|
case "merge":
|
|
2576
3075
|
default:
|
|
2577
|
-
this.startRun(
|
|
3076
|
+
this.startRun(nodeId, plan);
|
|
2578
3077
|
break;
|
|
2579
3078
|
}
|
|
2580
3079
|
}
|
|
2581
3080
|
/**
|
|
2582
3081
|
* Handle drop mode - drop execution if node is already running, otherwise start run
|
|
2583
3082
|
*/
|
|
2584
|
-
handleDropMode(
|
|
3083
|
+
handleDropMode(nodeId, plan) {
|
|
3084
|
+
const node = this.graph.getNode(nodeId);
|
|
3085
|
+
if (!node)
|
|
3086
|
+
return;
|
|
2585
3087
|
// Drop if node is already running
|
|
2586
3088
|
if (node.activeControllers.size > 0) {
|
|
2587
3089
|
return; // Don't increment pendingCount if we're dropping this run
|
|
2588
3090
|
}
|
|
2589
3091
|
// Start run if node is not running
|
|
2590
|
-
this.startRun(
|
|
3092
|
+
this.startRun(nodeId, plan);
|
|
2591
3093
|
}
|
|
2592
3094
|
/**
|
|
2593
3095
|
* Handle queue mode - add to queue and process sequentially
|
|
2594
3096
|
*/
|
|
2595
|
-
handleQueueMode(
|
|
3097
|
+
handleQueueMode(nodeId, plan) {
|
|
2596
3098
|
const maxQ = plan.policy?.maxQueue ?? 8;
|
|
2597
|
-
const
|
|
2598
|
-
if (!
|
|
3099
|
+
const node = this.graph.getNode(nodeId);
|
|
3100
|
+
if (!node)
|
|
2599
3101
|
return;
|
|
2600
3102
|
// Keep the originating run-context alive while work is queued by
|
|
2601
3103
|
// incrementing queued counters for the plan's run-contexts.
|
|
@@ -2605,18 +3107,18 @@ class NodeExecutor {
|
|
|
2605
3107
|
}
|
|
2606
3108
|
}
|
|
2607
3109
|
this.graph.addToNodeQueue(nodeId, plan);
|
|
2608
|
-
if (
|
|
3110
|
+
if (node.queue.length > maxQ) {
|
|
2609
3111
|
const dropped = this.graph.shiftNodeQueue(nodeId);
|
|
2610
3112
|
if (dropped) {
|
|
2611
3113
|
this.decrementQueuedForPlans(dropped, nodeId);
|
|
2612
3114
|
}
|
|
2613
3115
|
}
|
|
2614
|
-
this.processQueue(
|
|
3116
|
+
this.processQueue(nodeId);
|
|
2615
3117
|
}
|
|
2616
3118
|
/**
|
|
2617
3119
|
* Process queued executions sequentially
|
|
2618
3120
|
*/
|
|
2619
|
-
processQueue(
|
|
3121
|
+
processQueue(nodeId) {
|
|
2620
3122
|
const processNext = () => {
|
|
2621
3123
|
const node = this.graph.getNode(nodeId);
|
|
2622
3124
|
if (!node)
|
|
@@ -2629,7 +3131,7 @@ class NodeExecutor {
|
|
|
2629
3131
|
this.graph.setNodeLatestRunId(nodeId, next.runId);
|
|
2630
3132
|
// Start the run first (which increments pendingNodes), then decrement
|
|
2631
3133
|
// pendingQueued to ensure the run context stays alive.
|
|
2632
|
-
this.startRun(
|
|
3134
|
+
this.startRun(nodeId, next, () => {
|
|
2633
3135
|
setTimeout(processNext, 0);
|
|
2634
3136
|
});
|
|
2635
3137
|
this.decrementQueuedForPlans(next, nodeId);
|
|
@@ -2639,19 +3141,19 @@ class NodeExecutor {
|
|
|
2639
3141
|
/**
|
|
2640
3142
|
* Start a node execution run
|
|
2641
3143
|
*/
|
|
2642
|
-
startRun(
|
|
3144
|
+
startRun(nodeId, plan, onDone) {
|
|
2643
3145
|
// Track run-contexts
|
|
2644
3146
|
this.trackRunContextStart(nodeId, plan.runContextIdsForRun);
|
|
2645
3147
|
// Setup execution controller
|
|
2646
|
-
const controller = this.createExecutionController(nodeId,
|
|
3148
|
+
const controller = this.createExecutionController(nodeId, plan.runId);
|
|
2647
3149
|
// Handle concurrency mode
|
|
2648
|
-
this.applyConcurrencyMode(nodeId,
|
|
3150
|
+
this.applyConcurrencyMode(nodeId, controller, plan);
|
|
2649
3151
|
// Setup timeout if needed
|
|
2650
|
-
const timeoutId = this.setupTimeout(
|
|
3152
|
+
const timeoutId = this.setupTimeout(controller, plan);
|
|
2651
3153
|
// Create execution context
|
|
2652
|
-
const execCtx = this.createExecutionContext(nodeId,
|
|
3154
|
+
const execCtx = this.createExecutionContext(nodeId, plan.effectiveInputs, plan.runId, controller.signal, plan.runContextIdsForRun, this.createEmitAndProgressHandlers(nodeId, plan));
|
|
2653
3155
|
// Execute
|
|
2654
|
-
this.executeNode(
|
|
3156
|
+
this.executeNode(nodeId, execCtx, plan, controller, timeoutId, onDone);
|
|
2655
3157
|
}
|
|
2656
3158
|
/**
|
|
2657
3159
|
* Track run-context start for pending nodes
|
|
@@ -2680,12 +3182,13 @@ class NodeExecutor {
|
|
|
2680
3182
|
/**
|
|
2681
3183
|
* Create execution controller and update node stats
|
|
2682
3184
|
*/
|
|
2683
|
-
createExecutionController(nodeId,
|
|
3185
|
+
createExecutionController(nodeId, runId) {
|
|
2684
3186
|
const controller = new AbortController();
|
|
2685
3187
|
const now = Date.now();
|
|
3188
|
+
const node = this.graph.getNode(nodeId);
|
|
2686
3189
|
this.graph.updateNodeStats(nodeId, {
|
|
2687
|
-
runs: node
|
|
2688
|
-
active: node
|
|
3190
|
+
runs: (node?.stats.runs ?? 0) + 1,
|
|
3191
|
+
active: (node?.stats.active ?? 0) + 1,
|
|
2689
3192
|
lastStartAt: now,
|
|
2690
3193
|
progress: 0,
|
|
2691
3194
|
});
|
|
@@ -2695,7 +3198,7 @@ class NodeExecutor {
|
|
|
2695
3198
|
/**
|
|
2696
3199
|
* Apply concurrency mode (switch mode aborts other controllers)
|
|
2697
3200
|
*/
|
|
2698
|
-
applyConcurrencyMode(nodeId,
|
|
3201
|
+
applyConcurrencyMode(nodeId, controller, plan) {
|
|
2699
3202
|
const mode = plan.policy?.asyncConcurrency ?? "switch";
|
|
2700
3203
|
if (mode === "switch") {
|
|
2701
3204
|
const controllers = this.graph.getNodeControllers(nodeId);
|
|
@@ -2708,7 +3211,7 @@ class NodeExecutor {
|
|
|
2708
3211
|
/**
|
|
2709
3212
|
* Setup timeout for execution if configured
|
|
2710
3213
|
*/
|
|
2711
|
-
setupTimeout(
|
|
3214
|
+
setupTimeout(controller, plan) {
|
|
2712
3215
|
const policy = plan.policy ?? {};
|
|
2713
3216
|
if (policy.timeoutMs && policy.timeoutMs > 0) {
|
|
2714
3217
|
return setTimeout(() => controller.abort("timeout"), policy.timeoutMs);
|
|
@@ -2718,7 +3221,7 @@ class NodeExecutor {
|
|
|
2718
3221
|
/**
|
|
2719
3222
|
* Create emit and progress handlers for execution context
|
|
2720
3223
|
*/
|
|
2721
|
-
createEmitAndProgressHandlers(
|
|
3224
|
+
createEmitAndProgressHandlers(nodeId, plan) {
|
|
2722
3225
|
const policy = plan.policy ?? {};
|
|
2723
3226
|
return {
|
|
2724
3227
|
emitHandler: (handle, value) => {
|
|
@@ -2753,7 +3256,10 @@ class NodeExecutor {
|
|
|
2753
3256
|
/**
|
|
2754
3257
|
* Execute the node with retry logic and cleanup
|
|
2755
3258
|
*/
|
|
2756
|
-
executeNode(
|
|
3259
|
+
executeNode(nodeId, ctx, plan, controller, timeoutId, onDone) {
|
|
3260
|
+
const node = this.graph.getNode(nodeId);
|
|
3261
|
+
if (!node)
|
|
3262
|
+
return;
|
|
2757
3263
|
// Fire node-start event
|
|
2758
3264
|
this.eventEmitter.emit("stats", {
|
|
2759
3265
|
kind: "node-start",
|
|
@@ -2761,7 +3267,11 @@ class NodeExecutor {
|
|
|
2761
3267
|
typeId: node.typeId,
|
|
2762
3268
|
runId: plan.runId,
|
|
2763
3269
|
});
|
|
2764
|
-
ctx.log("debug", "node-start"
|
|
3270
|
+
ctx.log("debug", "node-start", {
|
|
3271
|
+
inputs: node.inputs,
|
|
3272
|
+
effectiveInputs: plan.effectiveInputs,
|
|
3273
|
+
reason: plan.reason,
|
|
3274
|
+
});
|
|
2765
3275
|
const exec = async (attempt) => {
|
|
2766
3276
|
let hadError = false;
|
|
2767
3277
|
try {
|
|
@@ -2799,7 +3309,7 @@ class NodeExecutor {
|
|
|
2799
3309
|
});
|
|
2800
3310
|
}
|
|
2801
3311
|
finally {
|
|
2802
|
-
this.cleanupExecution(
|
|
3312
|
+
this.cleanupExecution(nodeId, ctx, plan, controller, timeoutId, hadError, onDone);
|
|
2803
3313
|
}
|
|
2804
3314
|
};
|
|
2805
3315
|
exec(0);
|
|
@@ -2807,7 +3317,7 @@ class NodeExecutor {
|
|
|
2807
3317
|
/**
|
|
2808
3318
|
* Cleanup after execution completes
|
|
2809
3319
|
*/
|
|
2810
|
-
cleanupExecution(
|
|
3320
|
+
cleanupExecution(nodeId, ctx, plan, controller, timeoutId, hadError, onDone) {
|
|
2811
3321
|
// Decrement pendingNodes count for all relevant run-contexts
|
|
2812
3322
|
if (plan.runContextIdsForRun && plan.runContextIdsForRun.size > 0) {
|
|
2813
3323
|
for (const id of plan.runContextIdsForRun) {
|
|
@@ -2821,19 +3331,19 @@ class NodeExecutor {
|
|
|
2821
3331
|
if (timeoutId)
|
|
2822
3332
|
clearTimeout(timeoutId);
|
|
2823
3333
|
this.graph.removeNodeController(nodeId, controller);
|
|
2824
|
-
const
|
|
2825
|
-
if (!
|
|
3334
|
+
const node = this.graph.getNode(nodeId);
|
|
3335
|
+
if (!node)
|
|
2826
3336
|
return;
|
|
2827
3337
|
const controllers = this.graph.getNodeControllers(nodeId);
|
|
2828
3338
|
const lastEndAt = Date.now();
|
|
2829
|
-
const lastDurationMs =
|
|
2830
|
-
? lastEndAt -
|
|
3339
|
+
const lastDurationMs = node.stats.lastStartAt && lastEndAt
|
|
3340
|
+
? lastEndAt - node.stats.lastStartAt
|
|
2831
3341
|
: undefined;
|
|
2832
3342
|
this.graph.updateNodeStats(nodeId, {
|
|
2833
3343
|
active: Math.max(0, controllers.size),
|
|
2834
3344
|
lastEndAt,
|
|
2835
3345
|
lastDurationMs,
|
|
2836
|
-
lastError: hadError ?
|
|
3346
|
+
lastError: hadError ? node.stats.lastError : undefined,
|
|
2837
3347
|
});
|
|
2838
3348
|
// Track successful completion time (for detecting stale inputs)
|
|
2839
3349
|
const isCancelled = controller.signal.aborted &&
|
|
@@ -2845,26 +3355,22 @@ class NodeExecutor {
|
|
|
2845
3355
|
}
|
|
2846
3356
|
// Only emit node-done if not cancelled (cancellation events emitted separately)
|
|
2847
3357
|
if (!isCancelled) {
|
|
2848
|
-
if (
|
|
3358
|
+
if (node) {
|
|
2849
3359
|
this.eventEmitter.emit("stats", {
|
|
2850
3360
|
kind: "node-done",
|
|
2851
3361
|
nodeId,
|
|
2852
|
-
typeId:
|
|
3362
|
+
typeId: node.typeId,
|
|
2853
3363
|
runId: plan.runId,
|
|
2854
|
-
durationMs:
|
|
3364
|
+
durationMs: node.stats.lastDurationMs,
|
|
2855
3365
|
});
|
|
2856
3366
|
}
|
|
2857
3367
|
}
|
|
2858
|
-
if (
|
|
3368
|
+
if (node) {
|
|
2859
3369
|
ctx.log("debug", "node-done", {
|
|
2860
|
-
durationMs:
|
|
3370
|
+
durationMs: node.stats.lastDurationMs,
|
|
3371
|
+
outputs: node.outputs,
|
|
2861
3372
|
hadError,
|
|
2862
|
-
|
|
2863
|
-
? structuredClone(currentNode.inputs)
|
|
2864
|
-
: undefined,
|
|
2865
|
-
outputs: currentNode.outputs
|
|
2866
|
-
? structuredClone(currentNode.outputs)
|
|
2867
|
-
: undefined,
|
|
3373
|
+
reason: plan.reason,
|
|
2868
3374
|
});
|
|
2869
3375
|
}
|
|
2870
3376
|
if (onDone)
|
|
@@ -2873,14 +3379,13 @@ class NodeExecutor {
|
|
|
2873
3379
|
/**
|
|
2874
3380
|
* Cancel all active runs for a node
|
|
2875
3381
|
*/
|
|
2876
|
-
cancelNodeActiveRuns(
|
|
2877
|
-
const
|
|
3382
|
+
cancelNodeActiveRuns(nodeId, reason) {
|
|
3383
|
+
const node = this.graph.getNode(nodeId);
|
|
3384
|
+
if (!node)
|
|
3385
|
+
return;
|
|
2878
3386
|
const controllers = this.graph.getNodeControllers(nodeId);
|
|
2879
3387
|
for (const controller of controllers) {
|
|
2880
|
-
const
|
|
2881
|
-
if (!currentNode)
|
|
2882
|
-
continue;
|
|
2883
|
-
const runId = currentNode.controllerRunIds.get(controller);
|
|
3388
|
+
const runId = node.controllerRunIds.get(controller);
|
|
2884
3389
|
if (runId) {
|
|
2885
3390
|
// Track cancelled runIds for snapshot and user-cancelled operations
|
|
2886
3391
|
// (to drop emits from cancelled runs)
|
|
@@ -2890,8 +3395,8 @@ class NodeExecutor {
|
|
|
2890
3395
|
// Emit cancellation event
|
|
2891
3396
|
this.eventEmitter.emit("stats", {
|
|
2892
3397
|
kind: "node-done",
|
|
2893
|
-
nodeId:
|
|
2894
|
-
typeId:
|
|
3398
|
+
nodeId: nodeId,
|
|
3399
|
+
typeId: node.typeId,
|
|
2895
3400
|
runId,
|
|
2896
3401
|
cancelled: true,
|
|
2897
3402
|
});
|
|
@@ -2939,10 +3444,7 @@ class NodeExecutor {
|
|
|
2939
3444
|
}
|
|
2940
3445
|
// Cancel runs for all affected nodes
|
|
2941
3446
|
for (const nodeId of toCancel) {
|
|
2942
|
-
|
|
2943
|
-
if (!node)
|
|
2944
|
-
continue;
|
|
2945
|
-
this.cancelNodeActiveRuns(node, reason);
|
|
3447
|
+
this.cancelNodeActiveRuns(nodeId, reason);
|
|
2946
3448
|
const runSeq = this.graph.incrementNodeRunSeq(nodeId);
|
|
2947
3449
|
const now = Date.now();
|
|
2948
3450
|
const suffix = reason === "snapshot" ? "snapshot" : "cancelled";
|
|
@@ -3182,7 +3684,7 @@ class GraphRuntime {
|
|
|
3182
3684
|
// However, if autoRun policy is set, nodes run automatically even in manual mode.
|
|
3183
3685
|
if (anyChanged) {
|
|
3184
3686
|
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
3185
|
-
this.executeNodeAutoRun(nodeId);
|
|
3687
|
+
this.executeNodeAutoRun(nodeId, { reason: "setInputs" });
|
|
3186
3688
|
}
|
|
3187
3689
|
}
|
|
3188
3690
|
getOutput(nodeId, output) {
|
|
@@ -3193,7 +3695,7 @@ class GraphRuntime {
|
|
|
3193
3695
|
this.graph.forEachNode((node) => {
|
|
3194
3696
|
const effectiveInputs = this.nodeExecutor.getEffectiveInputs(node.nodeId);
|
|
3195
3697
|
const ctrl = new AbortController();
|
|
3196
|
-
const execCtx = this.nodeExecutor.createExecutionContext(node.nodeId,
|
|
3698
|
+
const execCtx = this.nodeExecutor.createExecutionContext(node.nodeId, effectiveInputs, `${node.nodeId}:init`, ctrl.signal);
|
|
3197
3699
|
if (node.lifecycle?.prepare) {
|
|
3198
3700
|
execCtx.log("debug", "prepare-start");
|
|
3199
3701
|
node.lifecycle.prepare(node.params ?? {}, execCtx);
|
|
@@ -3204,7 +3706,7 @@ class GraphRuntime {
|
|
|
3204
3706
|
if (this.runMode === "auto" && invalidate) {
|
|
3205
3707
|
for (const nodeId of this.graph.getNodeIds()) {
|
|
3206
3708
|
if (this.graph.allInboundHaveValue(nodeId))
|
|
3207
|
-
this.execute(nodeId);
|
|
3709
|
+
this.execute(nodeId, { reason: "launch" });
|
|
3208
3710
|
}
|
|
3209
3711
|
}
|
|
3210
3712
|
if (startPaused) {
|
|
@@ -3219,7 +3721,7 @@ class GraphRuntime {
|
|
|
3219
3721
|
if (this.isInvalidateEvent(event)) {
|
|
3220
3722
|
// Check if node has all inbound inputs (required for execution)
|
|
3221
3723
|
if (this.graph.allInboundHaveValue(nodeId)) {
|
|
3222
|
-
this.execute(nodeId);
|
|
3724
|
+
this.execute(nodeId, { reason: "triggerExternal" });
|
|
3223
3725
|
}
|
|
3224
3726
|
return;
|
|
3225
3727
|
}
|
|
@@ -3343,17 +3845,20 @@ class GraphRuntime {
|
|
|
3343
3845
|
setTimeout(check, 10);
|
|
3344
3846
|
});
|
|
3345
3847
|
}
|
|
3346
|
-
async runFromHereContext(startNodeId,
|
|
3848
|
+
async runFromHereContext(startNodeId, opts) {
|
|
3347
3849
|
const node = this.graph.getNode(startNodeId);
|
|
3348
3850
|
if (!node)
|
|
3349
3851
|
return;
|
|
3350
3852
|
return new Promise((resolve) => {
|
|
3351
3853
|
const id = this.runContextManager.createRunContext(startNodeId, {
|
|
3352
3854
|
resolve,
|
|
3353
|
-
...
|
|
3855
|
+
...opts,
|
|
3354
3856
|
});
|
|
3355
3857
|
this.graph.addNodeRunContextId(startNodeId, id);
|
|
3356
|
-
this.execute(startNodeId,
|
|
3858
|
+
this.execute(startNodeId, {
|
|
3859
|
+
runContextIds: new Set([id]),
|
|
3860
|
+
reason: opts?.reason ?? "runFromHereContext",
|
|
3861
|
+
});
|
|
3357
3862
|
});
|
|
3358
3863
|
}
|
|
3359
3864
|
setRunMode(runMode) {
|
|
@@ -3400,7 +3905,7 @@ class GraphRuntime {
|
|
|
3400
3905
|
}
|
|
3401
3906
|
}
|
|
3402
3907
|
}
|
|
3403
|
-
executeNodeAutoRun(nodeId) {
|
|
3908
|
+
executeNodeAutoRun(nodeId, opts) {
|
|
3404
3909
|
const node = this.graph.getNode(nodeId);
|
|
3405
3910
|
const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
|
|
3406
3911
|
let runContextIdsToUse = undefined;
|
|
@@ -3410,7 +3915,10 @@ class GraphRuntime {
|
|
|
3410
3915
|
]);
|
|
3411
3916
|
}
|
|
3412
3917
|
if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
|
|
3413
|
-
this.execute(nodeId,
|
|
3918
|
+
this.execute(nodeId, {
|
|
3919
|
+
runContextIds: runContextIdsToUse,
|
|
3920
|
+
reason: opts?.reason ?? "executeNodeAutoRun",
|
|
3921
|
+
});
|
|
3414
3922
|
}
|
|
3415
3923
|
}
|
|
3416
3924
|
copyOutputs(fromNodeId, toNodeId, options) {
|
|
@@ -3419,7 +3927,7 @@ class GraphRuntime {
|
|
|
3419
3927
|
return;
|
|
3420
3928
|
this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { invalidate: !options?.dry });
|
|
3421
3929
|
this.handleResolver.scheduleRecomputeHandles(toNodeId);
|
|
3422
|
-
this.executeNodeAutoRun(toNodeId);
|
|
3930
|
+
this.executeNodeAutoRun(toNodeId, { reason: "copyOutputs" });
|
|
3423
3931
|
}
|
|
3424
3932
|
hydrate(payload, opts) {
|
|
3425
3933
|
const releasePause = this.requestPause();
|
|
@@ -3477,7 +3985,7 @@ class GraphRuntime {
|
|
|
3477
3985
|
const node = this.graph.getNode(nodeId);
|
|
3478
3986
|
if (!node)
|
|
3479
3987
|
continue;
|
|
3480
|
-
this.nodeExecutor.cancelNodeActiveRuns(
|
|
3988
|
+
this.nodeExecutor.cancelNodeActiveRuns(nodeId, "node-deleted");
|
|
3481
3989
|
this.runContextManager.cancelNodeInRunContexts(nodeId, true);
|
|
3482
3990
|
node.runtime.onDeactivated?.();
|
|
3483
3991
|
node.runtime.dispose?.();
|
|
@@ -3538,7 +4046,7 @@ class GraphRuntime {
|
|
|
3538
4046
|
this.graph.setNode(n.nodeId, newNode);
|
|
3539
4047
|
const effectiveInputs = this.nodeExecutor.getEffectiveInputs(newNode.nodeId);
|
|
3540
4048
|
const ctrl = new AbortController();
|
|
3541
|
-
const execCtx = this.nodeExecutor.createExecutionContext(newNode.nodeId,
|
|
4049
|
+
const execCtx = this.nodeExecutor.createExecutionContext(newNode.nodeId, effectiveInputs, `${newNode.nodeId}:init`, ctrl.signal);
|
|
3542
4050
|
if (newNode.lifecycle?.prepare) {
|
|
3543
4051
|
execCtx.log("debug", "prepare-start");
|
|
3544
4052
|
newNode.lifecycle.prepare(newNode.params ?? {}, execCtx);
|
|
@@ -3649,7 +4157,7 @@ class GraphRuntime {
|
|
|
3649
4157
|
this.edgePropagator.clearArrayBuckets(nodeId);
|
|
3650
4158
|
// Trigger handle resolution when inputs are removed
|
|
3651
4159
|
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
3652
|
-
this.executeNodeAutoRun(nodeId);
|
|
4160
|
+
this.executeNodeAutoRun(nodeId, { reason: "graphUpdate" });
|
|
3653
4161
|
}
|
|
3654
4162
|
}
|
|
3655
4163
|
// Propagate changes on edges added
|
|
@@ -3719,8 +4227,8 @@ class GraphRuntime {
|
|
|
3719
4227
|
});
|
|
3720
4228
|
this.graph.clear();
|
|
3721
4229
|
}
|
|
3722
|
-
execute(nodeId,
|
|
3723
|
-
this.nodeExecutor.execute(nodeId,
|
|
4230
|
+
execute(nodeId, opts) {
|
|
4231
|
+
this.nodeExecutor.execute(nodeId, opts);
|
|
3724
4232
|
}
|
|
3725
4233
|
propagate(srcNodeId, srcHandle, value, runContextIds) {
|
|
3726
4234
|
this.edgePropagator.propagate(srcNodeId, srcHandle, value, runContextIds);
|
|
@@ -4083,6 +4591,7 @@ class LocalEngine {
|
|
|
4083
4591
|
await this.graphRuntime.runFromHereContext(nodeId, {
|
|
4084
4592
|
skipPropagateValues: options?.skipPropagateValues ?? false,
|
|
4085
4593
|
propagate: false, // Don't schedule downstream nodes
|
|
4594
|
+
reason: "computeNode",
|
|
4086
4595
|
});
|
|
4087
4596
|
}
|
|
4088
4597
|
/**
|
|
@@ -4091,7 +4600,9 @@ class LocalEngine {
|
|
|
4091
4600
|
* Uses run-context system for dynamic graph updates.
|
|
4092
4601
|
*/
|
|
4093
4602
|
async runFromHere(nodeId) {
|
|
4094
|
-
await this.graphRuntime.runFromHereContext(nodeId
|
|
4603
|
+
await this.graphRuntime.runFromHereContext(nodeId, {
|
|
4604
|
+
reason: "runFromHere",
|
|
4605
|
+
});
|
|
4095
4606
|
}
|
|
4096
4607
|
setRunMode(runMode) {
|
|
4097
4608
|
this.graphRuntime.setRunMode(runMode);
|
|
@@ -5529,235 +6040,6 @@ function createValidationGraphRegistry(id) {
|
|
|
5529
6040
|
return registry;
|
|
5530
6041
|
}
|
|
5531
6042
|
|
|
5532
|
-
function parseJsonPath(path) {
|
|
5533
|
-
if (typeof path === "string") {
|
|
5534
|
-
return path.split(".").flatMap((segment) => {
|
|
5535
|
-
const arrayMatch = segment.match(/^(.+)\[(\d+)\]$/);
|
|
5536
|
-
if (arrayMatch) {
|
|
5537
|
-
const index = parseInt(arrayMatch[2], 10);
|
|
5538
|
-
return arrayMatch[1] ? [arrayMatch[1], index] : [index];
|
|
5539
|
-
}
|
|
5540
|
-
return [segment];
|
|
5541
|
-
});
|
|
5542
|
-
}
|
|
5543
|
-
return path;
|
|
5544
|
-
}
|
|
5545
|
-
function getValueAtPath(obj, pathSegments) {
|
|
5546
|
-
if (pathSegments.length === 0) {
|
|
5547
|
-
return { value: obj, parent: null, key: "" };
|
|
5548
|
-
}
|
|
5549
|
-
let current = obj;
|
|
5550
|
-
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
5551
|
-
const segment = pathSegments[i];
|
|
5552
|
-
if (current === null ||
|
|
5553
|
-
current === undefined ||
|
|
5554
|
-
typeof current !== "object") {
|
|
5555
|
-
return null;
|
|
5556
|
-
}
|
|
5557
|
-
if (typeof segment === "string") {
|
|
5558
|
-
if (Array.isArray(current)) {
|
|
5559
|
-
const index = parseInt(segment, 10);
|
|
5560
|
-
if (isNaN(index))
|
|
5561
|
-
return null;
|
|
5562
|
-
current = current[index];
|
|
5563
|
-
}
|
|
5564
|
-
else {
|
|
5565
|
-
current = current[segment];
|
|
5566
|
-
}
|
|
5567
|
-
}
|
|
5568
|
-
else if (typeof segment === "number") {
|
|
5569
|
-
if (Array.isArray(current)) {
|
|
5570
|
-
if (segment >= 0 && segment < current.length) {
|
|
5571
|
-
current = current[segment];
|
|
5572
|
-
}
|
|
5573
|
-
else {
|
|
5574
|
-
return null;
|
|
5575
|
-
}
|
|
5576
|
-
}
|
|
5577
|
-
else {
|
|
5578
|
-
return null;
|
|
5579
|
-
}
|
|
5580
|
-
}
|
|
5581
|
-
else if (segment instanceof RegExp) {
|
|
5582
|
-
if (Array.isArray(current)) {
|
|
5583
|
-
return null;
|
|
5584
|
-
}
|
|
5585
|
-
const obj = current;
|
|
5586
|
-
const matchingKey = Object.keys(obj).find((key) => segment.test(key));
|
|
5587
|
-
if (!matchingKey)
|
|
5588
|
-
return null;
|
|
5589
|
-
current = obj[matchingKey];
|
|
5590
|
-
}
|
|
5591
|
-
else {
|
|
5592
|
-
return null;
|
|
5593
|
-
}
|
|
5594
|
-
}
|
|
5595
|
-
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
5596
|
-
if (typeof lastSegment === "string") {
|
|
5597
|
-
if (Array.isArray(current)) {
|
|
5598
|
-
const index = parseInt(lastSegment, 10);
|
|
5599
|
-
if (isNaN(index))
|
|
5600
|
-
return null;
|
|
5601
|
-
return { value: current[index], parent: current, key: index };
|
|
5602
|
-
}
|
|
5603
|
-
else if (current !== null &&
|
|
5604
|
-
current !== undefined &&
|
|
5605
|
-
typeof current === "object") {
|
|
5606
|
-
return {
|
|
5607
|
-
value: current[lastSegment],
|
|
5608
|
-
parent: current,
|
|
5609
|
-
key: lastSegment,
|
|
5610
|
-
};
|
|
5611
|
-
}
|
|
5612
|
-
}
|
|
5613
|
-
else if (typeof lastSegment === "number") {
|
|
5614
|
-
if (Array.isArray(current)) {
|
|
5615
|
-
if (lastSegment >= 0 && lastSegment < current.length) {
|
|
5616
|
-
return {
|
|
5617
|
-
value: current[lastSegment],
|
|
5618
|
-
parent: current,
|
|
5619
|
-
key: lastSegment,
|
|
5620
|
-
};
|
|
5621
|
-
}
|
|
5622
|
-
}
|
|
5623
|
-
return null;
|
|
5624
|
-
}
|
|
5625
|
-
else if (lastSegment instanceof RegExp) {
|
|
5626
|
-
if (Array.isArray(current)) {
|
|
5627
|
-
return null;
|
|
5628
|
-
}
|
|
5629
|
-
const obj = current;
|
|
5630
|
-
const matchingKey = Object.keys(obj).find((key) => lastSegment.test(key));
|
|
5631
|
-
if (!matchingKey)
|
|
5632
|
-
return null;
|
|
5633
|
-
return { value: obj[matchingKey], parent: current, key: matchingKey };
|
|
5634
|
-
}
|
|
5635
|
-
return null;
|
|
5636
|
-
}
|
|
5637
|
-
function setValueAtPath(obj, pathSegments, newValue) {
|
|
5638
|
-
const result = getValueAtPath(obj, pathSegments);
|
|
5639
|
-
if (!result || result.parent === null)
|
|
5640
|
-
return false;
|
|
5641
|
-
if (Array.isArray(result.parent)) {
|
|
5642
|
-
result.parent[result.key] = newValue;
|
|
5643
|
-
}
|
|
5644
|
-
else {
|
|
5645
|
-
result.parent[result.key] = newValue;
|
|
5646
|
-
}
|
|
5647
|
-
return true;
|
|
5648
|
-
}
|
|
5649
|
-
/**
|
|
5650
|
-
* Sets a value at a path, creating intermediate objects as needed.
|
|
5651
|
-
* Mutates the root object in place.
|
|
5652
|
-
* @param root - The root object to modify (must be an object, will be initialized if needed)
|
|
5653
|
-
* @param pathSegments - The path segments to traverse
|
|
5654
|
-
* @param value - The value to set, or null to delete the path
|
|
5655
|
-
* @throws Error if path cannot be created (e.g., array indices not supported, invalid parent types)
|
|
5656
|
-
*/
|
|
5657
|
-
function setValueAtPathWithCreation(root, pathSegments, value) {
|
|
5658
|
-
if (value === null) {
|
|
5659
|
-
const result = getValueAtPath(root, pathSegments);
|
|
5660
|
-
if (result && result.parent !== null && !Array.isArray(result.parent)) {
|
|
5661
|
-
delete result.parent[result.key];
|
|
5662
|
-
}
|
|
5663
|
-
return;
|
|
5664
|
-
}
|
|
5665
|
-
if (!root || typeof root !== "object" || Array.isArray(root)) {
|
|
5666
|
-
throw new Error("Root must be an object");
|
|
5667
|
-
}
|
|
5668
|
-
let current = root;
|
|
5669
|
-
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
5670
|
-
const segment = pathSegments[i];
|
|
5671
|
-
if (typeof segment === "string") {
|
|
5672
|
-
if (!current ||
|
|
5673
|
-
typeof current !== "object" ||
|
|
5674
|
-
Array.isArray(current) ||
|
|
5675
|
-
!(segment in current) ||
|
|
5676
|
-
typeof current[segment] !== "object" ||
|
|
5677
|
-
current[segment] === null ||
|
|
5678
|
-
Array.isArray(current[segment])) {
|
|
5679
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
5680
|
-
throw new Error(`Cannot create path: parent at segment ${i} is not an object`);
|
|
5681
|
-
}
|
|
5682
|
-
current[segment] = {};
|
|
5683
|
-
}
|
|
5684
|
-
current = current[segment];
|
|
5685
|
-
}
|
|
5686
|
-
else {
|
|
5687
|
-
throw new Error("Array indices not supported in extData paths");
|
|
5688
|
-
}
|
|
5689
|
-
}
|
|
5690
|
-
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
5691
|
-
if (typeof lastSegment === "string") {
|
|
5692
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
5693
|
-
throw new Error(`Cannot set value: parent at final segment is not an object`);
|
|
5694
|
-
}
|
|
5695
|
-
current[lastSegment] = value;
|
|
5696
|
-
}
|
|
5697
|
-
else {
|
|
5698
|
-
throw new Error("Array indices not supported in extData paths");
|
|
5699
|
-
}
|
|
5700
|
-
}
|
|
5701
|
-
function findMatchingPaths(obj, pathSegments, currentPath = []) {
|
|
5702
|
-
if (pathSegments.length === 0) {
|
|
5703
|
-
return [{ path: currentPath, value: obj }];
|
|
5704
|
-
}
|
|
5705
|
-
const [currentSegment, ...remainingSegments] = pathSegments;
|
|
5706
|
-
const results = [];
|
|
5707
|
-
if (currentSegment === undefined) {
|
|
5708
|
-
return results;
|
|
5709
|
-
}
|
|
5710
|
-
if (typeof currentSegment === "string") {
|
|
5711
|
-
if (Array.isArray(obj)) {
|
|
5712
|
-
const index = parseInt(currentSegment, 10);
|
|
5713
|
-
if (!isNaN(index) && index >= 0 && index < obj.length) {
|
|
5714
|
-
results.push(...findMatchingPaths(obj[index], remainingSegments, [
|
|
5715
|
-
...currentPath,
|
|
5716
|
-
index,
|
|
5717
|
-
]));
|
|
5718
|
-
}
|
|
5719
|
-
}
|
|
5720
|
-
else if (obj !== null && obj !== undefined && typeof obj === "object") {
|
|
5721
|
-
const objRecord = obj;
|
|
5722
|
-
if (currentSegment in objRecord) {
|
|
5723
|
-
results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [
|
|
5724
|
-
...currentPath,
|
|
5725
|
-
currentSegment,
|
|
5726
|
-
]));
|
|
5727
|
-
}
|
|
5728
|
-
}
|
|
5729
|
-
}
|
|
5730
|
-
else if (typeof currentSegment === "number") {
|
|
5731
|
-
if (Array.isArray(obj)) {
|
|
5732
|
-
if (currentSegment >= 0 && currentSegment < obj.length) {
|
|
5733
|
-
results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [
|
|
5734
|
-
...currentPath,
|
|
5735
|
-
currentSegment,
|
|
5736
|
-
]));
|
|
5737
|
-
}
|
|
5738
|
-
}
|
|
5739
|
-
}
|
|
5740
|
-
else if (currentSegment instanceof RegExp) {
|
|
5741
|
-
if (Array.isArray(obj)) {
|
|
5742
|
-
for (let i = 0; i < obj.length; i++) {
|
|
5743
|
-
results.push(...findMatchingPaths(obj[i], remainingSegments, [...currentPath, i]));
|
|
5744
|
-
}
|
|
5745
|
-
}
|
|
5746
|
-
else if (obj !== null && obj !== undefined && typeof obj === "object") {
|
|
5747
|
-
const objRecord = obj;
|
|
5748
|
-
for (const key of Object.keys(objRecord)) {
|
|
5749
|
-
if (currentSegment.test(key)) {
|
|
5750
|
-
results.push(...findMatchingPaths(objRecord[key], remainingSegments, [
|
|
5751
|
-
...currentPath,
|
|
5752
|
-
key,
|
|
5753
|
-
]));
|
|
5754
|
-
}
|
|
5755
|
-
}
|
|
5756
|
-
}
|
|
5757
|
-
}
|
|
5758
|
-
return results;
|
|
5759
|
-
}
|
|
5760
|
-
|
|
5761
6043
|
function mergeGraphDefinitions(target, source) {
|
|
5762
6044
|
const existingNodeIds = new Set(target.nodes.map((n) => n.nodeId));
|
|
5763
6045
|
const existingEdgeIds = new Set(target.edges.map((e) => e.id));
|
|
@@ -6355,5 +6637,5 @@ function buildValueConverter(config) {
|
|
|
6355
6637
|
};
|
|
6356
6638
|
}
|
|
6357
6639
|
|
|
6358
|
-
export { BaseCompareOperation, BaseLogicOperation, BaseMathOperation, CompositeCategory, ComputeCategory, Graph, GraphBuilder, GraphRuntime, LevelLogger, LocalEngine, Registry, buildValueConverter, computeGraphCenter, convertSnapshot, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createSimpleGraphDef, createSimpleGraphRegistry, createValidationGraphDef, createValidationGraphRegistry, findMatchingPaths, generateId, getEffectiveInputs, getInputDeclaredTypes, getInputHandleMetadata, getInputTypeId, getValueAtPath, installLogging, isInputPrivate, isTyped, mergeInputHandleDescriptors, mergeRuntimeState, mergeSnapshots, offsetImportedPositions, parseJsonPath, registerDelayNode, registerProgressNodes, setValueAtPath, setValueAtPathWithCreation, typed, unwrapTypeId, unwrapValue };
|
|
6640
|
+
export { BaseCompareOperation, BaseLogicOperation, BaseMathOperation, CompositeCategory, ComputeCategory, Graph, GraphBuilder, GraphRuntime, LevelLogger, LocalEngine, Registry, buildValueConverter, computeGraphCenter, convertSnapshot, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createSimpleGraphDef, createSimpleGraphRegistry, createValidationGraphDef, createValidationGraphRegistry, findMatchingPaths, generateId, getEffectiveInputs, getInputDeclaredTypes, getInputHandleMetadata, getInputTypeId, getValueAtPath, installLogging, isInputPrivate, isTyped, mergeInputHandleDescriptors, mergeRuntimeState, mergeSnapshots, offsetImportedPositions, parseJsonPath, registerDelayNode, registerProgressNodes, setValueAtPath, setValueAtPathWithCreation, stringifyJson, stringifySceneAndOps, typed, unwrapTypeId, unwrapValue };
|
|
6359
6641
|
//# sourceMappingURL=index.js.map
|