@livestore/common 0.3.0-dev.25 → 0.3.0-dev.27

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 (104) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-types.d.ts +13 -12
  3. package/dist/adapter-types.d.ts.map +1 -1
  4. package/dist/adapter-types.js +5 -6
  5. package/dist/adapter-types.js.map +1 -1
  6. package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
  7. package/dist/devtools/devtools-messages-common.d.ts +13 -6
  8. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  9. package/dist/devtools/devtools-messages-common.js +6 -0
  10. package/dist/devtools/devtools-messages-common.js.map +1 -1
  11. package/dist/devtools/devtools-messages-leader.d.ts +25 -25
  12. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  13. package/dist/devtools/devtools-messages-leader.js +1 -2
  14. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  15. package/dist/leader-thread/LeaderSyncProcessor.d.ts +15 -6
  16. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  17. package/dist/leader-thread/LeaderSyncProcessor.js +229 -207
  18. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  19. package/dist/leader-thread/apply-mutation.d.ts +14 -9
  20. package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
  21. package/dist/leader-thread/apply-mutation.js +43 -36
  22. package/dist/leader-thread/apply-mutation.js.map +1 -1
  23. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  24. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  25. package/dist/leader-thread/leader-worker-devtools.js +4 -5
  26. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  27. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  28. package/dist/leader-thread/make-leader-thread-layer.js +21 -33
  29. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  30. package/dist/leader-thread/mod.d.ts +1 -1
  31. package/dist/leader-thread/mod.d.ts.map +1 -1
  32. package/dist/leader-thread/mod.js +1 -1
  33. package/dist/leader-thread/mod.js.map +1 -1
  34. package/dist/leader-thread/mutationlog.d.ts +19 -3
  35. package/dist/leader-thread/mutationlog.d.ts.map +1 -1
  36. package/dist/leader-thread/mutationlog.js +105 -12
  37. package/dist/leader-thread/mutationlog.js.map +1 -1
  38. package/dist/leader-thread/pull-queue-set.d.ts +1 -1
  39. package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
  40. package/dist/leader-thread/pull-queue-set.js +6 -16
  41. package/dist/leader-thread/pull-queue-set.js.map +1 -1
  42. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  43. package/dist/leader-thread/recreate-db.js +4 -3
  44. package/dist/leader-thread/recreate-db.js.map +1 -1
  45. package/dist/leader-thread/types.d.ts +34 -19
  46. package/dist/leader-thread/types.d.ts.map +1 -1
  47. package/dist/leader-thread/types.js.map +1 -1
  48. package/dist/rehydrate-from-mutationlog.d.ts +5 -4
  49. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  50. package/dist/rehydrate-from-mutationlog.js +7 -9
  51. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  52. package/dist/schema/EventId.d.ts +9 -0
  53. package/dist/schema/EventId.d.ts.map +1 -1
  54. package/dist/schema/EventId.js +17 -2
  55. package/dist/schema/EventId.js.map +1 -1
  56. package/dist/schema/MutationEvent.d.ts +78 -25
  57. package/dist/schema/MutationEvent.d.ts.map +1 -1
  58. package/dist/schema/MutationEvent.js +25 -12
  59. package/dist/schema/MutationEvent.js.map +1 -1
  60. package/dist/schema/schema.js +1 -1
  61. package/dist/schema/schema.js.map +1 -1
  62. package/dist/schema/system-tables.d.ts +67 -0
  63. package/dist/schema/system-tables.d.ts.map +1 -1
  64. package/dist/schema/system-tables.js +12 -1
  65. package/dist/schema/system-tables.js.map +1 -1
  66. package/dist/sync/ClientSessionSyncProcessor.d.ts +9 -1
  67. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  68. package/dist/sync/ClientSessionSyncProcessor.js +49 -43
  69. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  70. package/dist/sync/sync.d.ts +6 -5
  71. package/dist/sync/sync.d.ts.map +1 -1
  72. package/dist/sync/sync.js.map +1 -1
  73. package/dist/sync/syncstate.d.ts +60 -84
  74. package/dist/sync/syncstate.d.ts.map +1 -1
  75. package/dist/sync/syncstate.js +127 -136
  76. package/dist/sync/syncstate.js.map +1 -1
  77. package/dist/sync/syncstate.test.js +205 -276
  78. package/dist/sync/syncstate.test.js.map +1 -1
  79. package/dist/version.d.ts +1 -1
  80. package/dist/version.js +1 -1
  81. package/package.json +2 -2
  82. package/src/adapter-types.ts +11 -13
  83. package/src/devtools/devtools-messages-common.ts +9 -0
  84. package/src/devtools/devtools-messages-leader.ts +1 -2
  85. package/src/leader-thread/LeaderSyncProcessor.ts +399 -364
  86. package/src/leader-thread/apply-mutation.ts +81 -71
  87. package/src/leader-thread/leader-worker-devtools.ts +5 -7
  88. package/src/leader-thread/make-leader-thread-layer.ts +26 -41
  89. package/src/leader-thread/mod.ts +1 -1
  90. package/src/leader-thread/mutationlog.ts +166 -13
  91. package/src/leader-thread/recreate-db.ts +4 -3
  92. package/src/leader-thread/types.ts +33 -23
  93. package/src/rehydrate-from-mutationlog.ts +12 -12
  94. package/src/schema/EventId.ts +20 -2
  95. package/src/schema/MutationEvent.ts +32 -16
  96. package/src/schema/schema.ts +1 -1
  97. package/src/schema/system-tables.ts +20 -1
  98. package/src/sync/ClientSessionSyncProcessor.ts +59 -47
  99. package/src/sync/sync.ts +6 -9
  100. package/src/sync/syncstate.test.ts +239 -315
  101. package/src/sync/syncstate.ts +191 -188
  102. package/src/version.ts +1 -1
  103. package/tmp/pack.tgz +0 -0
  104. package/src/leader-thread/pull-queue-set.ts +0 -67
