json-diff-ts 5.0.0-alpha.7 → 5.0.0-alpha.9

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/dist/index.js CHANGED
@@ -427,12 +427,14 @@ var compareArray = (oldObj, newObj, path, keyPath, options) => {
427
427
  const isFunctionKey = typeof uniqKey === "function" && uniqKey.length === 2;
428
428
  if (diffs.length) {
429
429
  const resolvedKey = isFunctionKey ? uniqKey(newObj[0] ?? oldObj[0], true) : uniqKey;
430
+ const sampleEl = newObj[0] ?? oldObj[0];
431
+ const isNestedPath = typeof resolvedKey === "string" && resolvedKey.includes(".") && NESTED_PATH_RE.test(resolvedKey) && (isFunctionKey || !(sampleEl != null && typeof sampleEl === "object" && resolvedKey in sampleEl));
430
432
  return [
431
433
  {
432
434
  type: "UPDATE" /* UPDATE */,
433
435
  key: getKey(path),
434
436
  embeddedKey: resolvedKey,
435
- ...isFunctionKey && typeof resolvedKey === "string" && NESTED_PATH_RE.test(resolvedKey) && resolvedKey.includes(".") ? { embeddedKeyIsPath: true } : {},
437
+ ...isNestedPath ? { embeddedKeyIsPath: true } : {},
436
438
  changes: diffs
437
439
  }
438
440
  ];
@@ -473,7 +475,8 @@ var convertArrayToObj = (arr, uniqKey) => {
473
475
  obj[i] = value;
474
476
  }
475
477
  } else {
476
- const keyFunction = typeof uniqKey === "string" ? (item) => item[uniqKey] : uniqKey;
478
+ const maybeNestedPath = typeof uniqKey === "string" && uniqKey.includes(".") && NESTED_PATH_RE.test(uniqKey);
479
+ const keyFunction = typeof uniqKey === "string" ? maybeNestedPath ? (item) => item != null && typeof item === "object" && uniqKey in item ? item[uniqKey] : resolveProperty(item, uniqKey, true) : (item) => item[uniqKey] : uniqKey;
477
480
  obj = keyBy(arr, keyFunction);
478
481
  }
479
482
  return obj;
@@ -520,7 +523,7 @@ var indexOfItemInArray = (arr, key, value, isPath) => {
520
523
  const item = arr[i];
521
524
  if (item == null) continue;
522
525
  const resolved = resolveProperty(item, key, isPath);
523
- if (resolved != null && String(resolved) === String(value)) {
526
+ if (resolved !== void 0 && String(resolved) === String(value)) {
524
527
  return i;
525
528
  }
526
529
  }
@@ -576,7 +579,7 @@ var applyArrayChange = (arr, change) => {
576
579
  } else {
577
580
  element = arr.find((el) => {
578
581
  const resolved = resolveProperty(el, change.embeddedKey, change.embeddedKeyIsPath);
579
- return resolved != null && String(resolved) === String(subchange.key);
582
+ return resolved !== void 0 && String(resolved) === String(subchange.key);
580
583
  });
581
584
  }
582
585
  if (element) {
@@ -646,7 +649,7 @@ var revertArrayChange = (arr, change) => {
646
649
  } else {
647
650
  element = arr.find((el) => {
648
651
  const resolved = resolveProperty(el, change.embeddedKey, change.embeddedKeyIsPath);
649
- return resolved != null && String(resolved) === String(subchange.key);
652
+ return resolved !== void 0 && String(resolved) === String(subchange.key);
650
653
  });
651
654
  }
652
655
  if (element) {
@@ -1294,14 +1297,16 @@ function walkChanges(changes, basePath, oldCtx, newCtx, ops, options) {
1294
1297
  const childOld = change.key === "$root" ? oldCtx : oldCtx?.[change.key];
1295
1298
  const childNew = change.key === "$root" ? newCtx : newCtx?.[change.key];
1296
1299
  if (change.embeddedKey) {
1297
- for (const childChange of change.changes) {
1300
+ const orderedChildChanges = orderArrayChildChanges(change.changes, change.embeddedKey);
1301
+ for (const childChange of orderedChildChanges) {
1298
1302
  const filterPath = buildCanonicalFilterPath(
1299
1303
  childPath,
1300
1304
  change.embeddedKey,
1301
1305
  childChange.key,
1302
1306
  childOld,
1303
1307
  childNew,
1304
- childChange
1308
+ childChange,
1309
+ change.embeddedKeyIsPath
1305
1310
  );
1306
1311
  if (childChange.changes) {
1307
1312
  const oldEl = findElement(childOld, change.embeddedKey, childChange.key);
@@ -1320,6 +1325,46 @@ function walkChanges(changes, basePath, oldCtx, newCtx, ops, options) {
1320
1325
  }
1321
1326
  }
1322
1327
  }
1328
+ function orderArrayChildChanges(changes, embeddedKey) {
1329
+ if (embeddedKey !== "$index") {
1330
+ return changes;
1331
+ }
1332
+ const groups = [];
1333
+ const pureRemoves = [];
1334
+ for (let i = 0; i < changes.length; i++) {
1335
+ const current = changes[i];
1336
+ const next = changes[i + 1];
1337
+ if (current.type === "REMOVE" /* REMOVE */ && next && next.type === "ADD" /* ADD */ && String(current.key) === String(next.key)) {
1338
+ groups.push({ kind: "preserved", changes: [current, next] });
1339
+ i++;
1340
+ continue;
1341
+ }
1342
+ if (current.type === "REMOVE" /* REMOVE */) {
1343
+ pureRemoves.push(current);
1344
+ groups.push({ kind: "pure-remove" });
1345
+ continue;
1346
+ }
1347
+ groups.push({ kind: "preserved", changes: [current] });
1348
+ }
1349
+ if (pureRemoves.length < 2) {
1350
+ return changes;
1351
+ }
1352
+ const removeIndices = pureRemoves.map((change) => Number(change.key));
1353
+ if (removeIndices.some((idx) => !Number.isInteger(idx))) {
1354
+ return changes;
1355
+ }
1356
+ pureRemoves.sort((a, b) => Number(b.key) - Number(a.key));
1357
+ const ordered = [];
1358
+ let removeIndex = 0;
1359
+ for (const group of groups) {
1360
+ if (group.kind === "pure-remove") {
1361
+ ordered.push(pureRemoves[removeIndex++]);
1362
+ } else {
1363
+ ordered.push(...group.changes);
1364
+ }
1365
+ }
1366
+ return ordered;
1367
+ }
1323
1368
  function emitLeafOp(change, path, ops, options) {
1324
1369
  switch (change.type) {
1325
1370
  case "ADD" /* ADD */: {
@@ -1344,7 +1389,7 @@ function emitLeafOp(change, path, ops, options) {
1344
1389
  }
1345
1390
  }
1346
1391
  }
1347
- function buildCanonicalFilterPath(basePath, embeddedKey, changeKey, oldArr, newArr, change) {
1392
+ function buildCanonicalFilterPath(basePath, embeddedKey, changeKey, oldArr, newArr, change, embeddedKeyIsPath) {
1348
1393
  if (embeddedKey === "$index") {
1349
1394
  return `${basePath}[${changeKey}]`;
1350
1395
  }
@@ -1364,9 +1409,11 @@ function buildCanonicalFilterPath(basePath, embeddedKey, changeKey, oldArr, newA
1364
1409
  const memberAccess2 = typeof keyName === "string" && SIMPLE_PROPERTY_RE2.test(keyName) ? `.${keyName}` : `.${changeKey}`;
1365
1410
  return `${basePath}[?(@${memberAccess2}=='${changeKey}')]`;
1366
1411
  }
1367
- const element = findElementByKey(oldArr, newArr, embeddedKey, changeKey, change.type);
1368
- const typedVal = element ? element[embeddedKey] : changeKey;
1369
- const memberAccess = SIMPLE_PROPERTY_RE2.test(embeddedKey) ? `.${embeddedKey}` : `['${embeddedKey.replace(/'/g, "''")}']`;
1412
+ const isNestedPath = embeddedKeyIsPath && NESTED_PATH_RE3.test(embeddedKey);
1413
+ const element = findElementByKey(oldArr, newArr, embeddedKey, changeKey, change.type, isNestedPath);
1414
+ const resolved = element !== void 0 ? resolveNestedKey(element, embeddedKey, !!isNestedPath) : void 0;
1415
+ const typedVal = resolved !== void 0 ? resolved : changeKey;
1416
+ const memberAccess = SIMPLE_PROPERTY_RE2.test(embeddedKey) || isNestedPath ? `.${embeddedKey}` : `['${embeddedKey.replace(/'/g, "''")}']`;
1370
1417
  return `${basePath}[?(@${memberAccess}==${formatFilterLiteral(typedVal)})]`;
1371
1418
  }
1372
1419
  function findActualValue(oldArr, newArr, stringKey, opType) {
@@ -1405,13 +1452,20 @@ function findElement(arr, embeddedKey, changeKey) {
1405
1452
  }
1406
1453
  return arr.find((item) => item && String(item[embeddedKey]) === changeKey);
1407
1454
  }
1408
- function findElementByKey(oldArr, newArr, embeddedKey, changeKey, opType) {
1455
+ function resolveNestedKey(item, key, isPath) {
1456
+ if (isPath) {
1457
+ return key.split(".").reduce((c, s) => c?.[s], item);
1458
+ }
1459
+ return item?.[key];
1460
+ }
1461
+ function findElementByKey(oldArr, newArr, embeddedKey, changeKey, opType, isPath) {
1462
+ const match = (item) => item && String(resolveNestedKey(item, embeddedKey, !!isPath)) === changeKey;
1409
1463
  if (opType === "REMOVE" /* REMOVE */ || opType === "UPDATE" /* UPDATE */) {
1410
- const el = oldArr?.find((item) => item && String(item[embeddedKey]) === changeKey);
1464
+ const el = oldArr?.find(match);
1411
1465
  if (el) return el;
1412
1466
  }
1413
1467
  if (opType === "ADD" /* ADD */ || opType === "UPDATE" /* UPDATE */) {
1414
- const el = newArr?.find((item) => item && String(item[embeddedKey]) === changeKey);
1468
+ const el = newArr?.find(match);
1415
1469
  if (el) return el;
1416
1470
  }
1417
1471
  return void 0;