@bian-womp/spark-graph 0.3.51 → 0.3.53
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 +537 -275
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/index.d.ts +3 -3
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/utils/LevelLogger.d.ts +52 -0
- 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/components/EdgePropagator.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/NodeExecutor.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 +536 -276
- package/lib/esm/index.js.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/components/EdgePropagator.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/NodeExecutor.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/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, `${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}}`;
|
|
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;
|
|
1030
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);
|
|
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);
|
|
1033
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
|
: "";
|
|
@@ -1413,29 +1868,63 @@ class RunContextManager {
|
|
|
1413
1868
|
for (const id of toCancel) {
|
|
1414
1869
|
this.graph.clearNodeRunContextIds(id);
|
|
1415
1870
|
}
|
|
1416
|
-
}
|
|
1417
|
-
/**
|
|
1418
|
-
* Resolve all pending run-context promises (for cleanup)
|
|
1419
|
-
*/
|
|
1420
|
-
resolveAll() {
|
|
1421
|
-
const count = this.runContexts.size;
|
|
1422
|
-
this.logger.info("resolve-all-run-contexts", {
|
|
1423
|
-
count,
|
|
1424
|
-
runContextIds: Array.from(this.runContexts.keys()),
|
|
1425
|
-
});
|
|
1426
|
-
for (const ctx of this.runContexts.values()) {
|
|
1427
|
-
if (ctx.resolve)
|
|
1428
|
-
ctx.resolve();
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Resolve all pending run-context promises (for cleanup)
|
|
1874
|
+
*/
|
|
1875
|
+
resolveAll() {
|
|
1876
|
+
const count = this.runContexts.size;
|
|
1877
|
+
this.logger.info("resolve-all-run-contexts", {
|
|
1878
|
+
count,
|
|
1879
|
+
runContextIds: Array.from(this.runContexts.keys()),
|
|
1880
|
+
});
|
|
1881
|
+
for (const ctx of this.runContexts.values()) {
|
|
1882
|
+
if (ctx.resolve)
|
|
1883
|
+
ctx.resolve();
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* Clear all run-contexts
|
|
1888
|
+
*/
|
|
1889
|
+
clear() {
|
|
1890
|
+
const count = this.runContexts.size;
|
|
1891
|
+
this.logger.info("clear-all-run-contexts", { count });
|
|
1892
|
+
this.runContexts.clear();
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
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;
|
|
1429
1925
|
}
|
|
1430
1926
|
}
|
|
1431
|
-
|
|
1432
|
-
* Clear all run-contexts
|
|
1433
|
-
*/
|
|
1434
|
-
clear() {
|
|
1435
|
-
const count = this.runContexts.size;
|
|
1436
|
-
this.logger.info("clear-all-run-contexts", { count });
|
|
1437
|
-
this.runContexts.clear();
|
|
1438
|
-
}
|
|
1927
|
+
return false;
|
|
1439
1928
|
}
|
|
1440
1929
|
|
|
1441
1930
|
function tryHandleResolving(def, registry, environment) {
|
|
@@ -5553,235 +6042,6 @@ function createValidationGraphRegistry(id) {
|
|
|
5553
6042
|
return registry;
|
|
5554
6043
|
}
|
|
5555
6044
|
|
|
5556
|
-
function parseJsonPath(path) {
|
|
5557
|
-
if (typeof path === "string") {
|
|
5558
|
-
return path.split(".").flatMap((segment) => {
|
|
5559
|
-
const arrayMatch = segment.match(/^(.+)\[(\d+)\]$/);
|
|
5560
|
-
if (arrayMatch) {
|
|
5561
|
-
const index = parseInt(arrayMatch[2], 10);
|
|
5562
|
-
return arrayMatch[1] ? [arrayMatch[1], index] : [index];
|
|
5563
|
-
}
|
|
5564
|
-
return [segment];
|
|
5565
|
-
});
|
|
5566
|
-
}
|
|
5567
|
-
return path;
|
|
5568
|
-
}
|
|
5569
|
-
function getValueAtPath(obj, pathSegments) {
|
|
5570
|
-
if (pathSegments.length === 0) {
|
|
5571
|
-
return { value: obj, parent: null, key: "" };
|
|
5572
|
-
}
|
|
5573
|
-
let current = obj;
|
|
5574
|
-
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
5575
|
-
const segment = pathSegments[i];
|
|
5576
|
-
if (current === null ||
|
|
5577
|
-
current === undefined ||
|
|
5578
|
-
typeof current !== "object") {
|
|
5579
|
-
return null;
|
|
5580
|
-
}
|
|
5581
|
-
if (typeof segment === "string") {
|
|
5582
|
-
if (Array.isArray(current)) {
|
|
5583
|
-
const index = parseInt(segment, 10);
|
|
5584
|
-
if (isNaN(index))
|
|
5585
|
-
return null;
|
|
5586
|
-
current = current[index];
|
|
5587
|
-
}
|
|
5588
|
-
else {
|
|
5589
|
-
current = current[segment];
|
|
5590
|
-
}
|
|
5591
|
-
}
|
|
5592
|
-
else if (typeof segment === "number") {
|
|
5593
|
-
if (Array.isArray(current)) {
|
|
5594
|
-
if (segment >= 0 && segment < current.length) {
|
|
5595
|
-
current = current[segment];
|
|
5596
|
-
}
|
|
5597
|
-
else {
|
|
5598
|
-
return null;
|
|
5599
|
-
}
|
|
5600
|
-
}
|
|
5601
|
-
else {
|
|
5602
|
-
return null;
|
|
5603
|
-
}
|
|
5604
|
-
}
|
|
5605
|
-
else if (segment instanceof RegExp) {
|
|
5606
|
-
if (Array.isArray(current)) {
|
|
5607
|
-
return null;
|
|
5608
|
-
}
|
|
5609
|
-
const obj = current;
|
|
5610
|
-
const matchingKey = Object.keys(obj).find((key) => segment.test(key));
|
|
5611
|
-
if (!matchingKey)
|
|
5612
|
-
return null;
|
|
5613
|
-
current = obj[matchingKey];
|
|
5614
|
-
}
|
|
5615
|
-
else {
|
|
5616
|
-
return null;
|
|
5617
|
-
}
|
|
5618
|
-
}
|
|
5619
|
-
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
5620
|
-
if (typeof lastSegment === "string") {
|
|
5621
|
-
if (Array.isArray(current)) {
|
|
5622
|
-
const index = parseInt(lastSegment, 10);
|
|
5623
|
-
if (isNaN(index))
|
|
5624
|
-
return null;
|
|
5625
|
-
return { value: current[index], parent: current, key: index };
|
|
5626
|
-
}
|
|
5627
|
-
else if (current !== null &&
|
|
5628
|
-
current !== undefined &&
|
|
5629
|
-
typeof current === "object") {
|
|
5630
|
-
return {
|
|
5631
|
-
value: current[lastSegment],
|
|
5632
|
-
parent: current,
|
|
5633
|
-
key: lastSegment,
|
|
5634
|
-
};
|
|
5635
|
-
}
|
|
5636
|
-
}
|
|
5637
|
-
else if (typeof lastSegment === "number") {
|
|
5638
|
-
if (Array.isArray(current)) {
|
|
5639
|
-
if (lastSegment >= 0 && lastSegment < current.length) {
|
|
5640
|
-
return {
|
|
5641
|
-
value: current[lastSegment],
|
|
5642
|
-
parent: current,
|
|
5643
|
-
key: lastSegment,
|
|
5644
|
-
};
|
|
5645
|
-
}
|
|
5646
|
-
}
|
|
5647
|
-
return null;
|
|
5648
|
-
}
|
|
5649
|
-
else if (lastSegment instanceof RegExp) {
|
|
5650
|
-
if (Array.isArray(current)) {
|
|
5651
|
-
return null;
|
|
5652
|
-
}
|
|
5653
|
-
const obj = current;
|
|
5654
|
-
const matchingKey = Object.keys(obj).find((key) => lastSegment.test(key));
|
|
5655
|
-
if (!matchingKey)
|
|
5656
|
-
return null;
|
|
5657
|
-
return { value: obj[matchingKey], parent: current, key: matchingKey };
|
|
5658
|
-
}
|
|
5659
|
-
return null;
|
|
5660
|
-
}
|
|
5661
|
-
function setValueAtPath(obj, pathSegments, newValue) {
|
|
5662
|
-
const result = getValueAtPath(obj, pathSegments);
|
|
5663
|
-
if (!result || result.parent === null)
|
|
5664
|
-
return false;
|
|
5665
|
-
if (Array.isArray(result.parent)) {
|
|
5666
|
-
result.parent[result.key] = newValue;
|
|
5667
|
-
}
|
|
5668
|
-
else {
|
|
5669
|
-
result.parent[result.key] = newValue;
|
|
5670
|
-
}
|
|
5671
|
-
return true;
|
|
5672
|
-
}
|
|
5673
|
-
/**
|
|
5674
|
-
* Sets a value at a path, creating intermediate objects as needed.
|
|
5675
|
-
* Mutates the root object in place.
|
|
5676
|
-
* @param root - The root object to modify (must be an object, will be initialized if needed)
|
|
5677
|
-
* @param pathSegments - The path segments to traverse
|
|
5678
|
-
* @param value - The value to set, or null to delete the path
|
|
5679
|
-
* @throws Error if path cannot be created (e.g., array indices not supported, invalid parent types)
|
|
5680
|
-
*/
|
|
5681
|
-
function setValueAtPathWithCreation(root, pathSegments, value) {
|
|
5682
|
-
if (value === null) {
|
|
5683
|
-
const result = getValueAtPath(root, pathSegments);
|
|
5684
|
-
if (result && result.parent !== null && !Array.isArray(result.parent)) {
|
|
5685
|
-
delete result.parent[result.key];
|
|
5686
|
-
}
|
|
5687
|
-
return;
|
|
5688
|
-
}
|
|
5689
|
-
if (!root || typeof root !== "object" || Array.isArray(root)) {
|
|
5690
|
-
throw new Error("Root must be an object");
|
|
5691
|
-
}
|
|
5692
|
-
let current = root;
|
|
5693
|
-
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
5694
|
-
const segment = pathSegments[i];
|
|
5695
|
-
if (typeof segment === "string") {
|
|
5696
|
-
if (!current ||
|
|
5697
|
-
typeof current !== "object" ||
|
|
5698
|
-
Array.isArray(current) ||
|
|
5699
|
-
!(segment in current) ||
|
|
5700
|
-
typeof current[segment] !== "object" ||
|
|
5701
|
-
current[segment] === null ||
|
|
5702
|
-
Array.isArray(current[segment])) {
|
|
5703
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
5704
|
-
throw new Error(`Cannot create path: parent at segment ${i} is not an object`);
|
|
5705
|
-
}
|
|
5706
|
-
current[segment] = {};
|
|
5707
|
-
}
|
|
5708
|
-
current = current[segment];
|
|
5709
|
-
}
|
|
5710
|
-
else {
|
|
5711
|
-
throw new Error("Array indices not supported in extData paths");
|
|
5712
|
-
}
|
|
5713
|
-
}
|
|
5714
|
-
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
5715
|
-
if (typeof lastSegment === "string") {
|
|
5716
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
5717
|
-
throw new Error(`Cannot set value: parent at final segment is not an object`);
|
|
5718
|
-
}
|
|
5719
|
-
current[lastSegment] = value;
|
|
5720
|
-
}
|
|
5721
|
-
else {
|
|
5722
|
-
throw new Error("Array indices not supported in extData paths");
|
|
5723
|
-
}
|
|
5724
|
-
}
|
|
5725
|
-
function findMatchingPaths(obj, pathSegments, currentPath = []) {
|
|
5726
|
-
if (pathSegments.length === 0) {
|
|
5727
|
-
return [{ path: currentPath, value: obj }];
|
|
5728
|
-
}
|
|
5729
|
-
const [currentSegment, ...remainingSegments] = pathSegments;
|
|
5730
|
-
const results = [];
|
|
5731
|
-
if (currentSegment === undefined) {
|
|
5732
|
-
return results;
|
|
5733
|
-
}
|
|
5734
|
-
if (typeof currentSegment === "string") {
|
|
5735
|
-
if (Array.isArray(obj)) {
|
|
5736
|
-
const index = parseInt(currentSegment, 10);
|
|
5737
|
-
if (!isNaN(index) && index >= 0 && index < obj.length) {
|
|
5738
|
-
results.push(...findMatchingPaths(obj[index], remainingSegments, [
|
|
5739
|
-
...currentPath,
|
|
5740
|
-
index,
|
|
5741
|
-
]));
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
5744
|
-
else if (obj !== null && obj !== undefined && typeof obj === "object") {
|
|
5745
|
-
const objRecord = obj;
|
|
5746
|
-
if (currentSegment in objRecord) {
|
|
5747
|
-
results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [
|
|
5748
|
-
...currentPath,
|
|
5749
|
-
currentSegment,
|
|
5750
|
-
]));
|
|
5751
|
-
}
|
|
5752
|
-
}
|
|
5753
|
-
}
|
|
5754
|
-
else if (typeof currentSegment === "number") {
|
|
5755
|
-
if (Array.isArray(obj)) {
|
|
5756
|
-
if (currentSegment >= 0 && currentSegment < obj.length) {
|
|
5757
|
-
results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [
|
|
5758
|
-
...currentPath,
|
|
5759
|
-
currentSegment,
|
|
5760
|
-
]));
|
|
5761
|
-
}
|
|
5762
|
-
}
|
|
5763
|
-
}
|
|
5764
|
-
else if (currentSegment instanceof RegExp) {
|
|
5765
|
-
if (Array.isArray(obj)) {
|
|
5766
|
-
for (let i = 0; i < obj.length; i++) {
|
|
5767
|
-
results.push(...findMatchingPaths(obj[i], remainingSegments, [...currentPath, i]));
|
|
5768
|
-
}
|
|
5769
|
-
}
|
|
5770
|
-
else if (obj !== null && obj !== undefined && typeof obj === "object") {
|
|
5771
|
-
const objRecord = obj;
|
|
5772
|
-
for (const key of Object.keys(objRecord)) {
|
|
5773
|
-
if (currentSegment.test(key)) {
|
|
5774
|
-
results.push(...findMatchingPaths(objRecord[key], remainingSegments, [
|
|
5775
|
-
...currentPath,
|
|
5776
|
-
key,
|
|
5777
|
-
]));
|
|
5778
|
-
}
|
|
5779
|
-
}
|
|
5780
|
-
}
|
|
5781
|
-
}
|
|
5782
|
-
return results;
|
|
5783
|
-
}
|
|
5784
|
-
|
|
5785
6045
|
function mergeGraphDefinitions(target, source) {
|
|
5786
6046
|
const existingNodeIds = new Set(target.nodes.map((n) => n.nodeId));
|
|
5787
6047
|
const existingEdgeIds = new Set(target.edges.map((e) => e.id));
|
|
@@ -6420,6 +6680,8 @@ exports.registerDelayNode = registerDelayNode;
|
|
|
6420
6680
|
exports.registerProgressNodes = registerProgressNodes;
|
|
6421
6681
|
exports.setValueAtPath = setValueAtPath;
|
|
6422
6682
|
exports.setValueAtPathWithCreation = setValueAtPathWithCreation;
|
|
6683
|
+
exports.stringifyJson = stringifyJson;
|
|
6684
|
+
exports.stringifySceneAndOps = stringifySceneAndOps;
|
|
6423
6685
|
exports.typed = typed;
|
|
6424
6686
|
exports.unwrapTypeId = unwrapTypeId;
|
|
6425
6687
|
exports.unwrapValue = unwrapValue;
|