@verdant-web/common 1.15.3-next.0 → 1.15.4

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 (154) hide show
  1. package/dist/cjs/EventSubscriber.d.ts +17 -17
  2. package/dist/cjs/EventSubscriber.js +66 -66
  3. package/dist/cjs/baseline.d.ts +5 -5
  4. package/dist/cjs/baseline.js +2 -2
  5. package/dist/cjs/batching.d.ts +42 -42
  6. package/dist/cjs/batching.js +91 -91
  7. package/dist/cjs/files.d.ts +19 -19
  8. package/dist/cjs/files.js +18 -18
  9. package/dist/cjs/index.d.ts +19 -19
  10. package/dist/cjs/index.js +41 -41
  11. package/dist/cjs/indexes.d.ts +17 -17
  12. package/dist/cjs/indexes.js +142 -142
  13. package/dist/cjs/indexes.test.d.ts +1 -1
  14. package/dist/cjs/indexes.test.js +127 -127
  15. package/dist/cjs/migration.d.ts +172 -172
  16. package/dist/cjs/migration.js +301 -301
  17. package/dist/cjs/oids.d.ts +100 -100
  18. package/dist/cjs/oids.js +461 -457
  19. package/dist/cjs/oids.js.map +1 -1
  20. package/dist/cjs/oids.test.d.ts +1 -1
  21. package/dist/cjs/oids.test.js +348 -348
  22. package/dist/cjs/operation.d.ts +139 -139
  23. package/dist/cjs/operation.js +545 -545
  24. package/dist/cjs/operation.test.d.ts +1 -1
  25. package/dist/cjs/operation.test.js +529 -529
  26. package/dist/cjs/patch.d.ts +26 -26
  27. package/dist/cjs/patch.js +226 -226
  28. package/dist/cjs/presence.d.ts +27 -27
  29. package/dist/cjs/presence.js +2 -2
  30. package/dist/cjs/protocol.d.ts +145 -145
  31. package/dist/cjs/protocol.js +3 -3
  32. package/dist/cjs/refs.d.ts +7 -7
  33. package/dist/cjs/refs.js +29 -29
  34. package/dist/cjs/replica.d.ts +28 -28
  35. package/dist/cjs/replica.js +28 -28
  36. package/dist/cjs/schema/defaults.test.d.ts +1 -1
  37. package/dist/cjs/schema/defaults.test.js +20 -20
  38. package/dist/cjs/schema/fields.d.ts +9 -9
  39. package/dist/cjs/schema/fields.js +144 -144
  40. package/dist/cjs/schema/index.d.ts +8 -8
  41. package/dist/cjs/schema/index.js +51 -51
  42. package/dist/cjs/schema/indexFilters.d.ts +6 -6
  43. package/dist/cjs/schema/indexFilters.js +30 -30
  44. package/dist/cjs/schema/types/collection.d.ts +23 -23
  45. package/dist/cjs/schema/types/collection.js +2 -2
  46. package/dist/cjs/schema/types/compounds.d.ts +11 -11
  47. package/dist/cjs/schema/types/compounds.js +2 -2
  48. package/dist/cjs/schema/types/fields.d.ts +75 -75
  49. package/dist/cjs/schema/types/fields.js +2 -2
  50. package/dist/cjs/schema/types/filters.d.ts +29 -29
  51. package/dist/cjs/schema/types/filters.js +2 -2
  52. package/dist/cjs/schema/types/shapes.d.ts +21 -21
  53. package/dist/cjs/schema/types/shapes.js +2 -2
  54. package/dist/cjs/schema/types/synthetics.d.ts +37 -37
  55. package/dist/cjs/schema/types/synthetics.js +2 -2
  56. package/dist/cjs/schema/types.d.ts +18 -18
  57. package/dist/cjs/schema/types.js +22 -22
  58. package/dist/cjs/schema/validation.d.ts +3 -3
  59. package/dist/cjs/schema/validation.js +86 -86
  60. package/dist/cjs/timestamp.d.ts +50 -50
  61. package/dist/cjs/timestamp.js +251 -251
  62. package/dist/cjs/timestamp.test.d.ts +1 -1
  63. package/dist/cjs/timestamp.test.js +104 -104
  64. package/dist/cjs/undo.d.ts +3 -3
  65. package/dist/cjs/undo.js +198 -198
  66. package/dist/cjs/utils.d.ts +20 -20
  67. package/dist/cjs/utils.js +145 -145
  68. package/dist/cjs/utils.test.d.ts +1 -1
  69. package/dist/cjs/utils.test.js +36 -36
  70. package/dist/esm/EventSubscriber.d.ts +17 -17
  71. package/dist/esm/EventSubscriber.js +62 -62
  72. package/dist/esm/baseline.d.ts +5 -5
  73. package/dist/esm/baseline.js +1 -1
  74. package/dist/esm/batching.d.ts +42 -42
  75. package/dist/esm/batching.js +86 -86
  76. package/dist/esm/files.d.ts +19 -19
  77. package/dist/esm/files.js +12 -12
  78. package/dist/esm/index.d.ts +19 -19
  79. package/dist/esm/index.js +16 -16
  80. package/dist/esm/indexes.d.ts +17 -17
  81. package/dist/esm/indexes.js +130 -130
  82. package/dist/esm/indexes.test.d.ts +1 -1
  83. package/dist/esm/indexes.test.js +125 -125
  84. package/dist/esm/migration.d.ts +172 -172
  85. package/dist/esm/migration.js +294 -294
  86. package/dist/esm/oids.d.ts +100 -100
  87. package/dist/esm/oids.js +426 -422
  88. package/dist/esm/oids.js.map +1 -1
  89. package/dist/esm/oids.test.d.ts +1 -1
  90. package/dist/esm/oids.test.js +346 -346
  91. package/dist/esm/operation.d.ts +139 -139
  92. package/dist/esm/operation.js +530 -530
  93. package/dist/esm/operation.test.d.ts +1 -1
  94. package/dist/esm/operation.test.js +527 -527
  95. package/dist/esm/patch.d.ts +26 -26
  96. package/dist/esm/patch.js +222 -222
  97. package/dist/esm/presence.d.ts +27 -27
  98. package/dist/esm/presence.js +1 -1
  99. package/dist/esm/protocol.d.ts +145 -145
  100. package/dist/esm/protocol.js +2 -2
  101. package/dist/esm/refs.d.ts +7 -7
  102. package/dist/esm/refs.js +22 -22
  103. package/dist/esm/replica.d.ts +28 -28
  104. package/dist/esm/replica.js +25 -25
  105. package/dist/esm/schema/defaults.test.d.ts +1 -1
  106. package/dist/esm/schema/defaults.test.js +18 -18
  107. package/dist/esm/schema/fields.d.ts +9 -9
  108. package/dist/esm/schema/fields.js +133 -133
  109. package/dist/esm/schema/index.d.ts +8 -8
  110. package/dist/esm/schema/index.js +32 -32
  111. package/dist/esm/schema/indexFilters.d.ts +6 -6
  112. package/dist/esm/schema/indexFilters.js +22 -22
  113. package/dist/esm/schema/types/collection.d.ts +23 -23
  114. package/dist/esm/schema/types/collection.js +1 -1
  115. package/dist/esm/schema/types/compounds.d.ts +11 -11
  116. package/dist/esm/schema/types/compounds.js +1 -1
  117. package/dist/esm/schema/types/fields.d.ts +75 -75
  118. package/dist/esm/schema/types/fields.js +1 -1
  119. package/dist/esm/schema/types/filters.d.ts +29 -29
  120. package/dist/esm/schema/types/filters.js +1 -1
  121. package/dist/esm/schema/types/shapes.d.ts +21 -21
  122. package/dist/esm/schema/types/shapes.js +1 -1
  123. package/dist/esm/schema/types/synthetics.d.ts +37 -37
  124. package/dist/esm/schema/types/synthetics.js +1 -1
  125. package/dist/esm/schema/types.d.ts +18 -18
  126. package/dist/esm/schema/types.js +6 -6
  127. package/dist/esm/schema/validation.d.ts +3 -3
  128. package/dist/esm/schema/validation.js +81 -81
  129. package/dist/esm/timestamp.d.ts +50 -50
  130. package/dist/esm/timestamp.js +234 -234
  131. package/dist/esm/timestamp.test.d.ts +1 -1
  132. package/dist/esm/timestamp.test.js +102 -102
  133. package/dist/esm/undo.d.ts +3 -3
  134. package/dist/esm/undo.js +194 -194
  135. package/dist/esm/utils.d.ts +20 -20
  136. package/dist/esm/utils.js +127 -127
  137. package/dist/esm/utils.test.d.ts +1 -1
  138. package/dist/esm/utils.test.js +34 -34
  139. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  140. package/dist/tsconfig.tsbuildinfo +1 -1
  141. package/package.json +1 -1
  142. package/src/oids.ts +5 -1
  143. package/dist/cjs/files.test.d.ts +0 -1
  144. package/dist/cjs/files.test.js +0 -81
  145. package/dist/cjs/files.test.js.map +0 -1
  146. package/dist/cjs/schema/defaults.d.ts +0 -4
  147. package/dist/cjs/schema/defaults.js +0 -72
  148. package/dist/cjs/schema/defaults.js.map +0 -1
  149. package/dist/esm/files.test.d.ts +0 -1
  150. package/dist/esm/files.test.js +0 -79
  151. package/dist/esm/files.test.js.map +0 -1
  152. package/dist/esm/schema/defaults.d.ts +0 -4
  153. package/dist/esm/schema/defaults.js +0 -66
  154. package/dist/esm/schema/defaults.js.map +0 -1