@@ -22,457 +22,386 @@ class TestEvent extends MutationEvent.EncodedWithMeta {
22
22
  return this.rebase(parentId, this.isLocal);
23
23
  };
24
24
  }
25
- const e_r_1 = new TestEvent({ global: -1, client: 1 }, EventId.ROOT, 'a', true);
26
- const e_0_0 = new TestEvent({ global: 0, client: 0 }, EventId.ROOT, 'a', false);
27
- const e_0_1 = new TestEvent({ global: 0, client: 1 }, e_0_0.id, 'a', true);
28
- const e_0_2 = new TestEvent({ global: 0, client: 2 }, e_0_1.id, 'a', true);
29
- const e_0_3 = new TestEvent({ global: 0, client: 3 }, e_0_2.id, 'a', true);
30
- const e_1_0 = new TestEvent({ global: 1, client: 0 }, e_0_0.id, 'a', false);
25
+ const e_0_1 = new TestEvent({ global: 0, client: 1 }, EventId.ROOT, 'a', true);
26
+ const e_1_0 = new TestEvent({ global: 1, client: 0 }, EventId.ROOT, 'a', false);
31
27
  const e_1_1 = new TestEvent({ global: 1, client: 1 }, e_1_0.id, 'a', true);
28
+ const e_1_2 = new TestEvent({ global: 1, client: 2 }, e_1_1.id, 'a', true);
29
+ const e_1_3 = new TestEvent({ global: 1, client: 3 }, e_1_2.id, 'a', true);
32
30
  const e_2_0 = new TestEvent({ global: 2, client: 0 }, e_1_0.id, 'a', false);
31
+ const e_2_1 = new TestEvent({ global: 2, client: 1 }, e_2_0.id, 'a', true);
32
+ const e_3_0 = new TestEvent({ global: 3, client: 0 }, e_2_0.id, 'a', false);
33
33
  const isEqualEvent = MutationEvent.isEqualEncoded;
34
34
  const isClientEvent = (event) => event.isLocal;
