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