@bian-womp/spark-graph 0.3.51 → 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/esm/index.js CHANGED
@@ -996,39 +996,494 @@ const LOG_LEVEL_VALUES = {
996
996
  silent: 4,
997
997
  };
998
998
 
999
- /**
1000
- * Shared utility functions for runtime components
1001
- */
1002
- /**
1003
- * Type guard to check if a value is a Promise
1004
- */
1005
- function isPromise(value) {
1006
- return !!value && typeof value.then === "function";
999
+ function parseJsonPath(path) {
1000
+ if (typeof path === "string") {
1001
+ return path.split(".").flatMap((segment) => {
1002
+ const arrayMatch = segment.match(/^(.+)\[(\d+)\]$/);
1003
+ if (arrayMatch) {
1004
+ const index = parseInt(arrayMatch[2], 10);
1005
+ return arrayMatch[1] ? [arrayMatch[1], index] : [index];
1006
+ }
1007
+ return [segment];
1008
+ });
1009
+ }
1010
+ return path;
1007
1011
  }
1008
- /**
1009
- * Unwrap a value that might be a Promise
1010
- */
1011
- async function unwrapMaybePromise(value) {
1012
- return isPromise(value) ? await value : value;
1012
+ function getValueAtPath(obj, pathSegments) {
1013
+ if (pathSegments.length === 0) {
1014
+ return { value: obj, parent: null, key: "" };
1015
+ }
1016
+ let current = obj;
1017
+ for (let i = 0; i < pathSegments.length - 1; i++) {
1018
+ const segment = pathSegments[i];
1019
+ if (current === null ||
1020
+ current === undefined ||
1021
+ typeof current !== "object") {
1022
+ return null;
1023
+ }
1024
+ if (typeof segment === "string") {
1025
+ if (Array.isArray(current)) {
1026
+ const index = parseInt(segment, 10);
1027
+ if (isNaN(index))
1028
+ return null;
1029
+ current = current[index];
1030
+ }
1031
+ else {
1032
+ current = current[segment];
1033
+ }
1034
+ }
1035
+ else if (typeof segment === "number") {
1036
+ if (Array.isArray(current)) {
1037
+ if (segment >= 0 && segment < current.length) {
1038
+ current = current[segment];
1039
+ }
1040
+ else {
1041
+ return null;
1042
+ }
1043
+ }
1044
+ else {
1045
+ return null;
1046
+ }
1047
+ }
1048
+ else if (segment instanceof RegExp) {
1049
+ if (Array.isArray(current)) {
1050
+ return null;
1051
+ }
1052
+ const obj = current;
1053
+ const matchingKey = Object.keys(obj).find((key) => segment.test(key));
1054
+ if (!matchingKey)
1055
+ return null;
1056
+ current = obj[matchingKey];
1057
+ }
1058
+ else {
1059
+ return null;
1060
+ }
1061
+ }
1062
+ const lastSegment = pathSegments[pathSegments.length - 1];
1063
+ if (typeof lastSegment === "string") {
1064
+ if (Array.isArray(current)) {
1065
+ const index = parseInt(lastSegment, 10);
1066
+ if (isNaN(index))
1067
+ return null;
1068
+ return { value: current[index], parent: current, key: index };
1069
+ }
1070
+ else if (current !== null &&
1071
+ current !== undefined &&
1072
+ typeof current === "object") {
1073
+ return {
1074
+ value: current[lastSegment],
1075
+ parent: current,
1076
+ key: lastSegment,
1077
+ };
1078
+ }
1079
+ }
1080
+ else if (typeof lastSegment === "number") {
1081
+ if (Array.isArray(current)) {
1082
+ if (lastSegment >= 0 && lastSegment < current.length) {
1083
+ return {
1084
+ value: current[lastSegment],
1085
+ parent: current,
1086
+ key: lastSegment,
1087
+ };
1088
+ }
1089
+ }
1090
+ return null;
1091
+ }
1092
+ else if (lastSegment instanceof RegExp) {
1093
+ if (Array.isArray(current)) {
1094
+ return null;
1095
+ }
1096
+ const obj = current;
1097
+ const matchingKey = Object.keys(obj).find((key) => lastSegment.test(key));
1098
+ if (!matchingKey)
1099
+ return null;
1100
+ return { value: obj[matchingKey], parent: current, key: matchingKey };
1101
+ }
1102
+ return null;
1103
+ }
1104
+ function setValueAtPath(obj, pathSegments, newValue) {
1105
+ const result = getValueAtPath(obj, pathSegments);
1106
+ if (!result || result.parent === null)
1107
+ return false;
1108
+ if (Array.isArray(result.parent)) {
1109
+ result.parent[result.key] = newValue;
1110
+ }
1111
+ else {
1112
+ result.parent[result.key] = newValue;
1113
+ }
1114
+ return true;
1013
1115
  }
1014
1116
  /**
1015
- * Shallow/deep-ish equality check to avoid unnecessary runs on identical values
1117
+ * Sets a value at a path, creating intermediate objects as needed.
1118
+ * Mutates the root object in place.
1119
+ * @param root - The root object to modify (must be an object, will be initialized if needed)
1120
+ * @param pathSegments - The path segments to traverse
1121
+ * @param value - The value to set, or null to delete the path
1122
+ * @throws Error if path cannot be created (e.g., array indices not supported, invalid parent types)
1016
1123
  */
1017
- function valuesEqual(a, b) {
1018
- if (a === b)
1019
- return true;
1020
- if (typeof a !== typeof b)
1021
- return false;
1022
- if (a && b && typeof a === "object") {
1023
- try {
1024
- return JSON.stringify(a) === JSON.stringify(b);
1124
+ function setValueAtPathWithCreation(root, pathSegments, value) {
1125
+ if (value === null) {
1126
+ const result = getValueAtPath(root, pathSegments);
1127
+ if (result && result.parent !== null && !Array.isArray(result.parent)) {
1128
+ delete result.parent[result.key];
1025
1129
  }
1026
- catch {
1130
+ return;
1131
+ }
1132
+ if (!root || typeof root !== "object" || Array.isArray(root)) {
1133
+ throw new Error("Root must be an object");
1134
+ }
1135
+ let current = root;
1136
+ for (let i = 0; i < pathSegments.length - 1; i++) {
1137
+ const segment = pathSegments[i];
1138
+ if (typeof segment === "string") {
1139
+ if (!current ||
1140
+ typeof current !== "object" ||
1141
+ Array.isArray(current) ||
1142
+ !(segment in current) ||
1143
+ typeof current[segment] !== "object" ||
1144
+ current[segment] === null ||
1145
+ Array.isArray(current[segment])) {
1146
+ if (!current || typeof current !== "object" || Array.isArray(current)) {
1147
+ throw new Error(`Cannot create path: parent at segment ${i} is not an object`);
1148
+ }
1149
+ current[segment] = {};
1150
+ }
1151
+ current = current[segment];
1152
+ }
1153
+ else {
1154
+ throw new Error("Array indices not supported in extData paths");
1155
+ }
1156
+ }
1157
+ const lastSegment = pathSegments[pathSegments.length - 1];
1158
+ if (typeof lastSegment === "string") {
1159
+ if (!current || typeof current !== "object" || Array.isArray(current)) {
1160
+ throw new Error(`Cannot set value: parent at final segment is not an object`);
1161
+ }
1162
+ current[lastSegment] = value;
1163
+ }
1164
+ else {
1165
+ throw new Error("Array indices not supported in extData paths");
1166
+ }
1167
+ }
1168
+ function findMatchingPaths(obj, pathSegments, currentPath = []) {
1169
+ if (pathSegments.length === 0) {
1170
+ return [{ path: currentPath, value: obj }];
1171
+ }
1172
+ const [currentSegment, ...remainingSegments] = pathSegments;
1173
+ const results = [];
1174
+ if (currentSegment === undefined) {
1175
+ return results;
1176
+ }
1177
+ if (typeof currentSegment === "string") {
1178
+ if (Array.isArray(obj)) {
1179
+ const index = parseInt(currentSegment, 10);
1180
+ if (!isNaN(index) && index >= 0 && index < obj.length) {
1181
+ results.push(...findMatchingPaths(obj[index], remainingSegments, [
1182
+ ...currentPath,
1183
+ index,
1184
+ ]));
1185
+ }
1186
+ }
1187
+ else if (obj !== null && obj !== undefined && typeof obj === "object") {
1188
+ const objRecord = obj;
1189
+ if (currentSegment in objRecord) {
1190
+ results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [
1191
+ ...currentPath,
1192
+ currentSegment,
1193
+ ]));
1194
+ }
1195
+ }
1196
+ }
1197
+ else if (typeof currentSegment === "number") {
1198
+ if (Array.isArray(obj)) {
1199
+ if (currentSegment >= 0 && currentSegment < obj.length) {
1200
+ results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [
1201
+ ...currentPath,
1202
+ currentSegment,
1203
+ ]));
1204
+ }
1205
+ }
1206
+ }
1207
+ else if (currentSegment instanceof RegExp) {
1208
+ if (Array.isArray(obj)) {
1209
+ for (let i = 0; i < obj.length; i++) {
1210
+ results.push(...findMatchingPaths(obj[i], remainingSegments, [...currentPath, i]));
1211
+ }
1212
+ }
1213
+ else if (obj !== null && obj !== undefined && typeof obj === "object") {
1214
+ const objRecord = obj;
1215
+ for (const key of Object.keys(objRecord)) {
1216
+ if (currentSegment.test(key)) {
1217
+ results.push(...findMatchingPaths(objRecord[key], remainingSegments, [
1218
+ ...currentPath,
1219
+ key,
1220
+ ]));
1221
+ }
1222
+ }
1223
+ }
1224
+ }
1225
+ return results;
1226
+ }
1227
+ function stringifyJson(obj, oneLiner) {
1228
+ // No formatting requested: behave exactly like native JSON.stringify.
1229
+ if (!oneLiner)
1230
+ return JSON.stringify(obj);
1231
+ const indentSize = Number.isFinite(oneLiner.indent)
1232
+ ? Math.max(0, Math.floor(oneLiner.indent))
1233
+ : 2;
1234
+ const maxDepth = typeof oneLiner.maxDepth === "number" && Number.isFinite(oneLiner.maxDepth)
1235
+ ? oneLiner.maxDepth
1236
+ : undefined;
1237
+ const patterns = (oneLiner.paths || []).filter(Boolean);
1238
+ // Preserve JSON.stringify semantics for things like toJSON(), dropping functions/undefined, etc.
1239
+ // Note: this still throws on circular structures (same as JSON.stringify).
1240
+ const base = JSON.stringify(obj);
1241
+ if (base === undefined)
1242
+ return base;
1243
+ const value = JSON.parse(base);
1244
+ const pathMatchers = patterns.map((p) => compilePathMatcher(String(p)));
1245
+ const formatKey = (k) => JSON.stringify(k);
1246
+ const shouldInline = (path, key, v, depth) => {
1247
+ if (maxDepth !== undefined && depth > maxDepth)
1248
+ return true;
1249
+ if (oneLiner.criteria?.({ path, key, value: v, depth }))
1250
+ return true;
1251
+ if (!pathMatchers.length)
1027
1252
  return false;
1253
+ const tokensWithRoot = tokenizePath(path);
1254
+ const tokensNoRoot = tokensWithRoot[0] === "$" ? tokensWithRoot.slice(1) : tokensWithRoot;
1255
+ return pathMatchers.some((m) => m(tokensWithRoot, tokensNoRoot));
1256
+ };
1257
+ const stringifyInline = (v, depth, path) => {
1258
+ if (v === null)
1259
+ return "null";
1260
+ const t = typeof v;
1261
+ if (t === "string")
1262
+ return JSON.stringify(v);
1263
+ if (t === "number" || t === "boolean")
1264
+ return String(v);
1265
+ if (Array.isArray(v)) {
1266
+ if (!v.length)
1267
+ return "[]";
1268
+ const parts = v.map((vv, i) => stringifyInline(vv));
1269
+ return `[ ${parts.join(", ")} ]`;
1270
+ }
1271
+ if (t === "object") {
1272
+ const keys = Object.keys(v);
1273
+ if (!keys.length)
1274
+ return "{}";
1275
+ const parts = keys.map((k) => `${formatKey(k)}: ${stringifyInline(v[k])}`);
1276
+ return `{ ${parts.join(", ")} }`;
1277
+ }
1278
+ // Shouldn't happen after JSON.parse(JSON.stringify(...)), but keep output valid JSON.
1279
+ return "null";
1280
+ };
1281
+ const stringifyPretty = (v, depth, path, key) => {
1282
+ if (shouldInline(path, key, v, depth))
1283
+ return stringifyInline(v);
1284
+ if (v === null)
1285
+ return "null";
1286
+ const t = typeof v;
1287
+ if (t === "string")
1288
+ return JSON.stringify(v);
1289
+ if (t === "number" || t === "boolean")
1290
+ return String(v);
1291
+ const indentCur = " ".repeat(indentSize * depth);
1292
+ const indentInner = " ".repeat(indentSize * (depth + 1));
1293
+ if (Array.isArray(v)) {
1294
+ if (!v.length)
1295
+ return "[]";
1296
+ // Compact array style: `[{...}, {...}]` while still allowing multi-line objects within.
1297
+ const parts = v.map((vv, i) => stringifyPretty(vv, depth + 1, `${path}[${i}]`, String(i)));
1298
+ return `[ ${parts.join(", ")} ]`;
1299
+ }
1300
+ if (t === "object") {
1301
+ const keys = Object.keys(v);
1302
+ if (!keys.length)
1303
+ return "{}";
1304
+ const lines = keys.map((k, idx) => {
1305
+ const childPath = `${path}.${k}`;
1306
+ const rendered = stringifyPretty(v[k], depth + 1, childPath, k);
1307
+ const comma = idx === keys.length - 1 ? "" : ",";
1308
+ return `${indentInner}${formatKey(k)}: ${rendered}${comma}`;
1309
+ });
1310
+ return `{\n${lines.join("\n")}\n${indentCur}}`;
1311
+ }
1312
+ return "null";
1313
+ };
1314
+ return stringifyPretty(value, 0, "$", "$");
1315
+ }
1316
+ function tokenizePath(path) {
1317
+ // Path format we generate: `$`, `$.a.b[0].c`
1318
+ // Tokens: ["$", "a", "b", "[0]", "c"]
1319
+ const tokens = [];
1320
+ let i = 0;
1321
+ if (path.startsWith("$")) {
1322
+ tokens.push("$");
1323
+ i = 1;
1324
+ }
1325
+ while (i < path.length) {
1326
+ const ch = path[i];
1327
+ if (ch === ".") {
1328
+ i++;
1329
+ const start = i;
1330
+ while (i < path.length && path[i] !== "." && path[i] !== "[")
1331
+ i++;
1332
+ if (i > start)
1333
+ tokens.push(path.slice(start, i));
1334
+ continue;
1028
1335
  }
1336
+ if (ch === "[") {
1337
+ const end = path.indexOf("]", i + 1);
1338
+ if (end < 0) {
1339
+ tokens.push(path.slice(i));
1340
+ break;
1341
+ }
1342
+ tokens.push(path.slice(i, end + 1));
1343
+ i = end + 1;
1344
+ continue;
1345
+ }
1346
+ // Unexpected char; skip.
1347
+ i++;
1029
1348
  }
1030
- return false;
1349
+ return tokens.filter((t) => t.length);
1350
+ }
1351
+ function tokenizePattern(pattern) {
1352
+ // Pattern format: `$`, `$.a.*.b`, `**.graph.**`, `arr[2].z`
1353
+ // Tokens: ["$", "a", "*", "b"] etc. Brackets become their own token: ["arr","[2]","z"]
1354
+ const tokens = [];
1355
+ let i = 0;
1356
+ if (pattern.startsWith("$")) {
1357
+ tokens.push("$");
1358
+ i = 1;
1359
+ }
1360
+ while (i < pattern.length) {
1361
+ const ch = pattern[i];
1362
+ if (ch === ".") {
1363
+ i++;
1364
+ continue;
1365
+ }
1366
+ if (ch === "[") {
1367
+ const end = pattern.indexOf("]", i + 1);
1368
+ if (end < 0) {
1369
+ tokens.push(pattern.slice(i));
1370
+ break;
1371
+ }
1372
+ tokens.push(pattern.slice(i, end + 1));
1373
+ i = end + 1;
1374
+ continue;
1375
+ }
1376
+ const start = i;
1377
+ while (i < pattern.length && pattern[i] !== "." && pattern[i] !== "[")
1378
+ i++;
1379
+ if (i > start)
1380
+ tokens.push(pattern.slice(start, i));
1381
+ }
1382
+ return tokens.filter((t) => t.length);
1031
1383
  }
1384
+ function compilePathMatcher(pattern) {
1385
+ // Wildcard semantics (case-insensitive):
1386
+ // - `*` matches exactly 1 path segment (key or `[index]`)
1387
+ // - `**` matches 0 or more segments
1388
+ // - `#` matches exactly 1 numeric segment (e.g. `"0"` or `[0]`)
1389
+ // - `##` matches 0 or more numeric segments
1390
+ // - `[*]` matches exactly 1 index segment
1391
+ const pat = tokenizePattern(pattern);
1392
+ const expectsRoot = pat[0] === "$";
1393
+ const eq = (a, b) => a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0;
1394
+ const isIndex = (t) => t.startsWith("[") && t.endsWith("]");
1395
+ const isNumericKey = (t) => /^[0-9]+$/.test(t);
1396
+ const isNumericSegment = (t) => isNumericKey(t) || (isIndex(t) && /^[0-9]+$/.test(t.slice(1, -1)));
1397
+ const match = (patTokens, pathTokens) => {
1398
+ const memo = new Map();
1399
+ const go = (pi, ti) => {
1400
+ const key = `${pi},${ti}`;
1401
+ const cached = memo.get(key);
1402
+ if (cached !== undefined)
1403
+ return cached;
1404
+ let res = false;
1405
+ if (pi === patTokens.length) {
1406
+ res = ti === pathTokens.length;
1407
+ }
1408
+ else {
1409
+ const p = patTokens[pi];
1410
+ if (p === "**") {
1411
+ // Match any number of segments, including zero.
1412
+ for (let k = ti; k <= pathTokens.length; k++) {
1413
+ if (go(pi + 1, k)) {
1414
+ res = true;
1415
+ break;
1416
+ }
1417
+ }
1418
+ }
1419
+ else if (p === "##") {
1420
+ // Match any number of numeric segments, including zero.
1421
+ for (let k = ti; k <= pathTokens.length; k++) {
1422
+ let ok = true;
1423
+ for (let j = ti; j < k; j++) {
1424
+ if (!isNumericSegment(pathTokens[j])) {
1425
+ ok = false;
1426
+ break;
1427
+ }
1428
+ }
1429
+ if (ok && go(pi + 1, k)) {
1430
+ res = true;
1431
+ break;
1432
+ }
1433
+ }
1434
+ }
1435
+ else if (p === "*") {
1436
+ res = ti < pathTokens.length && go(pi + 1, ti + 1);
1437
+ }
1438
+ else if (p === "#") {
1439
+ res =
1440
+ ti < pathTokens.length &&
1441
+ isNumericSegment(pathTokens[ti]) &&
1442
+ go(pi + 1, ti + 1);
1443
+ }
1444
+ else if (p === "[*]") {
1445
+ res =
1446
+ ti < pathTokens.length &&
1447
+ isIndex(pathTokens[ti]) &&
1448
+ go(pi + 1, ti + 1);
1449
+ }
1450
+ else {
1451
+ res =
1452
+ ti < pathTokens.length &&
1453
+ eq(p.toLowerCase(), pathTokens[ti].toLowerCase()) &&
1454
+ go(pi + 1, ti + 1);
1455
+ }
1456
+ }
1457
+ memo.set(key, res);
1458
+ return res;
1459
+ };
1460
+ return go(0, 0);
1461
+ };
1462
+ return (tokensWithRoot, tokensNoRoot) => match(pat, expectsRoot ? tokensWithRoot : tokensNoRoot);
1463
+ }
1464
+ function stringifySceneAndOps(obj) {
1465
+ return stringifyJson(obj, {
1466
+ indent: 2,
1467
+ paths: [
1468
+ "**.camera.animation.*",
1469
+ "**.animations.*",
1470
+ "**.nodes.#.*",
1471
+ "**.graphs.#.*",
1472
+ "**.instances.#.*",
1473
+ "**.shaders.#.*",
1474
+ "**.materials.#.*",
1475
+ "**.ext.#",
1476
+ "**.ext.*.#.*",
1477
+ "**.perUserExt.*.#",
1478
+ "**.perUserExt.*.*.#",
1479
+ ],
1480
+ criteria: ({ value, key }) => (typeof value === "object" &&
1481
+ value &&
1482
+ Object.keys(value).sort().join(",") === "x,y,z") ||
1483
+ key === "value",
1484
+ });
1485
+ }
1486
+
1032
1487
  /**
1033
1488
  * A reusable logger class that supports configurable log levels and prefixes.
1034
1489
  * Can be instantiated with a default log level and optionally override per call.
@@ -1106,7 +1561,7 @@ class LevelLogger {
1106
1561
  const contextStr = rest && Object.keys(rest).length > 0
1107
1562
  ? `${formatJson ? "\n" : " "}${Object.entries(rest)
1108
1563
  .map(([k, v]) => formatJson
1109
- ? `${k}=${JSON.stringify(v, undefined, 2)}`
1564
+ ? `${k}=${stringifySceneAndOps(v)}`
1110
1565
  : `${k}=${JSON.stringify(v)}`)
1111
1566
  .join(formatJson ? "\n" : " ")}`
1112
1567
  : "";
@@ -1411,29 +1866,63 @@ class RunContextManager {
1411
1866
  for (const id of toCancel) {
1412
1867
  this.graph.clearNodeRunContextIds(id);
1413
1868
  }
1414
- }
1415
- /**
1416
- * Resolve all pending run-context promises (for cleanup)
1417
- */
1418
- resolveAll() {
1419
- const count = this.runContexts.size;
1420
- this.logger.info("resolve-all-run-contexts", {
1421
- count,
1422
- runContextIds: Array.from(this.runContexts.keys()),
1423
- });
1424
- for (const ctx of this.runContexts.values()) {
1425
- if (ctx.resolve)
1426
- ctx.resolve();
1869
+ }
1870
+ /**
1871
+ * Resolve all pending run-context promises (for cleanup)
1872
+ */
1873
+ resolveAll() {
1874
+ const count = this.runContexts.size;
1875
+ this.logger.info("resolve-all-run-contexts", {
1876
+ count,
1877
+ runContextIds: Array.from(this.runContexts.keys()),
1878
+ });
1879
+ for (const ctx of this.runContexts.values()) {
1880
+ if (ctx.resolve)
1881
+ ctx.resolve();
1882
+ }
1883
+ }
1884
+ /**
1885
+ * Clear all run-contexts
1886
+ */
1887
+ clear() {
1888
+ const count = this.runContexts.size;
1889
+ this.logger.info("clear-all-run-contexts", { count });
1890
+ this.runContexts.clear();
1891
+ }
1892
+ }
1893
+
1894
+ /**
1895
+ * Shared utility functions for runtime components
1896
+ */
1897
+ /**
1898
+ * Type guard to check if a value is a Promise
1899
+ */
1900
+ function isPromise(value) {
1901
+ return !!value && typeof value.then === "function";
1902
+ }
1903
+ /**
1904
+ * Unwrap a value that might be a Promise
1905
+ */
1906
+ async function unwrapMaybePromise(value) {
1907
+ return isPromise(value) ? await value : value;
1908
+ }
1909
+ /**
1910
+ * Shallow/deep-ish equality check to avoid unnecessary runs on identical values
1911
+ */
1912
+ function valuesEqual(a, b) {
1913
+ if (a === b)
1914
+ return true;
1915
+ if (typeof a !== typeof b)
1916
+ return false;
1917
+ if (a && b && typeof a === "object") {
1918
+ try {
1919
+ return JSON.stringify(a) === JSON.stringify(b);
1920
+ }
1921
+ catch {
1922
+ return false;
1427
1923
  }
1428
1924
  }
1429
- /**
1430
- * Clear all run-contexts
1431
- */
1432
- clear() {
1433
- const count = this.runContexts.size;
1434
- this.logger.info("clear-all-run-contexts", { count });
1435
- this.runContexts.clear();
1436
- }
1925
+ return false;
1437
1926
  }
1438
1927
 
1439
1928
  function tryHandleResolving(def, registry, environment) {
@@ -5551,235 +6040,6 @@ function createValidationGraphRegistry(id) {
5551
6040
  return registry;
5552
6041
  }
5553
6042
 
5554
- function parseJsonPath(path) {
5555
- if (typeof path === "string") {
5556
- return path.split(".").flatMap((segment) => {
5557
- const arrayMatch = segment.match(/^(.+)\[(\d+)\]$/);
5558
- if (arrayMatch) {
5559
- const index = parseInt(arrayMatch[2], 10);
5560
- return arrayMatch[1] ? [arrayMatch[1], index] : [index];
5561
- }
5562
- return [segment];
5563
- });
5564
- }
5565
- return path;
5566
- }
5567
- function getValueAtPath(obj, pathSegments) {
5568
- if (pathSegments.length === 0) {
5569
- return { value: obj, parent: null, key: "" };
5570
- }
5571
- let current = obj;
5572
- for (let i = 0; i < pathSegments.length - 1; i++) {
5573
- const segment = pathSegments[i];
5574
- if (current === null ||
5575
- current === undefined ||
5576
- typeof current !== "object") {
5577
- return null;
5578
- }
5579
- if (typeof segment === "string") {
5580
- if (Array.isArray(current)) {
5581
- const index = parseInt(segment, 10);
5582
- if (isNaN(index))
5583
- return null;
5584
- current = current[index];
5585
- }
5586
- else {
5587
- current = current[segment];
5588
- }
5589
- }
5590
- else if (typeof segment === "number") {
5591
- if (Array.isArray(current)) {
5592
- if (segment >= 0 && segment < current.length) {
5593
- current = current[segment];
5594
- }
5595
- else {
5596
- return null;
5597
- }
5598
- }
5599
- else {
5600
- return null;
5601
- }
5602
- }
5603
- else if (segment instanceof RegExp) {
5604
- if (Array.isArray(current)) {
5605
- return null;
5606
- }
5607
- const obj = current;
5608
- const matchingKey = Object.keys(obj).find((key) => segment.test(key));
5609
- if (!matchingKey)
5610
- return null;
5611
- current = obj[matchingKey];
5612
- }
5613
- else {
5614
- return null;
5615
- }
5616
- }
5617
- const lastSegment = pathSegments[pathSegments.length - 1];
5618
- if (typeof lastSegment === "string") {
5619
- if (Array.isArray(current)) {
5620
- const index = parseInt(lastSegment, 10);
5621
- if (isNaN(index))
5622
- return null;
5623
- return { value: current[index], parent: current, key: index };
5624
- }
5625
- else if (current !== null &&
5626
- current !== undefined &&
5627
- typeof current === "object") {
5628
- return {
5629
- value: current[lastSegment],
5630
- parent: current,
5631
- key: lastSegment,
5632
- };
5633
- }
5634
- }
5635
- else if (typeof lastSegment === "number") {
5636
- if (Array.isArray(current)) {
5637
- if (lastSegment >= 0 && lastSegment < current.length) {
5638
- return {
5639
- value: current[lastSegment],
5640
- parent: current,
5641
- key: lastSegment,
5642
- };
5643
- }
5644
- }
5645
- return null;
5646
- }
5647
- else if (lastSegment instanceof RegExp) {
5648
- if (Array.isArray(current)) {
5649
- return null;
5650
- }
5651
- const obj = current;
5652
- const matchingKey = Object.keys(obj).find((key) => lastSegment.test(key));
5653
- if (!matchingKey)
5654
- return null;
5655
- return { value: obj[matchingKey], parent: current, key: matchingKey };
5656
- }
5657
- return null;
5658
- }
5659
- function setValueAtPath(obj, pathSegments, newValue) {
5660
- const result = getValueAtPath(obj, pathSegments);
5661
- if (!result || result.parent === null)
5662
- return false;
5663
- if (Array.isArray(result.parent)) {
5664
- result.parent[result.key] = newValue;
5665
- }
5666
- else {
5667
- result.parent[result.key] = newValue;
5668
- }
5669
- return true;
5670
- }
5671
- /**
5672
- * Sets a value at a path, creating intermediate objects as needed.
5673
- * Mutates the root object in place.
5674
- * @param root - The root object to modify (must be an object, will be initialized if needed)
5675
- * @param pathSegments - The path segments to traverse
5676
- * @param value - The value to set, or null to delete the path
5677
- * @throws Error if path cannot be created (e.g., array indices not supported, invalid parent types)
5678
- */
5679
- function setValueAtPathWithCreation(root, pathSegments, value) {
5680
- if (value === null) {
5681
- const result = getValueAtPath(root, pathSegments);
5682
- if (result && result.parent !== null && !Array.isArray(result.parent)) {
5683
- delete result.parent[result.key];
5684
- }
5685
- return;
5686
- }
5687
- if (!root || typeof root !== "object" || Array.isArray(root)) {
5688
- throw new Error("Root must be an object");
5689
- }
5690
- let current = root;
5691
- for (let i = 0; i < pathSegments.length - 1; i++) {
5692
- const segment = pathSegments[i];
5693
- if (typeof segment === "string") {
5694
- if (!current ||
5695
- typeof current !== "object" ||
5696
- Array.isArray(current) ||
5697
- !(segment in current) ||
5698
- typeof current[segment] !== "object" ||
5699
- current[segment] === null ||
5700
- Array.isArray(current[segment])) {
5701
- if (!current || typeof current !== "object" || Array.isArray(current)) {
5702
- throw new Error(`Cannot create path: parent at segment ${i} is not an object`);
5703
- }
5704
- current[segment] = {};
5705
- }
5706
- current = current[segment];
5707
- }
5708
- else {
5709
- throw new Error("Array indices not supported in extData paths");
5710
- }
5711
- }
5712
- const lastSegment = pathSegments[pathSegments.length - 1];
5713
- if (typeof lastSegment === "string") {
5714
- if (!current || typeof current !== "object" || Array.isArray(current)) {
5715
- throw new Error(`Cannot set value: parent at final segment is not an object`);
5716
- }
5717
- current[lastSegment] = value;
5718
- }
5719
- else {
5720
- throw new Error("Array indices not supported in extData paths");
5721
- }
5722
- }
5723
- function findMatchingPaths(obj, pathSegments, currentPath = []) {
5724
- if (pathSegments.length === 0) {
5725
- return [{ path: currentPath, value: obj }];
5726
- }
5727
- const [currentSegment, ...remainingSegments] = pathSegments;
5728
- const results = [];
5729
- if (currentSegment === undefined) {
5730
- return results;
5731
- }
5732
- if (typeof currentSegment === "string") {
5733
- if (Array.isArray(obj)) {
5734
- const index = parseInt(currentSegment, 10);
5735
- if (!isNaN(index) && index >= 0 && index < obj.length) {
5736
- results.push(...findMatchingPaths(obj[index], remainingSegments, [
5737
- ...currentPath,
5738
- index,
5739
- ]));
5740
- }
5741
- }
5742
- else if (obj !== null && obj !== undefined && typeof obj === "object") {
5743
- const objRecord = obj;
5744
- if (currentSegment in objRecord) {
5745
- results.push(...findMatchingPaths(objRecord[currentSegment], remainingSegments, [
5746
- ...currentPath,
5747
- currentSegment,
5748
- ]));
5749
- }
5750
- }
5751
- }
5752
- else if (typeof currentSegment === "number") {
5753
- if (Array.isArray(obj)) {
5754
- if (currentSegment >= 0 && currentSegment < obj.length) {
5755
- results.push(...findMatchingPaths(obj[currentSegment], remainingSegments, [
5756
- ...currentPath,
5757
- currentSegment,
5758
- ]));
5759
- }
5760
- }
5761
- }
5762
- else if (currentSegment instanceof RegExp) {
5763
- if (Array.isArray(obj)) {
5764
- for (let i = 0; i < obj.length; i++) {
5765
- results.push(...findMatchingPaths(obj[i], remainingSegments, [...currentPath, i]));
5766
- }
5767
- }
5768
- else if (obj !== null && obj !== undefined && typeof obj === "object") {
5769
- const objRecord = obj;
5770
- for (const key of Object.keys(objRecord)) {
5771
- if (currentSegment.test(key)) {
5772
- results.push(...findMatchingPaths(objRecord[key], remainingSegments, [
5773
- ...currentPath,
5774
- key,
5775
- ]));
5776
- }
5777
- }
5778
- }
5779
- }
5780
- return results;
5781
- }
5782
-
5783
6043
  function mergeGraphDefinitions(target, source) {
5784
6044
  const existingNodeIds = new Set(target.nodes.map((n) => n.nodeId));
5785
6045
  const existingEdgeIds = new Set(target.edges.map((e) => e.id));
@@ -6377,5 +6637,5 @@ function buildValueConverter(config) {
6377
6637
  };
6378
6638
  }
6379
6639
 
6380
- export { BaseCompareOperation, BaseLogicOperation, BaseMathOperation, CompositeCategory, ComputeCategory, Graph, GraphBuilder, GraphRuntime, LevelLogger, LocalEngine, Registry, buildValueConverter, computeGraphCenter, convertSnapshot, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createSimpleGraphDef, createSimpleGraphRegistry, createValidationGraphDef, createValidationGraphRegistry, findMatchingPaths, generateId, getEffectiveInputs, getInputDeclaredTypes, getInputHandleMetadata, getInputTypeId, getValueAtPath, installLogging, isInputPrivate, isTyped, mergeInputHandleDescriptors, mergeRuntimeState, mergeSnapshots, offsetImportedPositions, parseJsonPath, registerDelayNode, registerProgressNodes, setValueAtPath, setValueAtPathWithCreation, typed, unwrapTypeId, unwrapValue };
6640
+ export { BaseCompareOperation, BaseLogicOperation, BaseMathOperation, CompositeCategory, ComputeCategory, Graph, GraphBuilder, GraphRuntime, LevelLogger, LocalEngine, Registry, buildValueConverter, computeGraphCenter, convertSnapshot, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createSimpleGraphDef, createSimpleGraphRegistry, createValidationGraphDef, createValidationGraphRegistry, findMatchingPaths, generateId, getEffectiveInputs, getInputDeclaredTypes, getInputHandleMetadata, getInputTypeId, getValueAtPath, installLogging, isInputPrivate, isTyped, mergeInputHandleDescriptors, mergeRuntimeState, mergeSnapshots, offsetImportedPositions, parseJsonPath, registerDelayNode, registerProgressNodes, setValueAtPath, setValueAtPathWithCreation, stringifyJson, stringifySceneAndOps, typed, unwrapTypeId, unwrapValue };
6381
6641
  //# sourceMappingURL=index.js.map