35
35
  describe('syncstate', () => {
36
- describe('updateSyncState', () => {
37
- const update = ({ syncState, payload, ignoreClientEvents = false, }) => SyncState.updateSyncState({ syncState, payload, isClientEvent, isEqualEvent, ignoreClientEvents });
38
- describe.each([{ trimRollbackUntil: false }, { trimRollbackUntil: true }])('upstream-rebase (trimRollbackUntil: $trimRollbackUntil)', ({ trimRollbackUntil }) => {
36
+ describe('merge', () => {
37
+ const merge = ({ syncState, payload, ignoreClientEvents = false, }) => SyncState.merge({ syncState, payload, isClientEvent, isEqualEvent, ignoreClientEvents });
38
+ describe('upstream-rebase', () => {
39
39
  it('should rollback until start', () => {
40
40
  const syncState = new SyncState.SyncState({
41
- pending: [e_1_0],
42
- rollbackTail: [e_0_0, e_0_1],
41
+ pending: [e_2_0],
43
42
  upstreamHead: EventId.ROOT,
44
- localHead: e_1_0.id,
43
+ localHead: e_2_0.id,
45
44
  });
46
- const e_0_0_e_1_0 = e_0_0.rebase_(e_1_0.id);
47
- const e_0_1_e_1_1 = e_0_1.rebase_(e_0_0_e_1_0.id);
48
- const result = update({
45
+ const e_1_0_e_2_0 = e_1_0.rebase_(e_2_0.id);
46
+ const e_1_1_e_2_1 = e_1_1.rebase_(e_1_0_e_2_0.id);
47
+ const result = merge({
49
48
  syncState,
50
- payload: {
51
- _tag: 'upstream-rebase',
52
- rollbackUntil: e_0_0.id,
53
- newEvents: [e_0_0_e_1_0, e_0_1_e_1_1],
54
- trimRollbackUntil: trimRollbackUntil ? e_0_1_e_1_1.id : undefined,
55
- },
56
- });
57
- const e_1_0_e_2_0 = e_1_0.rebase_(e_0_0_e_1_0.id);
58
- expectRebase(result);
59
- expectEventArraysEqual(result.newSyncState.pending, [e_1_0_e_2_0]);
60
- if (trimRollbackUntil) {
61
- expectEventArraysEqual(result.newSyncState.rollbackTail, []);
62
- }
63
- else {
64
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0_e_1_0, e_0_1_e_1_1]);
65
- }
66
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1_e_1_1.id);
67
- expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id);
68
- expectEventArraysEqual(result.newEvents, [e_0_0_e_1_0, e_0_1_e_1_1, e_1_0_e_2_0]);
69
- expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0]);
70
- });
71
- it('should rollback only to specified point', () => {
72
- const syncState = new SyncState.SyncState({
73
- pending: [e_1_0],
74
- rollbackTail: [e_0_0, e_0_1],
75
- upstreamHead: EventId.ROOT,
76
- localHead: e_1_0.id,
49
+ payload: SyncState.PayloadUpstreamRebase.make({
50
+ rollbackEvents: [e_1_0, e_1_1],
51
+ newEvents: [e_1_0_e_2_0, e_1_1_e_2_1],
52
+ }),
77
53
  });
78
- const e_0_1_e_1_0 = e_0_1.rebase_(e_0_0.id);
79
- const result = update({
80
- syncState,
81
- payload: {
82
- _tag: 'upstream-rebase',
83
- rollbackUntil: e_0_1.id,
84
- newEvents: [e_0_1_e_1_0],
85
- trimRollbackUntil: trimRollbackUntil ? e_0_0.id : undefined,
86
- },
87
- });
88
- const e_1_0_e_2_0 = e_1_0.rebase_(e_0_1_e_1_0.id);
54
+ const e_2_0_e_3_0 = e_2_0.rebase_(e_1_0_e_2_0.id);
89
55
  expectRebase(result);
90
- expectEventArraysEqual(result.newSyncState.pending, [e_1_0_e_2_0]);
91
- if (trimRollbackUntil) {
92
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1_e_1_0]);
93
- }
94
- else {
95
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1_e_1_0]);
96
- }
97
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1_e_1_0.id);
98
- expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id);
99
- expectEventArraysEqual(result.newEvents, [e_0_1_e_1_0, e_1_0_e_2_0]);
100
- expectEventArraysEqual(result.eventsToRollback, [e_0_1, e_1_0]);
56
+ expectEventArraysEqual(result.newSyncState.pending, [e_2_0_e_3_0]);
57
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1_e_2_1.id);
58
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0_e_3_0.id);
59
+ expectEventArraysEqual(result.newEvents, [e_1_0_e_2_0, e_1_1_e_2_1, e_2_0_e_3_0]);
60
+ expectEventArraysEqual(result.rollbackEvents, [e_1_0, e_1_1, e_2_0]);
101
61
  });
102
- it('should work for empty pending', () => {
62
+ it('should rollback only to specified point', () => {
103
63
  const syncState = new SyncState.SyncState({
104
- pending: [],
105
- rollbackTail: [e_0_0],
64
+ pending: [e_2_0],
106
65
  upstreamHead: EventId.ROOT,
107
- localHead: e_0_0.id,
66
+ localHead: e_2_0.id,
108
67
  });
109
- const result = update({
68
+ const e_1_1_e_2_0 = e_1_1.rebase_(e_1_0.id);
69
+ const result = merge({
110
70
  syncState,
111
- payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [e_1_0] },
71
+ payload: SyncState.PayloadUpstreamRebase.make({
72
+ newEvents: [e_1_1_e_2_0],
73
+ rollbackEvents: [e_1_1],
74
+ }),
112
75
  });
76
+ const e_2_0_e_3_0 = e_2_0.rebase_(e_1_1_e_2_0.id);
113
77
  expectRebase(result);
114
- expectEventArraysEqual(result.newSyncState.pending, []);
115
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_1_0]);
116
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
117
- expect(result.newSyncState.localHead).toMatchObject(e_1_0.id);
118
- expect(result.newEvents).toStrictEqual([e_1_0]);
119
- });
120
- it('should fail for empty rollback tail', () => {
121
- const syncState = new SyncState.SyncState({
122
- pending: [],
123
- rollbackTail: [],
124
- upstreamHead: EventId.ROOT,
125
- localHead: e_0_0.id,
126
- });
127
- const result = update({
128
- syncState,
129
- payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [e_1_0] },
130
- });
131
- expect(result).toMatchObject({ _tag: 'unexpected-error' });
78
+ expectEventArraysEqual(result.newSyncState.pending, [e_2_0_e_3_0]);
79
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1_e_2_0.id);
80
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0_e_3_0.id);
81
+ expectEventArraysEqual(result.newEvents, [e_1_1_e_2_0, e_2_0_e_3_0]);
82
+ expectEventArraysEqual(result.rollbackEvents, [e_1_1, e_2_0]);
132
83
  });
