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.cjs CHANGED
@@ -487,12 +487,14 @@ var compareArray = (oldObj, newObj, path, keyPath, options) => {
487
487
  const isFunctionKey = typeof uniqKey === "function" && uniqKey.length === 2;
488
488
  if (diffs.length) {
489
489
  const resolvedKey = isFunctionKey ? uniqKey(newObj[0] ?? oldObj[0], true) : uniqKey;
490
+ const sampleEl = newObj[0] ?? oldObj[0];
491
+ const isNestedPath = typeof resolvedKey === "string" && resolvedKey.includes(".") && NESTED_PATH_RE.test(resolvedKey) && (isFunctionKey || !(sampleEl != null && typeof sampleEl === "object" && resolvedKey in sampleEl));
490
492
  return [
491
493
  {
492
494
  type: "UPDATE" /* UPDATE */,
493
495
  key: getKey(path),
494
496
  embeddedKey: resolvedKey,
495
- ...isFunctionKey && typeof resolvedKey === "string" && NESTED_PATH_RE.test(resolvedKey) && resolvedKey.includes(".") ? { embeddedKeyIsPath: true } : {},
497
+ ...isNestedPath ? { embeddedKeyIsPath: true } : {},
496
498
  changes: diffs
497
499
  }
498
500
  ];
@@ -533,7 +535,8 @@ var convertArrayToObj = (arr, uniqKey) => {
533
535
  obj[i] = value;
534
536
  }
535
537
  } else {
536
- const keyFunction = typeof uniqKey === "string" ? (item) => item[uniqKey] : uniqKey;
538
+ const maybeNestedPath = typeof uniqKey === "string" && uniqKey.includes(".") && NESTED_PATH_RE.test(uniqKey);
539
+ 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;
537
540
  obj = keyBy(arr, keyFunction);
538
541
  }
539
542
  return obj;
@@ -580,7 +583,7 @@ var indexOfItemInArray = (arr, key, value, isPath) => {
580
583
  const item = arr[i];
581
584
  if (item == null) continue;
582
585
  const resolved = resolveProperty(item, key, isPath);
583
- if (resolved != null && String(resolved) === String(value)) {
586
+ if (resolved !== void 0 && String(resolved) === String(value)) {
584
587
  return i;
585
588
  }
586
589
  }
@@ -636,7 +639,7 @@ var applyArrayChange = (arr, change) => {
636
639
  } else {
637
640
  element = arr.find((el) => {
638
641
  const resolved = resolveProperty(el, change.embeddedKey, change.embeddedKeyIsPath);
639
- return resolved != null && String(resolved) === String(subchange.key);
642
+ return resolved !== void 0 && String(resolved) === String(subchange.key);
640
643
  });
641
644
  }
642
645
  if (element) {
@@ -706,7 +709,7 @@ var revertArrayChange = (arr, change) => {
706
709
  } else {
707
710
  element = arr.find((el) => {
708
711
  const resolved = resolveProperty(el, change.embeddedKey, change.embeddedKeyIsPath);
709
- return resolved != null && String(resolved) === String(subchange.key);
712
+ return resolved !== void 0 && String(resolved) === String(subchange.key);
710
713
  });
711
714
  }
712
715
  if (element) {
@@ -1354,14 +1357,16 @@ function walkChanges(changes, basePath, oldCtx, newCtx, ops, options) {
1354
1357
  const childOld = change.key === "$root" ? oldCtx : oldCtx?.[change.key];
1355
1358
  const childNew = change.key === "$root" ? newCtx : newCtx?.[change.key];
1356
1359
  if (change.embeddedKey) {
1357
- for (const childChange of change.changes) {
1360
+ const orderedChildChanges = orderArrayChildChanges(change.changes, change.embeddedKey);
1361
+ for (const childChange of orderedChildChanges) {
1358
1362
  const filterPath = buildCanonicalFilterPath(
1359
1363
  childPath,
1360
1364
  change.embeddedKey,
1361
1365
  childChange.key,
1362
1366
  childOld,
1363
1367
  childNew,
1364
- childChange
1368
+ childChange,
1369
+ change.embeddedKeyIsPath
1365
1370
  );
1366
1371
  if (childChange.changes) {
1367
1372
  const oldEl = findElement(childOld, change.embeddedKey, childChange.key);
@@ -1380,6 +1385,46 @@ function walkChanges(changes, basePath, oldCtx, newCtx, ops, options) {
1380
1385
  }
1381
1386
  }
1382
1387
  }
1388
+ function orderArrayChildChanges(changes, embeddedKey) {
1389
+ if (embeddedKey !== "$index") {
1390
+ return changes;
1391
+ }
1392
+ const groups = [];
1393
+ const pureRemoves = [];
1394
+ for (let i = 0; i < changes.length; i++) {
1395
+ const current = changes[i];
1396
+ const next = changes[i + 1];
1397
+ if (current.type === "REMOVE" /* REMOVE */ && next && next.type === "ADD" /* ADD */ && String(current.key) === String(next.key)) {
1398
+ groups.push({ kind: "preserved", changes: [current, next] });
1399
+ i++;
1400
+ continue;
1401
+ }
1402
+ if (current.type === "REMOVE" /* REMOVE */) {
1403
+ pureRemoves.push(current);
1404
+ groups.push({ kind: "pure-remove" });
1405
+ continue;
1406
+ }
1407
+ groups.push({ kind: "preserved", changes: [current] });
1408
+ }
1409
+ if (pureRemoves.length < 2) {
1410
+ return changes;
1411
+ }
1412
+ const removeIndices = pureRemoves.map((change) => Number(change.key));
1413
+ if (removeIndices.some((idx) => !Number.isInteger(idx))) {
1414
+ return changes;
1415
+ }
1416
+ pureRemoves.sort((a, b) => Number(b.key) - Number(a.key));
1417
+ const ordered = [];
1418
+ let removeIndex = 0;
1419
+ for (const group of groups) {
1420
+ if (group.kind === "pure-remove") {
1421
+ ordered.push(pureRemoves[removeIndex++]);
1422
+ } else {
1423
+ ordered.push(...group.changes);
1424
+ }
1425
+ }
1426
+ return ordered;
1427
+ }
1383
1428
  function emitLeafOp(change, path, ops, options) {
1384
1429
  switch (change.type) {
1385
1430
  case "ADD" /* ADD */: {
@@ -1404,7 +1449,7 @@ function emitLeafOp(change, path, ops, options) {
1404
1449
  }
1405
1450
  }
1406
1451
  }
1407
- function buildCanonicalFilterPath(basePath, embeddedKey, changeKey, oldArr, newArr, change) {
1452
+ function buildCanonicalFilterPath(basePath, embeddedKey, changeKey, oldArr, newArr, change, embeddedKeyIsPath) {
1408
1453
  if (embeddedKey === "$index") {
1409
1454
  return `${basePath}[${changeKey}]`;
1410
1455
  }
@@ -1424,9 +1469,11 @@ function buildCanonicalFilterPath(basePath, embeddedKey, changeKey, oldArr, newA
1424
1469
  const memberAccess2 = typeof keyName === "string" && SIMPLE_PROPERTY_RE2.test(keyName) ? `.${keyName}` : `.${changeKey}`;
1425
1470
  return `${basePath}[?(@${memberAccess2}=='${changeKey}')]`;
1426
1471
  }
1427
- const element = findElementByKey(oldArr, newArr, embeddedKey, changeKey, change.type);
1428
- const typedVal = element ? element[embeddedKey] : changeKey;
1429
- const memberAccess = SIMPLE_PROPERTY_RE2.test(embeddedKey) ? `.${embeddedKey}` : `['${embeddedKey.replace(/'/g, "''")}']`;
1472
+ const isNestedPath = embeddedKeyIsPath && NESTED_PATH_RE3.test(embeddedKey);
1473
+ const element = findElementByKey(oldArr, newArr, embeddedKey, changeKey, change.type, isNestedPath);
1474
+ const resolved = element !== void 0 ? resolveNestedKey(element, embeddedKey, !!isNestedPath) : void 0;
1475
+ const typedVal = resolved !== void 0 ? resolved : changeKey;
1476
+ const memberAccess = SIMPLE_PROPERTY_RE2.test(embeddedKey) || isNestedPath ? `.${embeddedKey}` : `['${embeddedKey.replace(/'/g, "''")}']`;
1430
1477
  return `${basePath}[?(@${memberAccess}==${formatFilterLiteral(typedVal)})]`;
1431
1478
  }
1432
1479
  function findActualValue(oldArr, newArr, stringKey, opType) {
@@ -1465,13 +1512,20 @@ function findElement(arr, embeddedKey, changeKey) {
1465
1512
  }
1466
1513
  return arr.find((item) => item && String(item[embeddedKey]) === changeKey);
1467
1514
  }
1468
- function findElementByKey(oldArr, newArr, embeddedKey, changeKey, opType) {
1515
+ function resolveNestedKey(item, key, isPath) {
1516
+ if (isPath) {
1517
+ return key.split(".").reduce((c, s) => c?.[s], item);
1518
+ }
1519
+ return item?.[key];
1520
+ }
1521
+ function findElementByKey(oldArr, newArr, embeddedKey, changeKey, opType, isPath) {
1522
+ const match = (item) => item && String(resolveNestedKey(item, embeddedKey, !!isPath)) === changeKey;
1469
1523
  if (opType === "REMOVE" /* REMOVE */ || opType === "UPDATE" /* UPDATE */) {
1470
- const el = oldArr?.find((item) => item && String(item[embeddedKey]) === changeKey);
1524
+ const el = oldArr?.find(match);
1471
1525
  if (el) return el;
1472
1526
  }
1473
1527
  if (opType === "ADD" /* ADD */ || opType === "UPDATE" /* UPDATE */) {
1474
- const el = newArr?.find((item) => item && String(item[embeddedKey]) === changeKey);
1528
+ const el = newArr?.find(match);
1475
1529
  if (el) return el;
1476
1530
  }
1477
1531
  return void 0;