@@ -1,103 +1,103 @@
1
- import { describe, expect, it, vitest } from 'vitest';
2
- import { convertOldHlcTimestamp, encodeVersion, HybridLogicalClockTimestampProvider, OLD_encodeVersion, OLD_serializeHlcTimestamp, } from './timestamp.js';
3
- describe('the hybrid logical clock', () => {
4
- it('always produces incrementing timestamps', async () => {
5
- const clock = new HybridLogicalClockTimestampProvider();
6
- let prev = clock.now(1);
7
- clock.update(prev);
8
- let now;
9
- for (let i = 0; i < 1000; i++) {
10
- now = clock.now(1);
11
- expect(now > prev, `${now} not later than ${prev}`).toBe(true);
12
- clock.update(now);
13
- prev = now;
14
- }
15
- let time = new Date().getTime();
16
- for (let i = 0; i < 1000; i++) {
17
- vitest.setSystemTime(time++);
18
- now = clock.now(1);
19
- expect(now > prev, `${now} not later than ${prev}`).toBe(true);
20
- clock.update(now);
21
- prev = now;
22
- }
23
- });
24
- it('groups blocks of time by version', () => {
25
- const clock = new HybridLogicalClockTimestampProvider();
26
- let prev = clock.now(1);
27
- clock.update(prev);
28
- let now;
29
- for (let i = 0; i < 10; i++) {
30
- now = clock.now(1);
31
- expect(now > prev, `${now} not later than ${prev}`).toBe(true);
32
- clock.update(now);
33
- prev = now;
34
- }
35
- for (let i = 0; i < 10; i++) {
36
- now = clock.now(2);
37
- expect(now > prev, `${now} not later than ${prev}`).toBe(true);
38
- clock.update(now);
39
- prev = now;
40
- }
41
- const startOfVersion2 = now;
42
- // jump back to 1
43
- prev = clock.now(1);
44
- clock.update(prev);
45
- for (let i = 0; i < 10; i++) {
46
- now = clock.now(1);
47
- expect(now > prev, `${now} not later than ${prev}`).toBe(true);
48
- expect(now < startOfVersion2, `${now} not earlier than ${startOfVersion2}`).toBe(true);
49
- clock.update(now);
50
- prev = now;
51
- }
52
- });
53
- it('is shorter than the previous format', () => {
54
- const clock = new HybridLogicalClockTimestampProvider();
55
- expect(clock.now(1).length).toBeLessThan(clock.OLD_now(1).length);
56
- });
57
- it('correctly converts old to new format', () => {
58
- const clock = new HybridLogicalClockTimestampProvider();
59
- const oldFormat = clock.OLD_now(1);
60
- const value = clock.timerState();
61
- const converted = convertOldHlcTimestamp(oldFormat);
62
- const newFormat = clock.get(1, value);
63
- expect(converted).toEqual(newFormat);
64
- });
65
- // this is mostly to verify as I move to the new format.
66
- // instead of attempting a distributed migration of timestamps, it
67
- // will be easier that the new format just acts as always in the
68
- // future of the old one. this will only mess with offline clients,
69
- // but nobody is using verdant but me yet at time of this change (2022-12-19)
70
- // which is kind of why I'm doing it now anyway.
71
- it('old format always comes before new format', () => {
72
- const clock = new HybridLogicalClockTimestampProvider();
73
- const newFormat = clock.now(1);
74
- const oldFormat = clock.OLD_now(1);
75
- expect(oldFormat < newFormat).toBe(true);
76
- // even far-future old timestamps are before new timestamps
77
- const oldFormat2 = OLD_serializeHlcTimestamp({
78
- counter: 999,
79
- node: '9876543210',
80
- time: new Date(3000, 1, 1).getTime(),
81
- });
82
- expect(OLD_encodeVersion(1) + oldFormat2 < newFormat).toBe(true);
83
- });
84
- it('decodes timestamps', () => {
85
- const time = new Date();
86
- vitest.setSystemTime(time);
87
- const clock = new HybridLogicalClockTimestampProvider();
88
- const now = clock.now(1);
89
- expect(clock.getWallClockTime(now)).toEqual(time.getTime());
90
- });
91
- });
92
- describe('version encoding', () => {
93
- it('orders 1 before 2', () => {
94
- expect(encodeVersion(1) < encodeVersion(2)).toBe(true);
95
- });
96
- it('orders 32 before 33', () => {
97
- expect(encodeVersion(32) < encodeVersion(33)).toBe(true);
98
- });
99
- it('orders 34 before 35', () => {
100
- expect(encodeVersion(34) < encodeVersion(35)).toBe(true);
101
- });
102
- });
1
+ import { describe, expect, it, vitest } from 'vitest';
2
+ import { convertOldHlcTimestamp, encodeVersion, HybridLogicalClockTimestampProvider, OLD_encodeVersion, OLD_serializeHlcTimestamp, } from './timestamp.js';
3
+ describe('the hybrid logical clock', () => {
4
+ it('always produces incrementing timestamps', async () => {
5
+ const clock = new HybridLogicalClockTimestampProvider();
6
+ let prev = clock.now(1);
7
+ clock.update(prev);
8
+ let now;
9
+ for (let i = 0; i < 1000; i++) {
10
+ now = clock.now(1);
11
+ expect(now > prev, `${now} not later than ${prev}`).toBe(true);
12
+ clock.update(now);
13
+ prev = now;
14
+ }
15
+ let time = new Date().getTime();
16
+ for (let i = 0; i < 1000; i++) {
17
+ vitest.setSystemTime(time++);
18
+ now = clock.now(1);
19
+ expect(now > prev, `${now} not later than ${prev}`).toBe(true);
20
+ clock.update(now);
21
+ prev = now;
22
+ }
23
+ });
24
+ it('groups blocks of time by version', () => {
25
+ const clock = new HybridLogicalClockTimestampProvider();
26
+ let prev = clock.now(1);
27
+ clock.update(prev);
28
+ let now;
29
+ for (let i = 0; i < 10; i++) {
30
+ now = clock.now(1);
31
+ expect(now > prev, `${now} not later than ${prev}`).toBe(true);
32
+ clock.update(now);
33
+ prev = now;
34
+ }
35
+ for (let i = 0; i < 10; i++) {
36
+ now = clock.now(2);
37
+ expect(now > prev, `${now} not later than ${prev}`).toBe(true);
38
+ clock.update(now);
39
+ prev = now;
40
+ }
41
+ const startOfVersion2 = now;
42
+ // jump back to 1
43
+ prev = clock.now(1);
44
+ clock.update(prev);
45
+ for (let i = 0; i < 10; i++) {
46
+ now = clock.now(1);
47
+ expect(now > prev, `${now} not later than ${prev}`).toBe(true);
48
+ expect(now < startOfVersion2, `${now} not earlier than ${startOfVersion2}`).toBe(true);
49
+ clock.update(now);
50
+ prev = now;
51
+ }
52
+ });
53
+ it('is shorter than the previous format', () => {
54
+ const clock = new HybridLogicalClockTimestampProvider();
55
+ expect(clock.now(1).length).toBeLessThan(clock.OLD_now(1).length);
56
+ });
57
+ it('correctly converts old to new format', () => {
58
+ const clock = new HybridLogicalClockTimestampProvider();
59
+ const oldFormat = clock.OLD_now(1);
60
+ const value = clock.timerState();
61
+ const converted = convertOldHlcTimestamp(oldFormat);
62
+ const newFormat = clock.get(1, value);
63
+ expect(converted).toEqual(newFormat);
64
+ });
65
+ // this is mostly to verify as I move to the new format.
66
+ // instead of attempting a distributed migration of timestamps, it
67
+ // will be easier that the new format just acts as always in the
68
+ // future of the old one. this will only mess with offline clients,
69
+ // but nobody is using verdant but me yet at time of this change (2022-12-19)
70
+ // which is kind of why I'm doing it now anyway.
71
+ it('old format always comes before new format', () => {
72
+ const clock = new HybridLogicalClockTimestampProvider();
73
+ const newFormat = clock.now(1);
74
+ const oldFormat = clock.OLD_now(1);
75
+ expect(oldFormat < newFormat).toBe(true);
76
+ // even far-future old timestamps are before new timestamps
77
+ const oldFormat2 = OLD_serializeHlcTimestamp({
78
+ counter: 999,
79
+ node: '9876543210',
80
+ time: new Date(3000, 1, 1).getTime(),
81
+ });
82
+ expect(OLD_encodeVersion(1) + oldFormat2 < newFormat).toBe(true);
83
+ });
84
+ it('decodes timestamps', () => {
85
+ const time = new Date();
86
+ vitest.setSystemTime(time);
87
+ const clock = new HybridLogicalClockTimestampProvider();
88
+ const now = clock.now(1);
89
+ expect(clock.getWallClockTime(now)).toEqual(time.getTime());
90
+ });
91
+ });
92
+ describe('version encoding', () => {
93
+ it('orders 1 before 2', () => {
94
+ expect(encodeVersion(1) < encodeVersion(2)).toBe(true);
95
+ });
96
+ it('orders 32 before 33', () => {
97
+ expect(encodeVersion(32) < encodeVersion(33)).toBe(true);
98
+ });
99
+ it('orders 34 before 35', () => {
100
+ expect(encodeVersion(34) < encodeVersion(35)).toBe(true);
101
+ });
102
+ });
103
103
  //# sourceMappingURL=timestamp.test.js.map