133
- it('should work for empty incoming', () => {
84
+ it('should work for empty pending', () => {
134
85
  const syncState = new SyncState.SyncState({
135
86
  pending: [],
136
- rollbackTail: [e_0_0],
137
87
  upstreamHead: EventId.ROOT,
138
- localHead: e_0_0.id,
88
+ localHead: e_1_0.id,
139
89
  });
140
- const result = update({
90
+ const result = merge({
141
91
  syncState,
142
- payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [] },
92
+ payload: SyncState.PayloadUpstreamRebase.make({ rollbackEvents: [e_1_0], newEvents: [e_2_0] }),
143
93
  });
144
94
  expectRebase(result);
145
95
  expectEventArraysEqual(result.newSyncState.pending, []);
146
- expectEventArraysEqual(result.newSyncState.rollbackTail, []);
147
- expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT);
148
- expect(result.newSyncState.localHead).toMatchObject(EventId.ROOT);
149
- expect(result.newEvents).toStrictEqual([]);
96
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_0.id);
97
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0.id);
98
+ expect(result.newEvents).toStrictEqual([e_2_0]);
150
99
  });
151
100
  });
152
101
  describe('upstream-advance: advance', () => {
153
102
  it('should throw error if newEvents are not sorted in ascending order by eventId (client)', () => {
154
103
  const syncState = new SyncState.SyncState({
155
- pending: [e_0_0],
156
- rollbackTail: [],
104
+ pending: [e_1_0],
157
105
  upstreamHead: EventId.ROOT,
158
- localHead: e_0_0.id,
106
+ localHead: e_1_0.id,
159
107
  });
160
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_0] } });
108
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_1, e_1_0] } });
161
109
  expect(result).toMatchObject({ _tag: 'unexpected-error' });
162
110
  });
163
111
  it('should throw error if newEvents are not sorted in ascending order by eventId (global)', () => {
164
112
  const syncState = new SyncState.SyncState({
165
- pending: [e_0_0],
166
- rollbackTail: [],
113
+ pending: [e_1_0],
167
114
  upstreamHead: EventId.ROOT,
168
- localHead: e_0_0.id,
115
+ localHead: e_1_0.id,
169
116
  });
170
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0, e_0_0] } });
117
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_2_0, e_1_0] } });
171
118
  expect(result).toMatchObject({ _tag: 'unexpected-error' });
172
119
  });
173
120
  it('should throw error if incoming event is < expected upstream head', () => {
174
121
  const syncState = new SyncState.SyncState({
175
122
  pending: [],
176
- rollbackTail: [],
177
- upstreamHead: e_1_0.id,
178
- localHead: e_1_0.id,
123
+ upstreamHead: e_2_0.id,
124
+ localHead: e_2_0.id,
179
125
  });
180
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } });
126
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } });
181
127
  expect(result).toMatchObject({ _tag: 'unexpected-error' });
182
128
  });
183
129
  it('should throw error if incoming event is = expected upstream head', () => {
184
130
  const syncState = new SyncState.SyncState({
185
131
  pending: [],
186
- rollbackTail: [],
187
- upstreamHead: e_1_0.id,
188
- localHead: e_1_0.id,
132
+ upstreamHead: e_2_0.id,
133
+ localHead: e_2_0.id,
189
134
  });
190
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } });
135
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_2_0] } });
191
136
  expect(result).toMatchObject({ _tag: 'unexpected-error' });
192
137
  });
193
138
  it('should throw if the parent id of the first incoming event is unknown', () => {
194
139
  const syncState = new SyncState.SyncState({
195
140
  pending: [],
196
- rollbackTail: [e_0_0],
197
141
  upstreamHead: EventId.ROOT,
198
- localHead: e_0_0.id,
142
+ localHead: e_1_0.id,
199
143
  });
200
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_2_0] } });
144
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_3_0] } });
201
145
  expect(result).toMatchObject({ _tag: 'unexpected-error' });
202
146
  });
203
- it('should acknowledge pending event when receiving matching event', () => {
147
+ it('should confirm pending event when receiving matching event', () => {
204
148
  const syncState = new SyncState.SyncState({
205
- pending: [e_0_0],
206
- rollbackTail: [],
149
+ pending: [e_1_0],
207
150
  upstreamHead: EventId.ROOT,
208
- localHead: e_0_0.id,
151
+ localHead: e_1_0.id,
209
152
  });
210
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } });
153
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } });
211
154
  expectAdvance(result);
212
155
  expectEventArraysEqual(result.newSyncState.pending, []);
213
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0]);
214
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id);
215
- expect(result.newSyncState.localHead).toMatchObject(e_0_0.id);
216
- expect(result.newEvents).toStrictEqual([]);
156
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
157
+ expect(result.newSyncState.localHead).toMatchObject(e_1_0.id);
158
+ expectEventArraysEqual(result.newEvents, []);
159
+ expectEventArraysEqual(result.confirmedEvents, [e_1_0]);
217
160
  });
218
- it('should acknowledge partial pending event when receiving matching event', () => {
161
+ it('should confirm partial pending event when receiving matching event', () => {
219
162
  const syncState = new SyncState.SyncState({
220
- pending: [e_0_0, e_1_0],
221
- rollbackTail: [],
163
+ pending: [e_1_0, e_2_0],
222
164
  upstreamHead: EventId.ROOT,
223
- localHead: e_1_0.id,
165
+ localHead: e_2_0.id,
224
166
  });
225
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } });
167
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } });
226
168
  expectAdvance(result);
