document-model 1.7.0 → 2.0.0

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.
Files changed (59) hide show
  1. package/dist/browser/cjs/document-model.js +2 -2
  2. package/dist/browser/cjs/document.js +2 -2
  3. package/dist/browser/cjs/index.js +2 -2
  4. package/dist/browser/cjs/internal/{index-DTRXQ2Nf.js → index-UG6TQ_ad.js} +2 -2
  5. package/dist/browser/cjs/internal/index-UG6TQ_ad.js.map +1 -0
  6. package/dist/browser/cjs/internal/{index-BllCzBc9.js → index-jRdLtWv9.js} +3 -3
  7. package/dist/browser/cjs/internal/index-jRdLtWv9.js.map +1 -0
  8. package/dist/browser/cjs/internal/{object-Bf9_woMQ.js → object-CCclzskm.js} +480 -2109
  9. package/dist/browser/cjs/internal/object-CCclzskm.js.map +1 -0
  10. package/dist/browser/es/document-model.js +2 -2
  11. package/dist/browser/es/document.js +2 -2
  12. package/dist/browser/es/index.js +2 -2
  13. package/dist/browser/es/internal/{index-BAvDobTr.js → index-CDwXQBxG.js} +2 -2
  14. package/dist/browser/es/internal/index-CDwXQBxG.js.map +1 -0
  15. package/dist/browser/es/internal/{index-Db40bdYP.js → index-v4H3kbAF.js} +3 -3
  16. package/dist/browser/es/internal/index-v4H3kbAF.js.map +1 -0
  17. package/dist/browser/es/internal/{object-CY74acQg.js → object-CU5T1DpX.js} +491 -2120
  18. package/dist/browser/es/internal/object-CU5T1DpX.js.map +1 -0
  19. package/dist/browser/src/document/reducer.d.ts +4 -1
  20. package/dist/browser/src/document/types.d.ts +1 -0
  21. package/dist/browser/src/document/utils/base.d.ts +2 -2
  22. package/dist/browser/src/document/utils/document-helpers.d.ts +10 -0
  23. package/dist/browser/src/document-model/gen/schema/zod.d.ts +8 -8
  24. package/dist/node/cjs/document-model.js +2 -2
  25. package/dist/node/cjs/document.js +2 -2
  26. package/dist/node/cjs/index.js +2 -2
  27. package/dist/node/cjs/internal/{index-kI4cPPpE.js → index-5qN282Jw.js} +3 -3
  28. package/dist/node/cjs/internal/index-5qN282Jw.js.map +1 -0
  29. package/dist/node/cjs/internal/{index-aABa-Hb0.js → index-Bm-pIfaz.js} +2 -2
  30. package/dist/node/cjs/internal/index-Bm-pIfaz.js.map +1 -0
  31. package/dist/node/cjs/internal/{object-BMm0OLWL.js → object-BMOaYPkd.js} +247 -199
  32. package/dist/node/cjs/internal/object-BMOaYPkd.js.map +1 -0
  33. package/dist/node/es/document-model.js +2 -2
  34. package/dist/node/es/document.js +2 -2
  35. package/dist/node/es/index.js +2 -2
  36. package/dist/node/es/internal/{index-B0WPutmO.js → index-CAjAt1Xx.js} +2 -2
  37. package/dist/node/es/internal/index-CAjAt1Xx.js.map +1 -0
  38. package/dist/node/es/internal/{index-MkEgGMJR.js → index-CVuLZAmf.js} +3 -3
  39. package/dist/node/es/internal/index-CVuLZAmf.js.map +1 -0
  40. package/dist/node/es/internal/{object-VZ_AS47_.js → object-COSf2HUT.js} +258 -210
  41. package/dist/node/es/internal/object-COSf2HUT.js.map +1 -0
  42. package/dist/node/src/document/reducer.d.ts +4 -1
  43. package/dist/node/src/document/types.d.ts +1 -0
  44. package/dist/node/src/document/utils/base.d.ts +2 -2
  45. package/dist/node/src/document/utils/document-helpers.d.ts +10 -0
  46. package/dist/node/src/document-model/gen/schema/zod.d.ts +8 -8
  47. package/package.json +7 -7
  48. package/dist/browser/cjs/internal/index-BllCzBc9.js.map +0 -1
  49. package/dist/browser/cjs/internal/index-DTRXQ2Nf.js.map +0 -1
  50. package/dist/browser/cjs/internal/object-Bf9_woMQ.js.map +0 -1
  51. package/dist/browser/es/internal/index-BAvDobTr.js.map +0 -1
  52. package/dist/browser/es/internal/index-Db40bdYP.js.map +0 -1
  53. package/dist/browser/es/internal/object-CY74acQg.js.map +0 -1
  54. package/dist/node/cjs/internal/index-aABa-Hb0.js.map +0 -1
  55. package/dist/node/cjs/internal/index-kI4cPPpE.js.map +0 -1
  56. package/dist/node/cjs/internal/object-BMm0OLWL.js.map +0 -1
  57. package/dist/node/es/internal/index-B0WPutmO.js.map +0 -1
  58. package/dist/node/es/internal/index-MkEgGMJR.js.map +0 -1
  59. package/dist/node/es/internal/object-VZ_AS47_.js.map +0 -1
