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
@@ -157,16 +157,16 @@ var safeStableStringify = { exports: {} };
157
157
  exports.stringify = stringify;
158
158
  exports.configure = configure;
159
159
  module.exports = stringify;
160
- const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/;
160
+ const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]/;
161
161
  function strEscape(str) {
162
162
  if (str.length < 5e3 && !strEscapeSequencesRegExp.test(str)) {
163
163
  return `"${str}"`;
164
164
  }
165
165
  return JSON.stringify(str);
166
166
  }
167
- function insertSort(array) {
168
- if (array.length > 200) {
169
- return array.sort();
167
+ function sort(array, comparator) {
168
+ if (array.length > 200 || comparator) {
169
+ return array.sort(comparator);
170
170
  }
171
171
  for (let i = 1; i < array.length; i++) {
172
172
  const currentValue = array[i];
@@ -221,6 +221,16 @@ var safeStableStringify = { exports: {} };
221
221
  }
222
222
  return '"[Circular]"';
223
223
  }
224
+ function getDeterministicOption(options) {
225
+ let value;
226
+ if (hasOwnProperty.call(options, "deterministic")) {
227
+ value = options.deterministic;
228
+ if (typeof value !== "boolean" && typeof value !== "function") {
229
+ throw new TypeError('The "deterministic" argument must be of type boolean or comparator function');
230
+ }
231
+ }
232
+ return value === void 0 ? true : value;
233
+ }
224
234
  function getBooleanOption(options, key) {
225
235
  let value;
226
236
  if (hasOwnProperty.call(options, key)) {
@@ -290,7 +300,8 @@ var safeStableStringify = { exports: {} };
290
300
  }
291
301
  const circularValue = getCircularValueOption(options);
292
302
  const bigint = getBooleanOption(options, "bigint");
293
- const deterministic = getBooleanOption(options, "deterministic");
303
+ const deterministic = getDeterministicOption(options);
304
+ const comparator = typeof deterministic === "function" ? deterministic : void 0;
294
305
  const maximumDepth = getPositiveIntegerOption(options, "maximumDepth");
295
306
  const maximumBreadth = getPositiveIntegerOption(options, "maximumBreadth");
296
307
  function stringifyFnReplacer(key, parent, stack, replacer, spacer, indentation) {
@@ -365,7 +376,7 @@ ${indentation}`;
365
376
  }
366
377
  const maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
367
378
  if (deterministic && !isTypedArrayWithEntries(value)) {
368
- keys = insertSort(keys);
379
+ keys = sort(keys, comparator);
369
380
  }
370
381
  stack.push(value);
371
382
  for (let i = 0; i < maximumPropertiesToStringify; i++) {
@@ -566,7 +577,7 @@ ${indentation}`;
566
577
  separator = join2;
567
578
  }
568
579
  if (deterministic) {
569
- keys = insertSort(keys);
580
+ keys = sort(keys, comparator);
570
581
  }
571
582
  stack.push(value);
572
583
  for (let i = 0; i < maximumPropertiesToStringify; i++) {
@@ -625,7 +636,8 @@ ${originalIndentation}`;
625
636
  return circularValue;
626
637
  }
627
638
  let res = "";
628
- if (Array.isArray(value)) {
639
+ const hasLength = value.length !== void 0;
640
+ if (hasLength && Array.isArray(value)) {
629
641
  if (value.length === 0) {
630
642
  return "[]";
631
643
  }
@@ -659,14 +671,14 @@ ${originalIndentation}`;
659
671
  }
660
672
  let separator = "";
661
673
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
662
- if (isTypedArrayWithEntries(value)) {
674
+ if (hasLength && isTypedArrayWithEntries(value)) {
663
675
  res += stringifyTypedArray(value, ",", maximumBreadth);
664
676
  keys = keys.slice(value.length);
665
677
  maximumPropertiesToStringify -= value.length;
666
678
  separator = ",";
667
679
  }
668
680
  if (deterministic) {
669
- keys = insertSort(keys);
681
+ keys = sort(keys, comparator);
670
682
  }
671
683
  stack.push(value);
672
684
  for (let i = 0; i < maximumPropertiesToStringify; i++) {
@@ -758,168 +770,6 @@ function v4(options, buf, offset) {
758
770
  rnds[8] = rnds[8] & 63 | 128;
759
771
  return unsafeStringify(rnds);
760
772
  }
761
- function setNameOperation(document, name) {
762
- return { ...document, name };
763
- }
764
- function undoOperation(document, action, skip) {
765
- const { scope, input } = action;
766
- const defaultResult = {
767
- document,
768
- action,
769
- skip
770
- };
771
- return create(defaultResult, (draft) => {
772
- if (draft.document.operations[scope].length < 1) {
773
- throw new Error(
774
- `Cannot undo: no operations in history for scope "${scope}"`
775
- );
776
- }
777
- if (input < 1) {
778
- throw new Error(
779
- `Invalid UNDO action: input value must be greater than 0`
780
- );
781
- }
782
- if (draft.skip > 0) {
783
- throw new Error(
784
- `Cannot undo: skip value from reducer cannot be used with UNDO action`
785
- );
786
- }
787
- const lastOperation = draft.document.operations[scope].at(-1);
788
- const isLatestOpNOOP = lastOperation && lastOperation.type === "NOOP" && lastOperation.skip > 0;
789
- draft.skip += input;
790
- if (isLatestOpNOOP) {
791
- draft.skip += lastOperation.skip;
792
- const preLastOperation = draft.document.operations[scope][draft.document.operations[scope].length - 2];
793
- if (preLastOperation && lastOperation.index - preLastOperation.index === 1) {
794
- draft.document.operations[scope].pop();
795
- }
796
- }
797
- if (draft.document.operations[scope].length < draft.skip) {
798
- throw new Error(
799
- `Cannot undo: you can't undo more operations than the ones in the scope history`
800
- );
801
- }
802
- const operationsLastIndex = draft.document.operations[scope].length - 1;
803
- let skippedOpsLeft = input;
804
- let index = isLatestOpNOOP ? operationsLastIndex - lastOperation.skip : operationsLastIndex;
805
- while (skippedOpsLeft > 0 && index >= 0) {
806
- const op = draft.document.operations[scope][index];
807
- if (!op) {
808
- skippedOpsLeft--;
809
- index--;
810
- continue;
811
- }
812
- if (op.type === "NOOP" && op.skip > 0) {
813
- index = index - (op.skip + 1);
814
- draft.skip += op.skip + 1;
815
- } else {
816
- draft.document.clipboard.push({ ...op });
817
- skippedOpsLeft--;
818
- index--;
819
- }
820
- }
821
- draft.action = noop(scope);
822
- });
823
- }
824
- function redoOperation(document, action, skip) {
825
- const { scope, input } = action;
826
- const defaultResult = {
827
- document,
828
- action,
829
- skip
830
- };
831
- return create(defaultResult, (draft) => {
832
- if (draft.skip > 0) {
833
- throw new Error(
834
- `Cannot redo: skip value from reducer cannot be used with REDO action`
835
- );
836
- }
837
- if (input > 1) {
838
- throw new Error(
839
- `Cannot redo: you can only redo one operation at a time`
840
- );
841
- }
842
- if (input < 1) {
843
- throw new Error(`Invalid REDO action: invalid redo input value`);
844
- }
845
- if (draft.document.clipboard.length < 1) {
846
- throw new Error(`Cannot redo: no operations in the clipboard`);
847
- }
848
- const operationIndex = draft.document.clipboard.findLastIndex(
849
- (op) => op.scope === scope
850
- );
851
- if (operationIndex < 0) {
852
- throw new Error(
853
- `Cannot redo: no operations in clipboard for scope "${scope}"`
854
- );
855
- }
856
- const operation = draft.document.clipboard.splice(operationIndex, 1)[0];
857
- draft.action = castDraft({
858
- type: operation.type,
859
- scope: operation.scope,
860
- input: operation.input
861
- });
862
- });
863
- }
864
- function pruneOperation(document, action, wrappedReducer) {
865
- const { scope } = action;
866
- const operations = document.operations[scope];
867
- let {
868
- input: { start, end }
869
- } = action;
870
- start = start || 0;
871
- end = end || operations.length;
872
- const actionsToPrune = operations.slice(start, end);
873
- const actionsToKeepStart = operations.slice(0, start);
874
- const actionsToKeepEnd = operations.slice(end);
875
- const newDocument = replayOperations(
876
- document.initialState,
877
- {
878
- ...document.operations,
879
- [scope]: actionsToKeepStart.concat(actionsToPrune)
880
- },
881
- wrappedReducer
882
- );
883
- const { name, state: newState } = newDocument;
884
- const loadStateIndex = actionsToKeepStart.length;
885
- const loadStateTimestamp = actionsToKeepStart.length ? actionsToKeepStart[actionsToKeepStart.length - 1].timestamp : actionsToKeepEnd.length ? actionsToKeepEnd[0].timestamp : (/* @__PURE__ */ new Date()).toISOString();
886
- return replayOperations(
887
- document.initialState,
888
- {
889
- ...document.operations,
890
- [scope]: [
891
- ...actionsToKeepStart,
892
- {
893
- ...loadState(
894
- { name, state: newState },
895
- actionsToPrune.length
896
- ),
897
- timestamp: loadStateTimestamp,
898
- index: loadStateIndex,
899
- hash: hashDocument({ state: newState }, "global")
900
- },
901
- ...actionsToKeepEnd.map((action2, index) => ({
902
- ...action2,
903
- index: loadStateIndex + index + 1
904
- }))
905
- ]
906
- },
907
- wrappedReducer
908
- );
909
- }
910
- function loadStateOperation(oldDocument, newDocument) {
911
- return {
912
- ...oldDocument,
913
- name: newDocument.name,
914
- state: newDocument.state ?? { global: {}, local: {} }
915
- };
916
- }
917
- const SET_NAME = "SET_NAME";
918
- const UNDO = "UNDO";
919
- const REDO = "REDO";
920
- const PRUNE = "PRUNE";
921
- const LOAD_STATE = "LOAD_STATE";
922
- const NOOP = "NOOP";
923
773
  function writeFile(path, name, data) {
924
774
  const filePath = join(path, name);
925
775
  fs.mkdirSync(path, { recursive: true });
@@ -1486,6 +1336,13 @@ function filterDocumentOperationsResultingState(documentOperations) {
1486
1336
  {}
1487
1337
  );
1488
1338
  }
1339
+ function diffOperations(clearedOperationsA, clearedOperationsB) {
1340
+ return clearedOperationsA.filter(
1341
+ (operationA) => !clearedOperationsB.some(
1342
+ (operationB) => operationA.index === operationB.index
1343
+ )
1344
+ );
1345
+ }
1489
1346
  const documentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1490
1347
  __proto__: null,
1491
1348
  IntegrityIssueSubType,
@@ -1494,6 +1351,7 @@ const documentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
1494
1351
  attachBranch,
1495
1352
  checkCleanedOperationsIntegrity,
1496
1353
  checkOperationsIntegrity,
1354
+ diffOperations,
1497
1355
  filterDocumentOperationsResultingState,
1498
1356
  filterDuplicatedOperations,
1499
1357
  garbageCollect,
@@ -1642,6 +1500,141 @@ async function getLocalFile(path) {
1642
1500
  const data = buffer.toString("base64");
1643
1501
  return { data, hash: hash(data), mimeType, ...attributes };
1644
1502
  }
1503
+ function setNameOperation(document, name) {
1504
+ return { ...document, name };
1505
+ }
1506
+ function undoOperation(document, action, skip) {
1507
+ const { scope, input } = action;
1508
+ const defaultResult = {
1509
+ document,
1510
+ action,
1511
+ skip,
1512
+ reuseLastOperationIndex: false
1513
+ };
1514
+ return create(defaultResult, (draft) => {
1515
+ const operations = [...document.operations[scope]];
1516
+ const sortedOperations = sortOperations$1(operations);
1517
+ draft.action = noop(scope);
1518
+ const lastOperation = sortedOperations.at(-1);
1519
+ let nextIndex = (lastOperation == null ? void 0 : lastOperation.index) ?? -1;
1520
+ const isNewNoop = (lastOperation == null ? void 0 : lastOperation.type) !== "NOOP";
1521
+ if (isNewNoop) {
1522
+ nextIndex = nextIndex + 1;
1523
+ } else {
1524
+ draft.reuseLastOperationIndex = true;
1525
+ }
1526
+ const nextOperationHistory = isNewNoop ? [...sortedOperations, { index: nextIndex, skip: 0 }] : sortedOperations;
1527
+ draft.skip = nextSkipNumber(nextOperationHistory);
1528
+ if (lastOperation && draft.skip > lastOperation.skip + 1) {
1529
+ draft.skip = draft.skip + 1;
1530
+ }
1531
+ if (draft.skip < 0) {
1532
+ throw new Error(
1533
+ `Cannot undo: you can't undo more operations than the ones in the scope history`
1534
+ );
1535
+ }
1536
+ });
1537
+ }
1538
+ function redoOperation(document, action, skip) {
1539
+ const { scope, input } = action;
1540
+ const defaultResult = {
1541
+ document,
1542
+ action,
1543
+ skip,
1544
+ reuseLastOperationIndex: false
1545
+ };
1546
+ return create(defaultResult, (draft) => {
1547
+ if (draft.skip > 0) {
1548
+ throw new Error(
1549
+ `Cannot redo: skip value from reducer cannot be used with REDO action`
1550
+ );
1551
+ }
1552
+ if (input > 1) {
1553
+ throw new Error(
1554
+ `Cannot redo: you can only redo one operation at a time`
1555
+ );
1556
+ }
1557
+ if (input < 1) {
1558
+ throw new Error(`Invalid REDO action: invalid redo input value`);
1559
+ }
1560
+ if (draft.document.clipboard.length < 1) {
1561
+ throw new Error(`Cannot redo: no operations in the clipboard`);
1562
+ }
1563
+ const operationIndex = draft.document.clipboard.findLastIndex(
1564
+ (op) => op.scope === scope
1565
+ );
1566
+ if (operationIndex < 0) {
1567
+ throw new Error(
1568
+ `Cannot redo: no operations in clipboard for scope "${scope}"`
1569
+ );
1570
+ }
1571
+ const operation = draft.document.clipboard.splice(operationIndex, 1)[0];
1572
+ draft.action = castDraft({
1573
+ type: operation.type,
1574
+ scope: operation.scope,
1575
+ input: operation.input
1576
+ });
1577
+ });
1578
+ }
1579
+ function pruneOperation(document, action, wrappedReducer) {
1580
+ const { scope } = action;
1581
+ const operations = document.operations[scope];
1582
+ let {
1583
+ input: { start, end }
1584
+ } = action;
1585
+ start = start || 0;
1586
+ end = end || operations.length;
1587
+ const actionsToPrune = operations.slice(start, end);
1588
+ const actionsToKeepStart = operations.slice(0, start);
1589
+ const actionsToKeepEnd = operations.slice(end);
1590
+ const newDocument = replayOperations(
1591
+ document.initialState,
1592
+ {
1593
+ ...document.operations,
1594
+ [scope]: actionsToKeepStart.concat(actionsToPrune)
1595
+ },
1596
+ wrappedReducer
1597
+ );
1598
+ const { name, state: newState } = newDocument;
1599
+ const loadStateIndex = actionsToKeepStart.length;
1600
+ const loadStateTimestamp = actionsToKeepStart.length ? actionsToKeepStart[actionsToKeepStart.length - 1].timestamp : actionsToKeepEnd.length ? actionsToKeepEnd[0].timestamp : (/* @__PURE__ */ new Date()).toISOString();
1601
+ return replayOperations(
1602
+ document.initialState,
1603
+ {
1604
+ ...document.operations,
1605
+ [scope]: [
1606
+ ...actionsToKeepStart,
1607
+ {
1608
+ ...loadState(
1609
+ { name, state: newState },
1610
+ actionsToPrune.length
1611
+ ),
1612
+ timestamp: loadStateTimestamp,
1613
+ index: loadStateIndex,
1614
+ hash: hashDocument({ state: newState }, "global")
1615
+ },
1616
+ ...actionsToKeepEnd.map((action2, index) => ({
1617
+ ...action2,
1618
+ index: loadStateIndex + index + 1
1619
+ }))
1620
+ ]
1621
+ },
1622
+ wrappedReducer
1623
+ );
1624
+ }
1625
+ function loadStateOperation(oldDocument, newDocument) {
1626
+ return {
1627
+ ...oldDocument,
1628
+ name: newDocument.name,
1629
+ state: newDocument.state ?? { global: {}, local: {} }
1630
+ };
1631
+ }
1632
+ const SET_NAME = "SET_NAME";
1633
+ const UNDO = "UNDO";
1634
+ const REDO = "REDO";
1635
+ const PRUNE = "PRUNE";
1636
+ const LOAD_STATE = "LOAD_STATE";
1637
+ const NOOP = "NOOP";
1645
1638
  function getNextRevision(document, action) {
1646
1639
  let latestOperation;
1647
1640
  if ("index" in action) {
@@ -1661,7 +1654,7 @@ function updateHeader(document, action) {
1661
1654
  lastModified: (/* @__PURE__ */ new Date()).toISOString()
1662
1655
  };
1663
1656
  }
1664
- function updateOperations(document, action, skip = 0) {
1657
+ function updateOperations(document, action, skip = 0, reuseLastOperationIndex = false) {
1665
1658
  if ([UNDO, REDO, PRUNE].includes(action.type)) {
1666
1659
  return document;
1667
1660
  }
@@ -1669,7 +1662,8 @@ function updateOperations(document, action, skip = 0) {
1669
1662
  const operations = document.operations[scope].slice();
1670
1663
  let operationId;
1671
1664
  const latestOperation = operations.at(-1);
1672
- let nextIndex = ((latestOperation == null ? void 0 : latestOperation.index) ?? -1) + 1;
1665
+ const lastOperationIndex = (latestOperation == null ? void 0 : latestOperation.index) ?? -1;
1666
+ let nextIndex = reuseLastOperationIndex ? lastOperationIndex : lastOperationIndex + 1;
1673
1667
  if ("index" in action) {
1674
1668
  if (action.index - skip > nextIndex) {
1675
1669
  throw new Error(
@@ -1696,8 +1690,13 @@ function updateOperations(document, action, skip = 0) {
1696
1690
  operations: { ...document.operations, [scope]: operations }
1697
1691
  };
1698
1692
  }
1699
- function updateDocument(document, action, skip = 0) {
1700
- let newDocument = updateOperations(document, action, skip);
1693
+ function updateDocument(document, action, skip = 0, reuseLastOperationIndex = false) {
1694
+ let newDocument = updateOperations(
1695
+ document,
1696
+ action,
1697
+ skip,
1698
+ reuseLastOperationIndex
1699
+ );
1701
1700
  newDocument = updateHeader(newDocument, action);
1702
1701
  return newDocument;
1703
1702
  }
@@ -1721,7 +1720,7 @@ function processUndoRedo(document, action, skip) {
1721
1720
  case REDO:
1722
1721
  return redoOperation(document, action, skip);
1723
1722
  default:
1724
- return { document, action, skip };
1723
+ return { document, action, skip, reuseLastOperationIndex: false };
1725
1724
  }
1726
1725
  }
1727
1726
  function processSkipOperation(document, action, customReducer, skipValue, reuseOperationResultingState = false, resultingStateParser = parseResultingState) {
@@ -1771,6 +1770,37 @@ function processSkipOperation(document, action, customReducer, skipValue, reuseO
1771
1770
  })
1772
1771
  };
1773
1772
  }
1773
+ function processUndoOperation(document, scope, customReducer, reuseOperationResultingState = false, resultingStateParser = parseResultingState) {
1774
+ const operations = [...document.operations[scope]];
1775
+ const sortedOperations = sortOperations$1(operations);
1776
+ sortedOperations.pop();
1777
+ const documentOperations = garbageCollectDocumentOperations(
1778
+ { ...document.operations }
1779
+ );
1780
+ const clearedOperations = [...documentOperations[scope]];
1781
+ const diff = diffOperations(
1782
+ garbageCollect(sortedOperations),
1783
+ clearedOperations
1784
+ );
1785
+ const doc = replayOperations(
1786
+ document.initialState,
1787
+ documentOperations,
1788
+ customReducer,
1789
+ void 0,
1790
+ void 0,
1791
+ void 0,
1792
+ void 0,
1793
+ {
1794
+ reuseHash: true,
1795
+ reuseOperationResultingState,
1796
+ operationResultingStateParser: resultingStateParser
1797
+ }
1798
+ );
1799
+ const clipboard = sortOperations$1(
1800
+ [...document.clipboard, ...diff].filter((op) => op.type !== "NOOP")
1801
+ ).reverse();
1802
+ return { ...doc, clipboard };
1803
+ }
1774
1804
  function baseReducer(document, action, customReducer, dispatch, options = {}) {
1775
1805
  const {
1776
1806
  skip,
@@ -1779,14 +1809,46 @@ function baseReducer(document, action, customReducer, dispatch, options = {}) {
1779
1809
  reuseOperationResultingState = false,
1780
1810
  operationResultingStateParser
1781
1811
  } = options;
1782
- const _action = { ...action };
1783
- const skipValue = skip || 0;
1812
+ let _action = { ...action };
1813
+ let skipValue = skip || 0;
1784
1814
  let newDocument = { ...document };
1815
+ let reuseLastOperationIndex = false;
1785
1816
  const shouldProcessSkipOperation = !ignoreSkipOperations && (skipValue > 0 || "index" in _action && _action.skip > 0);
1817
+ if (isUndoRedo(_action)) {
1818
+ const {
1819
+ skip: calculatedSkip,
1820
+ action: transformedAction,
1821
+ document: processedDocument,
1822
+ reuseLastOperationIndex: reuseIndex
1823
+ } = processUndoRedo(document, _action, skipValue);
1824
+ _action = transformedAction;
1825
+ skipValue = calculatedSkip;
1826
+ newDocument = processedDocument;
1827
+ reuseLastOperationIndex = reuseIndex;
1828
+ } else {
1829
+ newDocument = {
1830
+ ...newDocument,
1831
+ clipboard: []
1832
+ };
1833
+ }
1786
1834
  if (isBaseAction(_action)) {
1787
1835
  newDocument = _baseReducer(newDocument, _action, customReducer);
1788
1836
  }
1789
- newDocument = updateDocument(newDocument, _action, skipValue);
1837
+ newDocument = updateDocument(
1838
+ newDocument,
1839
+ _action,
1840
+ skipValue,
1841
+ reuseLastOperationIndex
1842
+ );
1843
+ const isUndoAction = isUndo(action);
1844
+ if (isUndoAction) {
1845
+ const result = processUndoOperation(
1846
+ newDocument,
1847
+ action.scope,
1848
+ customReducer
1849
+ );
1850
+ return result;
1851
+ }
1790
1852
  if (shouldProcessSkipOperation) {
1791
1853
  newDocument = processSkipOperation(
1792
1854
  newDocument,
@@ -1926,6 +1988,9 @@ function isNoopOperation(op) {
1926
1988
  function isUndoRedo(action) {
1927
1989
  return [UNDO, REDO].includes(action.type);
1928
1990
  }
1991
+ function isUndo(action) {
1992
+ return action.type === UNDO;
1993
+ }
1929
1994
  function isBaseAction(action) {
1930
1995
  return [SET_NAME, UNDO, REDO, PRUNE, LOAD_STATE].includes(action.type);
1931
1996
  }
@@ -2021,23 +2086,6 @@ function mapSkippedOperations(operations, skippedHeadOperations) {
2021
2086
  }
2022
2087
  return scopeOpsWithIgnore.reverse();
2023
2088
  }
2024
- function calculateSkipsLeft(operations, currentIndex, skip) {
2025
- const sortedOperations = operations.slice().sort((a, b) => a.skip - b.skip).sort((a, b) => a.index - b.index);
2026
- let skipsLeft = skip;
2027
- let skipsToPerform = 0;
2028
- let lastIndex = currentIndex;
2029
- for (const operation of sortedOperations.reverse()) {
2030
- const distance = lastIndex - operation.index;
2031
- skipsLeft = skipsLeft - distance;
2032
- if (skipsLeft > -1) {
2033
- skipsToPerform++;
2034
- lastIndex = operation.index;
2035
- } else {
2036
- break;
2037
- }
2038
- }
2039
- return skipsToPerform;
2040
- }
2041
2089
  function sortOperations(operations) {
2042
2090
  return Object.values(operations).flatMap((array) => array).sort(
2043
2091
  (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
@@ -2403,9 +2451,9 @@ function applyMixins(derivedCtor, constructors) {
2403
2451
  });
2404
2452
  }
2405
2453
  export {
2406
- isNoopOperation as A,
2454
+ isSameDocument as A,
2407
2455
  BaseDocument as B,
2408
- isSameDocument as C,
2456
+ isUndo as C,
2409
2457
  isUndoRedo as D,
2410
2458
  mapSkippedOperations as E,
2411
2459
  parseResultingState as F,
@@ -2437,16 +2485,16 @@ export {
2437
2485
  buildOperationSignatureMessage as m,
2438
2486
  buildOperationSignatureParams as n,
2439
2487
  buildSignedOperation as o,
2440
- calculateSkipsLeft as p,
2441
- createUnsafeReducer as q,
2442
- createZip as r,
2488
+ createUnsafeReducer as p,
2489
+ createZip as q,
2490
+ documentHelpers as r,
2443
2491
  saveToFile as s,
2444
- documentHelpers as t,
2445
- getLocalFile as u,
2446
- getRemoteFile as v,
2447
- getUnixTimestamp as w,
2448
- hashDocument as x,
2449
- hashKey as y,
2450
- hex2ab as z
2492
+ getLocalFile as t,
2493
+ getRemoteFile as u,
2494
+ getUnixTimestamp as v,
2495
+ hashDocument as w,
2496
+ hashKey as x,
2497
+ hex2ab as y,
2498
+ isNoopOperation as z
2451
2499
  };
2452
- //# sourceMappingURL=object-VZ_AS47_.js.map
2500
+ //# sourceMappingURL=object-COSf2HUT.js.map