@@ -1,3 +1,3 @@
1
- import { ObjectIdentifier } from './oids.js';
2
- import { Operation } from './operation.js';
3
- export declare function getUndoOperations(oid: ObjectIdentifier, initial: any, operations: Operation[], getNow: () => string): Operation[];
1
+ import { ObjectIdentifier } from './oids.js';
2
+ import { Operation } from './operation.js';
3
+ export declare function getUndoOperations(oid: ObjectIdentifier, initial: any, operations: Operation[], getNow: () => string): Operation[];
package/dist/esm/undo.js CHANGED
@@ -1,195 +1,195 @@
1
- import { applyPatch } from './operation.js';
2
- import { cloneDeep } from './utils.js';
3
- export function getUndoOperations(oid, initial, operations, getNow) {
4
- if (initial === undefined) {
5
- // if the initial state is nothing, then the undo is to delete everything.
6
- // there's nothing else to worry about!
7
- return [
8
- {
9
- oid,
10
- timestamp: getNow(),
11
- data: {
12
- op: 'delete',
13
- },
14
- },
15
- ];
16
- }
17
- // otherwise, traverse the operations one by one,
18
- // applying them, and using the prior state to determine
19
- // what the undo operation is.
20
- let state = cloneDeep(initial);
21
- const undoOperations = [];
22
- for (const operation of operations) {
23
- const undo = getUndoOperation(oid, state, operation, getNow);
24
- undoOperations.unshift(...undo);
25
- applyPatch(state, operation.data);
26
- }
27
- return undoOperations;
28
- }
29
- function getUndoOperation(oid, initial, operation, getNow) {
30
- const data = operation.data;
31
- switch (data.op) {
32
- case 'set':
33
- case 'remove':
34
- return [
35
- {
36
- oid,
37
- timestamp: getNow(),
38
- data: {
39
- op: 'set',
40
- name: data.name,
41
- value: initial[data.name],
42
- },
43
- },
44
- ];
45
- case 'list-insert':
46
- return [
47
- {
48
- oid,
49
- timestamp: getNow(),
50
- data: {
51
- op: 'list-delete',
52
- index: data.index,
53
- count: 1,
54
- },
55
- },
56
- ];
57
- case 'list-delete':
58
- return [
59
- {
60
- oid,
61
- timestamp: getNow(),
62
- data: {
63
- op: 'list-insert',
64
- index: data.index,
65
- values: initial.slice(data.index, data.count),
66
- },
67
- },
68
- ];
69
- case 'list-move-by-ref':
70
- return [
71
- {
72
- oid,
73
- timestamp: getNow(),
74
- data: {
75
- op: 'list-move-by-ref',
76
- value: data.value,
77
- index: initial.indexOf(data.value),
78
- },
79
- },
80
- ];
81
- case 'list-move-by-index':
82
- return [
83
- {
84
- oid,
85
- timestamp: getNow(),
86
- data: {
87
- op: 'list-move-by-index',
88
- from: data.to,
89
- to: data.from,
90
- },
91
- },
92
- ];
93
- case 'delete':
94
- return [
95
- {
96
- oid,
97
- timestamp: getNow(),
98
- data: {
99
- op: 'initialize',
100
- value: initial,
101
- },
102
- },
103
- ];
104
- case 'list-push':
105
- return [
106
- {
107
- oid,
108
- timestamp: getNow(),
109
- data: {
110
- op: 'list-remove',
111
- value: data.value,
112
- // best heuristic here - remove the last instance of the item.
113
- only: 'last',
114
- },
115
- },
116
- ];
117
- case 'list-remove':
118
- if (data.only === 'last') {
119
- const index = initial.lastIndexOf(data.value);
120
- return [
121
- {
122
- oid,
123
- timestamp: getNow(),
124
- data: {
125
- op: 'list-insert',
126
- index,
127
- values: [data.value],
128
- },
129
- },
130
- ];
131
- }
132
- else if (data.only === 'first') {
133
- const index = initial.indexOf(data.value);
134
- return [
135
- {
136
- oid,
137
- timestamp: getNow(),
138
- data: {
139
- op: 'list-insert',
140
- index,
141
- values: [data.value],
142
- },
143
- },
144
- ];
145
- }
146
- else {
147
- // find all instances of value and restore them at their
148
- // original index
149
- const indexesOfValue = [];
150
- let index = initial.indexOf(data.value);
151
- while (index !== -1) {
152
- indexesOfValue.push(index);
153
- index = initial.indexOf(data.value, index + 1);
154
- }
155
- return indexesOfValue.map((index) => ({
156
- oid,
157
- timestamp: getNow(),
158
- data: {
159
- op: 'list-insert',
160
- index,
161
- value: data.value,
162
- },
163
- }));
164
- }
165
- case 'list-add':
166
- // this one is kind of ambiguous. in theory the set may have
167
- // already included the value and so no change happened. but
168
- // basically we infer the intent is to remove what was meant
169
- // to be added by this change.
170
- return [
171
- {
172
- oid,
173
- timestamp: getNow(),
174
- data: {
175
- op: 'list-remove',
176
- value: data.value,
177
- only: 'last',
178
- },
179
- },
180
- ];
181
- case 'initialize':
182
- return [
183
- {
184
- oid,
185
- timestamp: getNow(),
186
- data: {
187
- op: 'delete',
188
- },
189
- },
190
- ];
191
- default:
192
- throw new Error(`Cannot undo operation type: ${data.op}`);
193
- }
194
- }
1
+ import { applyPatch } from './operation.js';
2
+ import { cloneDeep } from './utils.js';
3
+ export function getUndoOperations(oid, initial, operations, getNow) {
4
+ if (initial === undefined) {
5
+ // if the initial state is nothing, then the undo is to delete everything.
6
+ // there's nothing else to worry about!
7
+ return [
8
+ {
9
+ oid,
10
+ timestamp: getNow(),
11
+ data: {
12
+ op: 'delete',
13
+ },
14
+ },
15
+ ];
16
+ }
17
+ // otherwise, traverse the operations one by one,
18
+ // applying them, and using the prior state to determine
19
+ // what the undo operation is.
20
+ let state = cloneDeep(initial);
21
+ const undoOperations = [];
22
+ for (const operation of operations) {
23
+ const undo = getUndoOperation(oid, state, operation, getNow);
24
+ undoOperations.unshift(...undo);
25
+ applyPatch(state, operation.data);
26
+ }
27
+ return undoOperations;
28
+ }
29
+ function getUndoOperation(oid, initial, operation, getNow) {
30
+ const data = operation.data;
31
+ switch (data.op) {
32
+ case 'set':
33
+ case 'remove':
34
+ return [
35
+ {
36
+ oid,
37
+ timestamp: getNow(),
38
+ data: {
39
+ op: 'set',
40
+ name: data.name,
41
+ value: initial[data.name],
42
+ },
43
+ },
44
+ ];
45
+ case 'list-insert':
46
+ return [
47
+ {
48
+ oid,
49
+ timestamp: getNow(),
50
+ data: {
51
+ op: 'list-delete',
52
+ index: data.index,
53
+ count: 1,
54
+ },
55
+ },
56
+ ];
57
+ case 'list-delete':
58
+ return [
59
+ {
60
+ oid,
61
+ timestamp: getNow(),
62
+ data: {
63
+ op: 'list-insert',
64
+ index: data.index,
65
+ values: initial.slice(data.index, data.count),
66
+ },
67
+ },
68
+ ];
69
+ case 'list-move-by-ref':
70
+ return [
71
+ {
72
+ oid,
73
+ timestamp: getNow(),
74
+ data: {
75
+ op: 'list-move-by-ref',
76
+ value: data.value,
77
+ index: initial.indexOf(data.value),
78
+ },
79
+ },
80
+ ];
81
+ case 'list-move-by-index':
82
+ return [
83
+ {
84
+ oid,
85
+ timestamp: getNow(),
86
+ data: {
87
+ op: 'list-move-by-index',
88
+ from: data.to,
89
+ to: data.from,
90
+ },
91
+ },
92
+ ];
93
+ case 'delete':
94
+ return [
95
+ {
96
+ oid,
97
+ timestamp: getNow(),
98
+ data: {
99
+ op: 'initialize',
100
+ value: initial,
101
+ },
102
+ },
103
+ ];
104
+ case 'list-push':
105
+ return [
106
+ {
107
+ oid,
108
+ timestamp: getNow(),
109
+ data: {
110
+ op: 'list-remove',
111
+ value: data.value,
112
+ // best heuristic here - remove the last instance of the item.
113
+ only: 'last',
114
+ },
115
+ },
116
+ ];
117
+ case 'list-remove':
118
+ if (data.only === 'last') {
119
+ const index = initial.lastIndexOf(data.value);
120
+ return [
121
+ {
122
+ oid,
123
+ timestamp: getNow(),
124
+ data: {
125
+ op: 'list-insert',
126
+ index,
127
+ values: [data.value],
128
+ },
129
+ },
130
+ ];
131
+ }
132
+ else if (data.only === 'first') {
133
+ const index = initial.indexOf(data.value);
134
+ return [
135
+ {
136
+ oid,
137
+ timestamp: getNow(),
138
+ data: {
139
+ op: 'list-insert',
140
+ index,
141
+ values: [data.value],
142
+ },
143
+ },
144
+ ];
145
+ }
146
+ else {
147
+ // find all instances of value and restore them at their
148
+ // original index
149
+ const indexesOfValue = [];
150
+ let index = initial.indexOf(data.value);
151
+ while (index !== -1) {
152
+ indexesOfValue.push(index);
153
+ index = initial.indexOf(data.value, index + 1);
154
+ }
155
+ return indexesOfValue.map((index) => ({
156
+ oid,
157
+ timestamp: getNow(),
158
+ data: {
159
+ op: 'list-insert',
160
+ index,
161
+ value: data.value,
162
+ },
163
+ }));
164
+ }
165
+ case 'list-add':
166
+ // this one is kind of ambiguous. in theory the set may have
167
+ // already included the value and so no change happened. but
168
+ // basically we infer the intent is to remove what was meant
169
+ // to be added by this change.
170
+ return [
171
+ {
172
+ oid,
173
+ timestamp: getNow(),
174
+ data: {
175
+ op: 'list-remove',
176
+ value: data.value,
177
+ only: 'last',
178
+ },
179
+ },
180
+ ];
181
+ case 'initialize':
182
+ return [
183
+ {
184
+ oid,
185
+ timestamp: getNow(),
186
+ data: {
187
+ op: 'delete',
188
+ },
189
+ },
190
+ ];
191
+ default:
192
+ throw new Error(`Cannot undo operation type: ${data.op}`);
193
+ }
194
+ }
195
195
  //# sourceMappingURL=undo.js.map