227
- expectEventArraysEqual(result.newSyncState.pending, [e_1_0]);
228
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0]);
229
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id);
230
- expect(result.newSyncState.localHead).toMatchObject(e_1_0.id);
231
- expect(result.newEvents).toStrictEqual([]);
169
+ expectEventArraysEqual(result.newSyncState.pending, [e_2_0]);
170
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
171
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0.id);
172
+ expectEventArraysEqual(result.newEvents, []);
173
+ expectEventArraysEqual(result.confirmedEvents, [e_1_0]);
232
174
  });
233
- it('should acknowledge pending event and add new event', () => {
175
+ it('should confirm pending event and add new event', () => {
234
176
  const syncState = new SyncState.SyncState({
235
- pending: [e_0_0],
236
- rollbackTail: [],
177
+ pending: [e_1_0],
237
178
  upstreamHead: EventId.ROOT,
238
- localHead: e_0_0.id,
179
+ localHead: e_1_0.id,
239
180
  });
240
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_1] } });
181
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0, e_1_1] } });
241
182
  expectAdvance(result);
242
183
  expectEventArraysEqual(result.newSyncState.pending, []);
243
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1]);
244
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id);
245
- expect(result.newSyncState.localHead).toMatchObject(e_0_1.id);
246
- expect(result.newEvents).toStrictEqual([e_0_1]);
184
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1.id);
185
+ expect(result.newSyncState.localHead).toMatchObject(e_1_1.id);
186
+ expect(result.newEvents).toStrictEqual([e_1_1]);
187
+ expectEventArraysEqual(result.confirmedEvents, [e_1_0]);
247
188
  });
248
- it('should acknowledge pending event and add multiple new events', () => {
189
+ it('should confirm pending event and add multiple new events', () => {
249
190
  const syncState = new SyncState.SyncState({
250
- pending: [e_0_1],
251
- rollbackTail: [],
252
- upstreamHead: e_0_0.id,
253
- localHead: e_0_1.id,
191
+ pending: [e_1_1],
192
+ upstreamHead: e_1_0.id,
193
+ localHead: e_1_1.id,
254
194
  });
255
- const result = update({
195
+ const result = merge({
256
196
  syncState,
257
- payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1] },
197
+ payload: { _tag: 'upstream-advance', newEvents: [e_1_1, e_1_2, e_1_3, e_2_0, e_2_1] },
258
198
  });
259
199
  expectAdvance(result);
260
200
  expectEventArraysEqual(result.newSyncState.pending, []);
261
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1]);
262
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1.id);
201
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_1.id);
202
+ expect(result.newSyncState.localHead).toMatchObject(e_2_1.id);
203
+ expect(result.newEvents).toStrictEqual([e_1_2, e_1_3, e_2_0, e_2_1]);
204
+ expectEventArraysEqual(result.confirmedEvents, [e_1_1]);
205
+ });
206
+ it('should confirm pending global event while keep pending client events', () => {
207
+ const syncState = new SyncState.SyncState({
208
+ pending: [e_1_0, e_1_1],
209
+ upstreamHead: EventId.ROOT,
210
+ localHead: e_1_1.id,
211
+ });
212
+ const result = merge({
213
+ syncState,
214
+ payload: { _tag: 'upstream-advance', newEvents: [e_1_0] },
215
+ });
216
+ expectAdvance(result);
217
+ expectEventArraysEqual(result.newSyncState.pending, [e_1_1]);
218
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
263
219
  expect(result.newSyncState.localHead).toMatchObject(e_1_1.id);
264
- expect(result.newEvents).toStrictEqual([e_0_2, e_0_3, e_1_0, e_1_1]);
220
+ expectEventArraysEqual(result.newEvents, []);
221
+ expectEventArraysEqual(result.confirmedEvents, [e_1_0]);
265
222
  });
266
223
  it('should ignore client events (incoming is subset of pending)', () => {
267
224
  const syncState = new SyncState.SyncState({
268
- pending: [e_r_1, e_0_0],
269
- rollbackTail: [],
225
+ pending: [e_0_1, e_1_0],
270
226
  upstreamHead: EventId.ROOT,
271
- localHead: e_0_0.id,
227
+ localHead: e_1_0.id,
272
228
  });
273
- const result = update({
229
+ const result = merge({
274
230
  syncState,
275
- payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
231
+ payload: { _tag: 'upstream-advance', newEvents: [e_1_0] },
276
232
  ignoreClientEvents: true,
277
233
  });
278
234
  expectAdvance(result);
279
235
  expectEventArraysEqual(result.newSyncState.pending, []);
280
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0]);
281
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id);
282
- expect(result.newSyncState.localHead).toMatchObject(e_0_0.id);
283
- expect(result.newEvents).toStrictEqual([]);
236
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
237
+ expect(result.newSyncState.localHead).toMatchObject(e_1_0.id);
238
+ expectEventArraysEqual(result.newEvents, []);
239
+ expectEventArraysEqual(result.confirmedEvents, [e_0_1, e_1_0]);
284
240
  });