@@ -158,16 +158,16 @@ var safeStableStringify = { exports: {} };
158
158
  exports2.stringify = stringify;
159
159
  exports2.configure = configure;
160
160
  module2.exports = stringify;
161
- const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/;
161
+ const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]/;
162
162
  function strEscape(str) {
163
163
  if (str.length < 5e3 && !strEscapeSequencesRegExp.test(str)) {
164
164
  return `"${str}"`;
165
165
  }
166
166
  return JSON.stringify(str);
167
167
  }
168
- function insertSort(array) {
169
- if (array.length > 200) {
170
- return array.sort();
168
+ function sort(array, comparator) {
169
+ if (array.length > 200 || comparator) {
170
+ return array.sort(comparator);
171
171
  }
172
172
  for (let i = 1; i < array.length; i++) {
173
173
  const currentValue = array[i];
@@ -222,6 +222,16 @@ var safeStableStringify = { exports: {} };
222
222
  }
223
223
  return '"[Circular]"';
224
224
  }
225
+ function getDeterministicOption(options) {
226
+ let value;
227
+ if (hasOwnProperty.call(options, "deterministic")) {
228
+ value = options.deterministic;
229
+ if (typeof value !== "boolean" && typeof value !== "function") {
230
+ throw new TypeError('The "deterministic" argument must be of type boolean or comparator function');
231
+ }
232
+ }
233
+ return value === void 0 ? true : value;
234
+ }
225
235
  function getBooleanOption(options, key) {
226
236
  let value;
227
237
  if (hasOwnProperty.call(options, key)) {
@@ -291,7 +301,8 @@ var safeStableStringify = { exports: {} };
291
301
  }
292
302
  const circularValue = getCircularValueOption(options);
293
303
  const bigint = getBooleanOption(options, "bigint");
294
- const deterministic = getBooleanOption(options, "deterministic");
304
+ const deterministic = getDeterministicOption(options);
305
+ const comparator = typeof deterministic === "function" ? deterministic : void 0;
295
306
  const maximumDepth = getPositiveIntegerOption(options, "maximumDepth");
296
307
  const maximumBreadth = getPositiveIntegerOption(options, "maximumBreadth");
297
308
  function stringifyFnReplacer(key, parent, stack, replacer, spacer, indentation) {
@@ -366,7 +377,7 @@ ${indentation}`;
366
377
  }
367
378
  const maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
368
379
  if (deterministic && !isTypedArrayWithEntries(value)) {
369
- keys = insertSort(keys);
380
+ keys = sort(keys, comparator);
370
381
  }
371
382
  stack.push(value);
372
383
  for (let i = 0; i < maximumPropertiesToStringify; i++) {
@@ -567,7 +578,7 @@ ${indentation}`;
567
578
  separator = join;
568
579
  }
569
580
  if (deterministic) {
570
- keys = insertSort(keys);
581
+ keys = sort(keys, comparator);
571
582
  }
572
583
  stack.push(value);
573
584
  for (let i = 0; i < maximumPropertiesToStringify; i++) {
@@ -626,7 +637,8 @@ ${originalIndentation}`;
626
637
  return circularValue;
627
638
  }
628
639
  let res = "";
629
- if (Array.isArray(value)) {
640
+ const hasLength = value.length !== void 0;
641
+ if (hasLength && Array.isArray(value)) {
630
642
  if (value.length === 0) {
631
643
  return "[]";
632
644
  }
@@ -660,14 +672,14 @@ ${originalIndentation}`;
660
672
  }
661
673
  let separator = "";
662
674
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
663
- if (isTypedArrayWithEntries(value)) {
675
+ if (hasLength && isTypedArrayWithEntries(value)) {
664
676
  res += stringifyTypedArray(value, ",", maximumBreadth);
665
677
  keys = keys.slice(value.length);
666
678
  maximumPropertiesToStringify -= value.length;
667
679
  separator = ",";
668
680
  }
669
681
  if (deterministic) {
670
- keys = insertSort(keys);
682
+ keys = sort(keys, comparator);
671
683
  }
672
684
  stack.push(value);
673
685
  for (let i = 0; i < maximumPropertiesToStringify; i++) {
@@ -759,168 +771,6 @@ function v4(options, buf, offset) {
759
771
  rnds[8] = rnds[8] & 63 | 128;
760
772
  return unsafeStringify(rnds);
761
773
  }
762
- function setNameOperation(document, name) {
763
- return { ...document, name };
764
- }
765
- function undoOperation(document, action, skip) {
766
- const { scope, input } = action;
767
- const defaultResult = {
768
- document,
769
- action,
770
- skip
771
- };
772
- return mutative.create(defaultResult, (draft) => {
773
- if (draft.document.operations[scope].length < 1) {
774
- throw new Error(
775
- `Cannot undo: no operations in history for scope "${scope}"`
776
- );
777
- }
778
- if (input < 1) {
779
- throw new Error(
780
- `Invalid UNDO action: input value must be greater than 0`
781
- );
782
- }
783
- if (draft.skip > 0) {
784
- throw new Error(
785
- `Cannot undo: skip value from reducer cannot be used with UNDO action`
786
- );
787
- }
788
- const lastOperation = draft.document.operations[scope].at(-1);
789
- const isLatestOpNOOP = lastOperation && lastOperation.type === "NOOP" && lastOperation.skip > 0;
790
- draft.skip += input;
791
- if (isLatestOpNOOP) {
792
- draft.skip += lastOperation.skip;
793
- const preLastOperation = draft.document.operations[scope][draft.document.operations[scope].length - 2];
794
- if (preLastOperation && lastOperation.index - preLastOperation.index === 1) {
795
- draft.document.operations[scope].pop();
796
- }
797
- }
798
- if (draft.document.operations[scope].length < draft.skip) {
799
- throw new Error(
800
- `Cannot undo: you can't undo more operations than the ones in the scope history`
801
- );
802
- }
803
- const operationsLastIndex = draft.document.operations[scope].length - 1;
804
- let skippedOpsLeft = input;
805
- let index = isLatestOpNOOP ? operationsLastIndex - lastOperation.skip : operationsLastIndex;
806
- while (skippedOpsLeft > 0 && index >= 0) {
807
- const op = draft.document.operations[scope][index];
808
- if (!op) {
809
- skippedOpsLeft--;
810
- index--;
811
- continue;
812
- }
813
- if (op.type === "NOOP" && op.skip > 0) {
814
- index = index - (op.skip + 1);
815
- draft.skip += op.skip + 1;
816
- } else {
817
- draft.document.clipboard.push({ ...op });
818
- skippedOpsLeft--;
819
- index--;
820
- }
821
- }
822
- draft.action = noop(scope);
823
- });
824
- }
825
- function redoOperation(document, action, skip) {
826
- const { scope, input } = action;
827
- const defaultResult = {
828
- document,
829
- action,
830
- skip
831
- };
832
- return mutative.create(defaultResult, (draft) => {
833
- if (draft.skip > 0) {
834
- throw new Error(
835
- `Cannot redo: skip value from reducer cannot be used with REDO action`
836
- );
837
- }
838
- if (input > 1) {
839
- throw new Error(
840
- `Cannot redo: you can only redo one operation at a time`
841
- );
842
- }
843
- if (input < 1) {
844
- throw new Error(`Invalid REDO action: invalid redo input value`);
845
- }
846
- if (draft.document.clipboard.length < 1) {
847
- throw new Error(`Cannot redo: no operations in the clipboard`);
848
- }
849
- const operationIndex = draft.document.clipboard.findLastIndex(
850
- (op) => op.scope === scope
851
- );
852
- if (operationIndex < 0) {
853
- throw new Error(
854
- `Cannot redo: no operations in clipboard for scope "${scope}"`
855
- );
856
- }
857
- const operation = draft.document.clipboard.splice(operationIndex, 1)[0];
858
- draft.action = mutative.castDraft({
859
- type: operation.type,
860
- scope: operation.scope,
861
- input: operation.input
862
- });
863
- });
864
- }
865
- function pruneOperation(document, action, wrappedReducer) {
866
- const { scope } = action;
867
- const operations = document.operations[scope];
868
- let {
869
- input: { start, end }
870
- } = action;
871
- start = start || 0;
872
- end = end || operations.length;
873
- const actionsToPrune = operations.slice(start, end);
874
- const actionsToKeepStart = operations.slice(0, start);
875
- const actionsToKeepEnd = operations.slice(end);
876
- const newDocument = replayOperations(
877
- document.initialState,
878
- {
879
- ...document.operations,
880
- [scope]: actionsToKeepStart.concat(actionsToPrune)
881
- },
882
- wrappedReducer
883
- );
884
- const { name, state: newState } = newDocument;
885
- const loadStateIndex = actionsToKeepStart.length;
886
- const loadStateTimestamp = actionsToKeepStart.length ? actionsToKeepStart[actionsToKeepStart.length - 1].timestamp : actionsToKeepEnd.length ? actionsToKeepEnd[0].timestamp : (/* @__PURE__ */ new Date()).toISOString();
887
- return replayOperations(
888
- document.initialState,
889
- {
890
- ...document.operations,
891
- [scope]: [
892
- ...actionsToKeepStart,
893
- {
894
- ...loadState(
895
- { name, state: newState },
896
- actionsToPrune.length
897
- ),
898
- timestamp: loadStateTimestamp,
899
- index: loadStateIndex,
900
- hash: hashDocument({ state: newState }, "global")
901
- },
902
- ...actionsToKeepEnd.map((action2, index) => ({
903
- ...action2,
904
- index: loadStateIndex + index + 1
905
- }))
906
- ]
907
- },
908
- wrappedReducer
909
- );
910
- }
911
- function loadStateOperation(oldDocument, newDocument) {
912
- return {
913
- ...oldDocument,
914
- name: newDocument.name,
915
- state: newDocument.state ?? { global: {}, local: {} }
916
- };
917
- }
918
- const SET_NAME = "SET_NAME";
919
- const UNDO = "UNDO";
920
- const REDO = "REDO";
921
- const PRUNE = "PRUNE";
922
- const LOAD_STATE = "LOAD_STATE";
923
- const NOOP = "NOOP";
924
774
  function writeFile(path$1, name, data) {
925
775
  const filePath = path.join(path$1, name);
926
776
  fs.mkdirSync(path$1, { recursive: true });
@@ -1487,6 +1337,13 @@ function filterDocumentOperationsResultingState(documentOperations) {
1487
1337
  {}
1488
1338
  );
1489
1339
  }
1340
+ function diffOperations(clearedOperationsA, clearedOperationsB) {
1341
+ return clearedOperationsA.filter(
1342
+ (operationA) => !clearedOperationsB.some(
1343
+ (operationB) => operationA.index === operationB.index
1344
+ )
1345
+ );
1346
+ }
1490
1347
  const documentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1491
1348
  __proto__: null,
1492
1349
  IntegrityIssueSubType,
@@ -1495,6 +1352,7 @@ const documentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
1495
1352
  attachBranch,
1496
1353
  checkCleanedOperationsIntegrity,
1497
1354
  checkOperationsIntegrity,
1355
+ diffOperations,
1498
1356
  filterDocumentOperationsResultingState,
1499
1357
  filterDuplicatedOperations,
1500
1358
  garbageCollect,
@@ -1643,6 +1501,141 @@ async function getLocalFile(path2) {
1643
1501
  const data = buffer.toString("base64");
1644
1502
  return { data, hash: hash(data), mimeType, ...attributes };
1645
1503
  }
1504
+ function setNameOperation(document, name) {
1505
+ return { ...document, name };
1506
+ }
1507
+ function undoOperation(document, action, skip) {
1508
+ const { scope, input } = action;
1509
+ const defaultResult = {
1510
+ document,
1511
+ action,
1512
+ skip,
1513
+ reuseLastOperationIndex: false
1514
+ };
1515
+ return mutative.create(defaultResult, (draft) => {
1516
+ const operations = [...document.operations[scope]];
1517
+ const sortedOperations = sortOperations$1(operations);
1518
+ draft.action = noop(scope);
1519
+ const lastOperation = sortedOperations.at(-1);
1520
+ let nextIndex = (lastOperation == null ? void 0 : lastOperation.index) ?? -1;
1521
+ const isNewNoop = (lastOperation == null ? void 0 : lastOperation.type) !== "NOOP";
1522
+ if (isNewNoop) {
1523
+ nextIndex = nextIndex + 1;
1524
+ } else {
1525
+ draft.reuseLastOperationIndex = true;
1526
+ }
1527
+ const nextOperationHistory = isNewNoop ? [...sortedOperations, { index: nextIndex, skip: 0 }] : sortedOperations;
1528
+ draft.skip = nextSkipNumber(nextOperationHistory);
1529
+ if (lastOperation && draft.skip > lastOperation.skip + 1) {
1530
+ draft.skip = draft.skip + 1;
1531
+ }
1532
+ if (draft.skip < 0) {
1533
+ throw new Error(
1534
+ `Cannot undo: you can't undo more operations than the ones in the scope history`
1535
+ );
1536
+ }
1537
+ });
1538
+ }
1539
+ function redoOperation(document, action, skip) {
1540
+ const { scope, input } = action;
1541
+ const defaultResult = {
1542
+ document,
1543
+ action,
1544
+ skip,
1545
+ reuseLastOperationIndex: false
1546
+ };
1547
+ return mutative.create(defaultResult, (draft) => {
1548
+ if (draft.skip > 0) {
1549
+ throw new Error(
1550
+ `Cannot redo: skip value from reducer cannot be used with REDO action`
1551
+ );
1552
+ }
1553
+ if (input > 1) {
1554
+ throw new Error(
1555
+ `Cannot redo: you can only redo one operation at a time`
1556
+ );
1557
+ }
1558
+ if (input < 1) {
1559
+ throw new Error(`Invalid REDO action: invalid redo input value`);
1560
+ }
1561
+ if (draft.document.clipboard.length < 1) {
1562
+ throw new Error(`Cannot redo: no operations in the clipboard`);
1563
+ }
1564
+ const operationIndex = draft.document.clipboard.findLastIndex(
1565
+ (op) => op.scope === scope
1566
+ );
1567
+ if (operationIndex < 0) {
1568
+ throw new Error(
1569
+ `Cannot redo: no operations in clipboard for scope "${scope}"`
1570
+ );
1571
+ }
1572
+ const operation = draft.document.clipboard.splice(operationIndex, 1)[0];
1573
+ draft.action = mutative.castDraft({
1574
+ type: operation.type,
1575
+ scope: operation.scope,
1576
+ input: operation.input
1577
+ });
1578
+ });
1579
+ }
1580
+ function pruneOperation(document, action, wrappedReducer) {
1581
+ const { scope } = action;
1582
+ const operations = document.operations[scope];
1583
+ let {
1584
+ input: { start, end }
1585
+ } = action;
1586
+ start = start || 0;
1587
+ end = end || operations.length;
1588
+ const actionsToPrune = operations.slice(start, end);
1589
+ const actionsToKeepStart = operations.slice(0, start);
1590
+ const actionsToKeepEnd = operations.slice(end);
1591
+ const newDocument = replayOperations(
1592
+ document.initialState,
1593
+ {
1594
+ ...document.operations,
1595
+ [scope]: actionsToKeepStart.concat(actionsToPrune)
1596
+ },
1597
+ wrappedReducer
1598
+ );
1599
+ const { name, state: newState } = newDocument;
1600
+ const loadStateIndex = actionsToKeepStart.length;
1601
+ const loadStateTimestamp = actionsToKeepStart.length ? actionsToKeepStart[actionsToKeepStart.length - 1].timestamp : actionsToKeepEnd.length ? actionsToKeepEnd[0].timestamp : (/* @__PURE__ */ new Date()).toISOString();
1602
+ return replayOperations(
1603
+ document.initialState,
1604
+ {
1605
+ ...document.operations,
1606
+ [scope]: [
1607
+ ...actionsToKeepStart,
1608
+ {
1609
+ ...loadState(
1610
+ { name, state: newState },
1611
+ actionsToPrune.length
1612
+ ),
1613
+ timestamp: loadStateTimestamp,
1614
+ index: loadStateIndex,
1615
+ hash: hashDocument({ state: newState }, "global")
1616
+ },
1617
+ ...actionsToKeepEnd.map((action2, index) => ({
1618
+ ...action2,
1619
+ index: loadStateIndex + index + 1
1620
+ }))
1621
+ ]
1622
+ },
1623
+ wrappedReducer
1624
+ );
1625
+ }
1626
+ function loadStateOperation(oldDocument, newDocument) {
1627
+ return {
1628
+ ...oldDocument,
1629
+ name: newDocument.name,
1630
+ state: newDocument.state ?? { global: {}, local: {} }
1631
+ };
1632
+ }
1633
+ const SET_NAME = "SET_NAME";
1634
+ const UNDO = "UNDO";
1635
+ const REDO = "REDO";
1636
+ const PRUNE = "PRUNE";
1637
+ const LOAD_STATE = "LOAD_STATE";
1638
+ const NOOP = "NOOP";
1646
1639
  function getNextRevision(document, action) {
1647
1640
  let latestOperation;
1648
1641
  if ("index" in action) {
@@ -1662,7 +1655,7 @@ function updateHeader(document, action) {
1662
1655
  lastModified: (/* @__PURE__ */ new Date()).toISOString()
1663
1656
  };
1664
1657
  }
1665
- function updateOperations(document, action, skip = 0) {
1658
+ function updateOperations(document, action, skip = 0, reuseLastOperationIndex = false) {
1666
1659
  if ([UNDO, REDO, PRUNE].includes(action.type)) {
1667
1660
  return document;
1668
1661
  }
@@ -1670,7 +1663,8 @@ function updateOperations(document, action, skip = 0) {
1670
1663
  const operations = document.operations[scope].slice();
1671
1664
  let operationId;
1672
1665
  const latestOperation = operations.at(-1);
1673
- let nextIndex = ((latestOperation == null ? void 0 : latestOperation.index) ?? -1) + 1;
1666
+ const lastOperationIndex = (latestOperation == null ? void 0 : latestOperation.index) ?? -1;
1667
+ let nextIndex = reuseLastOperationIndex ? lastOperationIndex : lastOperationIndex + 1;
1674
1668
  if ("index" in action) {
1675
1669
  if (action.index - skip > nextIndex) {
1676
1670
  throw new Error(
@@ -1697,8 +1691,13 @@ function updateOperations(document, action, skip = 0) {
1697
1691
  operations: { ...document.operations, [scope]: operations }
1698
1692
  };
1699
1693
  }
1700
- function updateDocument(document, action, skip = 0) {
1701
- let newDocument = updateOperations(document, action, skip);
1694
+ function updateDocument(document, action, skip = 0, reuseLastOperationIndex = false) {
1695
+ let newDocument = updateOperations(
1696
+ document,
1697
+ action,
1698
+ skip,
1699
+ reuseLastOperationIndex
1700
+ );
1702
1701
  newDocument = updateHeader(newDocument, action);
1703
1702
  return newDocument;
1704
1703
  }
@@ -1722,7 +1721,7 @@ function processUndoRedo(document, action, skip) {
1722
1721
  case REDO:
1723
1722
  return redoOperation(document, action, skip);
1724
1723
  default:
1725
- return { document, action, skip };
1724
+ return { document, action, skip, reuseLastOperationIndex: false };
1726
1725
  }
1727
1726
  }
1728
1727
  function processSkipOperation(document, action, customReducer, skipValue, reuseOperationResultingState = false, resultingStateParser = parseResultingState) {
@@ -1772,6 +1771,37 @@ function processSkipOperation(document, action, customReducer, skipValue, reuseO
1772
1771
  })
1773
1772
  };
1774
1773
  }
1774
+ function processUndoOperation(document, scope, customReducer, reuseOperationResultingState = false, resultingStateParser = parseResultingState) {
1775
+ const operations = [...document.operations[scope]];
1776
+ const sortedOperations = sortOperations$1(operations);
1777
+ sortedOperations.pop();
1778
+ const documentOperations = garbageCollectDocumentOperations(
1779
+ { ...document.operations }
1780
+ );
1781
+ const clearedOperations = [...documentOperations[scope]];
1782
+ const diff = diffOperations(
1783
+ garbageCollect(sortedOperations),
1784
+ clearedOperations
1785
+ );
1786
+ const doc = replayOperations(
1787
+ document.initialState,
1788
+ documentOperations,
1789
+ customReducer,
1790
+ void 0,
1791
+ void 0,
1792
+ void 0,
1793
+ void 0,
1794
+ {
1795
+ reuseHash: true,
1796
+ reuseOperationResultingState,
1797
+ operationResultingStateParser: resultingStateParser
1798
+ }
1799
+ );
1800
+ const clipboard = sortOperations$1(
1801
+ [...document.clipboard, ...diff].filter((op) => op.type !== "NOOP")
1802
+ ).reverse();
1803
+ return { ...doc, clipboard };
1804
+ }
1775
1805
  function baseReducer(document, action, customReducer, dispatch, options = {}) {
1776
1806
  const {
1777
1807
  skip,
@@ -1780,14 +1810,46 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
1780
1810
  reuseOperationResultingState = false,
1781
1811
  operationResultingStateParser
1782
1812
  } = options;
1783
- const _action = { ...action };
1784
- const skipValue = skip || 0;
1813
+ let _action = { ...action };
1814
+ let skipValue = skip || 0;
1785
1815
  let newDocument = { ...document };
1816
+ let reuseLastOperationIndex = false;
1786
1817
  const shouldProcessSkipOperation = !ignoreSkipOperations && (skipValue > 0 || "index" in _action && _action.skip > 0);
1818
+ if (isUndoRedo(_action)) {
1819
+ const {
1820
+ skip: calculatedSkip,
1821
+ action: transformedAction,
1822
+ document: processedDocument,
1823
+ reuseLastOperationIndex: reuseIndex
1824
+ } = processUndoRedo(document, _action, skipValue);
1825
+ _action = transformedAction;
1826
+ skipValue = calculatedSkip;
1827
+ newDocument = processedDocument;
1828
+ reuseLastOperationIndex = reuseIndex;
1829
+ } else {
1830
+ newDocument = {
1831
+ ...newDocument,
1832
+ clipboard: []
1833
+ };
1834
+ }
1787
1835
  if (isBaseAction(_action)) {
1788
1836
  newDocument = _baseReducer(newDocument, _action, customReducer);
1789
1837
  }
1790
- newDocument = updateDocument(newDocument, _action, skipValue);
1838
+ newDocument = updateDocument(
1839
+ newDocument,
1840
+ _action,
1841
+ skipValue,
1842
+ reuseLastOperationIndex
1843
+ );
1844
+ const isUndoAction = isUndo(action);
1845
+ if (isUndoAction) {
1846
+ const result = processUndoOperation(
1847
+ newDocument,
1848
+ action.scope,
1849
+ customReducer
1850
+ );
1851
+ return result;
1852
+ }
1791
1853
  if (shouldProcessSkipOperation) {
1792
1854
  newDocument = processSkipOperation(
1793
1855
  newDocument,
@@ -1927,6 +1989,9 @@ function isNoopOperation(op) {
1927
1989
  function isUndoRedo(action) {
1928
1990
  return [UNDO, REDO].includes(action.type);
1929
1991
  }
1992
+ function isUndo(action) {
1993
+ return action.type === UNDO;
1994
+ }
1930
1995
  function isBaseAction(action) {
1931
1996
  return [SET_NAME, UNDO, REDO, PRUNE, LOAD_STATE].includes(action.type);
1932
1997
  }
@@ -2022,23 +2087,6 @@ function mapSkippedOperations(operations, skippedHeadOperations) {
2022
2087
  }
2023
2088
  return scopeOpsWithIgnore.reverse();
2024
2089
  }
2025
- function calculateSkipsLeft(operations, currentIndex, skip) {
2026
- const sortedOperations = operations.slice().sort((a, b) => a.skip - b.skip).sort((a, b) => a.index - b.index);
2027
- let skipsLeft = skip;
2028
- let skipsToPerform = 0;
2029
- let lastIndex = currentIndex;
2030
- for (const operation of sortedOperations.reverse()) {
2031
- const distance = lastIndex - operation.index;
2032
- skipsLeft = skipsLeft - distance;
2033
- if (skipsLeft > -1) {
2034
- skipsToPerform++;
2035
- lastIndex = operation.index;
2036
- } else {
2037
- break;
2038
- }
2039
- }
2040
- return skipsToPerform;
2041
- }
2042
2090
  function sortOperations(operations) {
2043
2091
  return Object.values(operations).flatMap((array) => array).sort(
2044
2092
  (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
@@ -2412,7 +2460,6 @@ exports.buildOperationSignature = buildOperationSignature;
2412
2460
  exports.buildOperationSignatureMessage = buildOperationSignatureMessage;
2413
2461
  exports.buildOperationSignatureParams = buildOperationSignatureParams;
2414
2462
  exports.buildSignedOperation = buildSignedOperation;
2415
- exports.calculateSkipsLeft = calculateSkipsLeft;
2416
2463
  exports.createAction = createAction;
2417
2464
  exports.createDocument = createDocument;
2418
2465
  exports.createExtendedState = createExtendedState;
@@ -2429,6 +2476,7 @@ exports.hex2ab = hex2ab;
2429
2476
  exports.isBaseAction = isBaseAction;
2430
2477
  exports.isNoopOperation = isNoopOperation;
2431
2478
  exports.isSameDocument = isSameDocument;
2479
+ exports.isUndo = isUndo;
2432
2480
  exports.isUndoRedo = isUndoRedo;
2433
2481
  exports.loadFromFile = loadFromFile;
2434
2482
  exports.loadFromInput = loadFromInput;
@@ -2448,4 +2496,4 @@ exports.updateHeader = updateHeader;
2448
2496
  exports.validateOperations = validateOperations;
2449
2497
  exports.verifyOperationSignature = verifyOperationSignature;
2450
2498
  exports.zod = zod;
2451
- //# sourceMappingURL=object-BMm0OLWL.js.map
2499
+ //# sourceMappingURL=object-BMOaYPkd.js.map