@@ -1,20 +1,20 @@
1
- export declare function take<T extends object, Keys extends keyof T>(obj: T, keys: Keys[]): Pick<T, Keys>;
2
- export declare function omit<T extends object, Keys extends keyof T>(obj: T, keys: Keys[]): Omit<T, Keys>;
3
- export declare function getSortedIndex<T>(array: T[], insert: T, compare: (a: T, b: T) => number): number;
4
- /**
5
- * Consistently stringifies an object regardless
6
- * of key insertion order
7
- */
8
- export declare function stableStringify(obj: any): string;
9
- /**
10
- * A version of structured cloning which preserves object identity
11
- * references in the system.
12
- */
13
- export declare function cloneDeep<T>(obj: T, copyOids?: boolean): T;
14
- export declare function hashObject(obj: any): string;
15
- export declare function isObject(obj: any): any;
16
- export declare function roughSizeOfObject(object: any): number;
17
- export declare function assert(condition: any, message?: string): asserts condition;
18
- export declare function generateId(length?: number): string;
19
- export declare function findLastIndex<T>(array: T[], predicate: (item: T) => boolean): number;
20
- export declare function debounce<T extends (...args: any[]) => any>(fn: T, wait: number): T;
1
+ export declare function take<T extends object, Keys extends keyof T>(obj: T, keys: Keys[]): Pick<T, Keys>;
2
+ export declare function omit<T extends object, Keys extends keyof T>(obj: T, keys: Keys[]): Omit<T, Keys>;
3
+ export declare function getSortedIndex<T>(array: T[], insert: T, compare: (a: T, b: T) => number): number;
4
+ /**
5
+ * Consistently stringifies an object regardless
6
+ * of key insertion order
7
+ */
8
+ export declare function stableStringify(obj: any): string;
9
+ /**
10
+ * A version of structured cloning which preserves object identity
11
+ * references in the system.
12
+ */
13
+ export declare function cloneDeep<T>(obj: T, copyOids?: boolean): T;
14
+ export declare function hashObject(obj: any): string;
15
+ export declare function isObject(obj: any): any;
16
+ export declare function roughSizeOfObject(object: any): number;
17
+ export declare function assert(condition: any, message?: string): asserts condition;
18
+ export declare function generateId(length?: number): string;
19
+ export declare function findLastIndex<T>(array: T[], predicate: (item: T) => boolean): number;
20
+ export declare function debounce<T extends (...args: any[]) => any>(fn: T, wait: number): T;