285
241
  it('should ignore client events (incoming is subset of pending case 2)', () => {
286
242
  const syncState = new SyncState.SyncState({
287
- pending: [e_r_1, e_0_0, e_1_0],
288
- rollbackTail: [],
243
+ pending: [e_0_1, e_1_0, e_2_0],
289
244
  upstreamHead: EventId.ROOT,
290
- localHead: e_0_0.id,
245
+ localHead: e_1_0.id,
291
246
  });
292
- const result = update({
247
+ const result = merge({
293
248
  syncState,
294
- payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
249
+ payload: { _tag: 'upstream-advance', newEvents: [e_1_0] },
295
250
  ignoreClientEvents: true,
296
251
  });
297
252
  expectAdvance(result);
298
- expectEventArraysEqual(result.newSyncState.pending, [e_1_0]);
299
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0]);
300
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id);
301
- expect(result.newSyncState.localHead).toMatchObject(e_1_0.id);
302
- expect(result.newEvents).toStrictEqual([]);
253
+ expectEventArraysEqual(result.newSyncState.pending, [e_2_0]);
254
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
255
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0.id);
256
+ expectEventArraysEqual(result.newEvents, []);
257
+ expectEventArraysEqual(result.confirmedEvents, [e_0_1, e_1_0]);
303
258
  });
304
259
  it('should ignore client events (incoming goes beyond pending)', () => {
305
260
  const syncState = new SyncState.SyncState({
306
- pending: [e_r_1, e_0_0, e_0_1],
307
- rollbackTail: [],
261
+ pending: [e_0_1, e_1_0, e_1_1],
308
262
  upstreamHead: EventId.ROOT,
309
- localHead: e_0_1.id,
263
+ localHead: e_1_1.id,
310
264
  });
311
- const result = update({
265
+ const result = merge({
312
266
  syncState,
313
- payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_1_0] },
267
+ payload: { _tag: 'upstream-advance', newEvents: [e_1_0, e_2_0] },
314
268
  ignoreClientEvents: true,
315
269
  });
316
270
  expectAdvance(result);
317
271
  expectEventArraysEqual(result.newSyncState.pending, []);
318
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0, e_0_1, e_1_0]);
319
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
320
- expect(result.newSyncState.localHead).toMatchObject(e_1_0.id);
321
- expect(result.newEvents).toStrictEqual([e_1_0]);
272
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_0.id);
273
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0.id);
274
+ expect(result.newEvents).toStrictEqual([e_2_0]);
275
+ expectEventArraysEqual(result.confirmedEvents, [e_0_1, e_1_0, e_1_1]);
322
276
  });
323
277
  it('should fail if incoming event is ≤ local head', () => {
324
278
  const syncState = new SyncState.SyncState({
325
279
  pending: [],
326
- rollbackTail: [],
327
- upstreamHead: e_1_0.id,
328
- localHead: e_1_0.id,
280
+ upstreamHead: e_2_0.id,
281
+ localHead: e_2_0.id,
329
282
  });
330
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } });
283
+ const result = merge({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } });
331
284
  expect(result).toMatchObject({ _tag: 'unexpected-error' });
332
285
  });
333
286
  });
334
287
  describe('upstream-advance: rebase', () => {
335
288
  it('should rebase single client event to end', () => {
336
289
  const syncState = new SyncState.SyncState({
337
- pending: [e_0_0],
338
- rollbackTail: [],
339
- upstreamHead: EventId.ROOT,
340
- localHead: e_0_0.id,
341
- });
342
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1] } });
343
- const e_0_0_e_0_2 = e_0_0.rebase_(e_0_1.id);
344
- expectRebase(result);
345
- expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_0_2]);
346
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1]);
347
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id);
348
- expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_0_2.id);
349
- expectEventArraysEqual(result.eventsToRollback, [e_0_0]);
350
- expectEventArraysEqual(result.newEvents, [e_0_1, e_0_0_e_0_2]);
351
- });
352
- it('should rebase different event with same id (no rollback tail)', () => {
353
- const e_0_0_b = new TestEvent({ global: 0, client: 0 }, EventId.ROOT, '0_0_b', true);
354
- const syncState = new SyncState.SyncState({
355
- pending: [e_0_0_b],
356
- rollbackTail: [],
290
+ pending: [e_1_0],
357
291
  upstreamHead: EventId.ROOT,
358
- localHead: e_0_0_b.id,
292
+ localHead: e_1_0.id,
359
293
  });
360
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } });
361
- const e_0_0_e_1_0 = e_0_0_b.rebase_(e_0_0.id);
294
+ const result = merge({ syncState, payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e_1_1] }) });
295
+ const e_1_0_e_1_2 = e_1_0.rebase_(e_1_1.id);
362
296
  expectRebase(result);
363
- expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_1_0]);
364
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0]);
365
- expectEventArraysEqual(result.newEvents, [e_0_0, e_0_0_e_1_0]);
366
- expectEventArraysEqual(result.eventsToRollback, [e_0_0_b]);
367
- expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id);
368
- expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_1_0.id);
297
+ expectEventArraysEqual(result.newSyncState.pending, [e_1_0_e_1_2]);
298
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1.id);
299
+ expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_1_2.id);
300
+ expectEventArraysEqual(result.rollbackEvents, [e_1_0]);
301
+ expectEventArraysEqual(result.newEvents, [e_1_1, e_1_0_e_1_2]);
369
302
  });
370
303
  it('should rebase different event with same id', () => {
371
- const e_1_0_b = new TestEvent({ global: 1, client: 0 }, e_0_0.id, '1_0_b', false);
304
+ const e_2_0_b = new TestEvent({ global: 1, client: 0 }, e_1_0.id, '1_0_b', false);
372
305
  const syncState = new SyncState.SyncState({
373
- pending: [e_1_0_b],
374
- rollbackTail: [e_0_0, e_0_1],
306
+ pending: [e_2_0_b],
375
307
  upstreamHead: EventId.ROOT,
376
- localHead: e_1_0_b.id,
308
+ localHead: e_2_0_b.id,
377
309
  });
378
- const result = update({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } });
379
- const e_1_0_e_2_0 = e_1_0_b.rebase_(e_1_0.id);
310
+ const result = merge({ syncState, payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e_2_0] }) });
311
+ const e_2_0_e_3_0 = e_2_0_b.rebase_(e_2_0.id);
380
312
  expectRebase(result);
381
- expectEventArraysEqual(result.newSyncState.pending, [e_1_0_e_2_0]);
382
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1, e_1_0]);
383
- expectEventArraysEqual(result.newEvents, [e_1_0, e_1_0_e_2_0]);
384
- expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0_b]);
385
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
386
- expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id);
313
+ expectEventArraysEqual(result.newSyncState.pending, [e_2_0_e_3_0]);
314
+ expectEventArraysEqual(result.newEvents, [e_2_0, e_2_0_e_3_0]);
315
+ expectEventArraysEqual(result.rollbackEvents, [e_2_0_b]);
316
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_0.id);
317
+ expect(result.newSyncState.localHead).toMatchObject(e_2_0_e_3_0.id);
387
318
  });
388
319
  it('should rebase single client event to end (more incoming events)', () => {
389
320
  const syncState = new SyncState.SyncState({
390
- pending: [e_0_0],
391
- rollbackTail: [],
321
+ pending: [e_1_0],
392
322
  upstreamHead: EventId.ROOT,
393
- localHead: e_0_0.id,
323
+ localHead: e_1_0.id,
394
324
  });
395
- const result = update({
325
+ const result = merge({
396
326
  syncState,
397
- payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
327
+ payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e_1_1, e_1_2, e_1_3, e_2_0] }),
398
328
  });
399
- const e_0_0_e_2_0 = e_0_0.rebase_(e_1_0.id);
329
+ const e_1_0_e_3_0 = e_1_0.rebase_(e_2_0.id);
400
330
  expectRebase(result);
401
- expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_2_0]);
402
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0]);
403
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
404
- expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_2_0.id);
331
+ expectEventArraysEqual(result.newSyncState.pending, [e_1_0_e_3_0]);
332
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_0.id);
333
+ expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_3_0.id);
405
334
  });
406
335
  it('should only rebase divergent events when first event matches', () => {
407
336
  const syncState = new SyncState.SyncState({
408
- pending: [e_0_0, e_0_1],
409
- rollbackTail: [],
337
+ pending: [e_1_0, e_1_1],
410
338
  upstreamHead: EventId.ROOT,
411
- localHead: e_0_0.id,
339
+ localHead: e_1_0.id,
412
340
  });
413
- const result = update({
341
+ const result = merge({
414
342
  syncState,
415
- payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_2, e_0_3, e_1_0] },
343
+ payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e_1_0, e_1_2, e_1_3, e_2_0] }),
416
344
  });
417
- const e_0_1_e_1_1 = e_0_1.rebase_(e_1_0.id);
345
+ const e_1_1_e_2_1 = e_1_1.rebase_(e_2_0.id);
418
346
  expectRebase(result);
419
- expectEventArraysEqual(result.newSyncState.pending, [e_0_1_e_1_1]);
420
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_2, e_0_3, e_1_0]);
421
- expectEventArraysEqual(result.eventsToRollback, [e_0_1]);
422
- expectEventArraysEqual(result.newEvents, [e_0_2, e_0_3, e_1_0, e_0_1_e_1_1]);
423
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
424
- expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_1.id);
347
+ expectEventArraysEqual(result.newSyncState.pending, [e_1_1_e_2_1]);
348
+ expectEventArraysEqual(result.rollbackEvents, [e_1_1]);
349
+ expectEventArraysEqual(result.newEvents, [e_1_2, e_1_3, e_2_0, e_1_1_e_2_1]);
350
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_0.id);
351
+ expect(result.newSyncState.localHead).toMatchObject(e_1_1_e_2_1.id);
425
352
  });
426
353
  it('should rebase all client events when incoming chain starts differently', () => {
427
354
  const syncState = new SyncState.SyncState({
428
- pending: [e_0_0, e_0_1],
429
- rollbackTail: [],
355
+ pending: [e_1_0, e_1_1],
430
356
  upstreamHead: EventId.ROOT,
431
- localHead: e_0_1.id,
357
+ localHead: e_1_1.id,
432
358
  });
433
- const result = update({
359
+ const result = merge({
434
360
  syncState,
435
- payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
361
+ payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e_1_1, e_1_2, e_1_3, e_2_0] }),
436
362
  });
437
- const e_0_0_e_1_1 = e_0_0.rebase_(e_1_0.id);
438
- const e_0_1_e_1_2 = e_0_1.rebase_(e_0_0_e_1_1.id);
363
+ const e_1_0_e_2_1 = e_1_0.rebase_(e_2_0.id);
364
+ const e_1_1_e_2_2 = e_1_1.rebase_(e_1_0_e_2_1.id);
439
365
  expectRebase(result);
440
- expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_1_1, e_0_1_e_1_2]);
441
- expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0]);
442
- expectEventArraysEqual(result.newEvents, [e_0_1, e_0_2, e_0_3, e_1_0, e_0_0_e_1_1, e_0_1_e_1_2]);
443
- expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1]);
444
- expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id);
445
- expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_2.id);
366
+ expectEventArraysEqual(result.newSyncState.pending, [e_1_0_e_2_1, e_1_1_e_2_2]);
367
+ expectEventArraysEqual(result.newEvents, [e_1_1, e_1_2, e_1_3, e_2_0, e_1_0_e_2_1, e_1_1_e_2_2]);
368
+ expectEventArraysEqual(result.rollbackEvents, [e_1_0, e_1_1]);
369
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_2_0.id);
370
+ expect(result.newSyncState.localHead).toMatchObject(e_1_1_e_2_2.id);
446
371
  });
447
372
  describe('local-push', () => {
448
373
  describe('advance', () => {
449
374
  it('should advance with new events', () => {
450
375
  const syncState = new SyncState.SyncState({
451
- pending: [e_0_0],
452
- rollbackTail: [],
376
+ pending: [e_1_0],
453
377
  upstreamHead: EventId.ROOT,
454
- localHead: e_0_0.id,
378
+ localHead: e_1_0.id,
379
+ });
380
+ const result = merge({
381
+ syncState,
382
+ payload: SyncState.PayloadLocalPush.make({ newEvents: [e_1_1, e_1_2, e_1_3] }),
455
383
  });
456
- const result = update({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2, e_0_3] } });
457
384
  expectAdvance(result);
458
- expectEventArraysEqual(result.newSyncState.pending, [e_0_0, e_0_1, e_0_2, e_0_3]);
459
- expectEventArraysEqual(result.newSyncState.rollbackTail, []);
385
+ expectEventArraysEqual(result.newSyncState.pending, [e_1_0, e_1_1, e_1_2, e_1_3]);
460
386
  expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT);
461
- expect(result.newSyncState.localHead).toMatchObject(e_0_3.id);
462
- expectEventArraysEqual(result.newEvents, [e_0_1, e_0_2, e_0_3]);
387
+ expect(result.newSyncState.localHead).toMatchObject(e_1_3.id);
388
+ expectEventArraysEqual(result.newEvents, [e_1_1, e_1_2, e_1_3]);
389
+ expectEventArraysEqual(result.confirmedEvents, []);
463
390
  });
464
391
  });
465
392
  describe('reject', () => {
466
393
  it('should reject when new events are greater than pending events', () => {
467
394
  const syncState = new SyncState.SyncState({
468
- pending: [e_0_0, e_0_1],
469
- rollbackTail: [],
395
+ pending: [e_1_0, e_1_1],
470
396
  upstreamHead: EventId.ROOT,
471
- localHead: e_0_1.id,
397
+ localHead: e_1_1.id,
398
+ });
399
+ const result = merge({
400
+ syncState,
401
+ payload: SyncState.PayloadLocalPush.make({ newEvents: [e_1_1, e_1_2] }),
472
402
  });
473
- const result = update({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2] } });
474
403
  expectReject(result);
475
- expect(result.expectedMinimumId).toMatchObject(e_0_2.id);
404
+ expect(result.expectedMinimumId).toMatchObject(e_1_2.id);
476
405
  });
477
406
  });
478
407
  });
@@ -492,7 +421,7 @@ function expectAdvance(result) {
492
421
  expect(result._tag).toBe('advance');
493
422
  }
494
423
  function expectRebase(result) {
495
- expect(result._tag).toBe('rebase');
424
+ expect(result._tag, `Expected rebase, got ${result}`).toBe('rebase');
496
425
  }
497
426
  function expectReject(result) {
498
427
  expect(result._tag).toBe('reject');