@livestore/common 0.3.0-dev.8 → 0.3.0-dev.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/devtools/devtools-messages.d.ts +39 -39
  3. package/dist/leader-thread/LeaderSyncProcessor.d.ts +16 -26
  4. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  5. package/dist/leader-thread/LeaderSyncProcessor.js +146 -157
  6. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  7. package/dist/leader-thread/apply-mutation.js +1 -1
  8. package/dist/leader-thread/apply-mutation.js.map +1 -1
  9. package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
  10. package/dist/leader-thread/types.d.ts +9 -3
  11. package/dist/leader-thread/types.d.ts.map +1 -1
  12. package/dist/leader-thread/types.js.map +1 -1
  13. package/dist/schema/MutationEvent.d.ts +6 -5
  14. package/dist/schema/MutationEvent.d.ts.map +1 -1
  15. package/dist/schema/MutationEvent.js.map +1 -1
  16. package/dist/sync/ClientSessionSyncProcessor.d.ts +1 -1
  17. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  18. package/dist/sync/ClientSessionSyncProcessor.js +5 -3
  19. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  20. package/dist/sync/syncstate.d.ts +39 -17
  21. package/dist/sync/syncstate.d.ts.map +1 -1
  22. package/dist/sync/syncstate.js +56 -12
  23. package/dist/sync/syncstate.js.map +1 -1
  24. package/dist/sync/syncstate.test.js +110 -51
  25. package/dist/sync/syncstate.test.js.map +1 -1
  26. package/dist/version.d.ts +1 -1
  27. package/dist/version.js +1 -1
  28. package/package.json +3 -3
  29. package/src/leader-thread/LeaderSyncProcessor.ts +196 -205
  30. package/src/leader-thread/apply-mutation.ts +1 -1
  31. package/src/leader-thread/types.ts +10 -2
  32. package/src/schema/MutationEvent.ts +5 -1
  33. package/src/sync/ClientSessionSyncProcessor.ts +6 -4
  34. package/src/sync/syncstate.test.ts +110 -51
  35. package/src/sync/syncstate.ts +21 -19
  36. package/src/version.ts +1 -1
@@ -37,7 +37,6 @@ const e_0_2 = new TestEvent({ global: 0, local: 2 }, e_0_1.id, 'a', true)
37
37
  const e_0_3 = new TestEvent({ global: 0, local: 3 }, e_0_2.id, 'a', true)
38
38
  const e_1_0 = new TestEvent({ global: 1, local: 0 }, e_0_0.id, 'a', false)
39
39
  const e_1_1 = new TestEvent({ global: 1, local: 1 }, e_1_0.id, 'a', true)
40
- const e_2_0 = new TestEvent({ global: 2, local: 0 }, e_1_0.id, 'a', false)
41
40
 
42
41
  const isEqualEvent = MutationEvent.isEqualEncoded
43
42
 
@@ -59,12 +58,12 @@ describe('syncstate', () => {
59
58
  'upstream-rebase (trimRollbackUntil: $trimRollbackUntil)',
60
59
  ({ trimRollbackUntil }) => {
61
60
  it('should rollback until start', () => {
62
- const syncState = {
61
+ const syncState = new SyncState.SyncState({
63
62
  pending: [e_1_0],
64
63
  rollbackTail: [e_0_0, e_0_1],
65
64
  upstreamHead: EventId.ROOT,
66
65
  localHead: e_1_0.id,
67
- }
66
+ })
68
67
  const e_0_0_e_1_0 = e_0_0.rebase_(e_1_0.id)
69
68
  const e_0_1_e_1_1 = e_0_1.rebase_(e_0_0_e_1_0.id)
70
69
  const result = run({
@@ -84,19 +83,19 @@ describe('syncstate', () => {
84
83
  } else {
85
84
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0_e_1_0, e_0_1_e_1_1])
86
85
  }
87
- expect(result.newSyncState.upstreamHead).toBe(e_0_1_e_1_1.id)
86
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1_e_1_1.id)
88
87
  expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
89
88
  expectEventArraysEqual(result.newEvents, [e_0_0_e_1_0, e_0_1_e_1_1])
90
89
  expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0])
91
90
  })
92
91
 
93
92
  it('should rollback only to specified point', () => {
94
- const syncState = {
93
+ const syncState = new SyncState.SyncState({
95
94
  pending: [e_1_0],
96
95
  rollbackTail: [e_0_0, e_0_1],
97
96
  upstreamHead: EventId.ROOT,
98
97
  localHead: e_1_0.id,
99
- }
98
+ })
100
99
  const e_0_1_e_1_0 = e_0_1.rebase_(e_0_0.id)
101
100
  const result = run({
102
101
  syncState,
@@ -115,14 +114,19 @@ describe('syncstate', () => {
115
114
  } else {
116
115
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1_e_1_0])
117
116
  }
118
- expect(result.newSyncState.upstreamHead).toBe(e_0_1_e_1_0.id)
117
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1_e_1_0.id)
119
118
  expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
120
119
  expectEventArraysEqual(result.newEvents, [e_0_1_e_1_0])
121
120
  expectEventArraysEqual(result.eventsToRollback, [e_0_1, e_1_0])
122
121
  })
123
122
 
124
123
  it('should work for empty pending', () => {
125
- const syncState = { pending: [], rollbackTail: [e_0_0], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
124
+ const syncState = new SyncState.SyncState({
125
+ pending: [],
126
+ rollbackTail: [e_0_0],
127
+ upstreamHead: EventId.ROOT,
128
+ localHead: e_0_0.id,
129
+ })
126
130
  const result = run({
127
131
  syncState,
128
132
  payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [e_1_0] },
@@ -130,13 +134,18 @@ describe('syncstate', () => {
130
134
  expectRebase(result)
131
135
  expectEventArraysEqual(result.newSyncState.pending, [])
132
136
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_1_0])
133
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
137
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
134
138
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
135
139
  expect(result.newEvents).toStrictEqual([e_1_0])
136
140
  })
137
141
 
138
142
  it('should fail for empty rollback tail', () => {
139
- const syncState = { pending: [], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
143
+ const syncState = new SyncState.SyncState({
144
+ pending: [],
145
+ rollbackTail: [],
146
+ upstreamHead: EventId.ROOT,
147
+ localHead: e_0_0.id,
148
+ })
140
149
  expect(() =>
141
150
  run({
142
151
  syncState,
@@ -146,7 +155,12 @@ describe('syncstate', () => {
146
155
  })
147
156
 
148
157
  it('should work for empty incoming', () => {
149
- const syncState = { pending: [], rollbackTail: [e_0_0], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
158
+ const syncState = new SyncState.SyncState({
159
+ pending: [],
160
+ rollbackTail: [e_0_0],
161
+ upstreamHead: EventId.ROOT,
162
+ localHead: e_0_0.id,
163
+ })
150
164
  const result = run({
151
165
  syncState,
152
166
  payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [] },
@@ -154,7 +168,7 @@ describe('syncstate', () => {
154
168
  expectRebase(result)
155
169
  expectEventArraysEqual(result.newSyncState.pending, [])
156
170
  expectEventArraysEqual(result.newSyncState.rollbackTail, [])
157
- expect(result.newSyncState.upstreamHead).toBe(EventId.ROOT)
171
+ expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT)
158
172
  expect(result.newSyncState.localHead).toMatchObject(EventId.ROOT)
159
173
  expect(result.newEvents).toStrictEqual([])
160
174
  })
@@ -163,58 +177,83 @@ describe('syncstate', () => {
163
177
 
164
178
  describe('upstream-advance: advance', () => {
165
179
  it('should throw error if newEvents are not sorted in ascending order by eventId (local)', () => {
166
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
180
+ const syncState = new SyncState.SyncState({
181
+ pending: [e_0_0],
182
+ rollbackTail: [],
183
+ upstreamHead: EventId.ROOT,
184
+ localHead: e_0_0.id,
185
+ })
167
186
  expect(() => run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_0] } })).toThrow()
168
187
  })
169
188
 
170
189
  it('should throw error if newEvents are not sorted in ascending order by eventId (global)', () => {
171
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
190
+ const syncState = new SyncState.SyncState({
191
+ pending: [e_0_0],
192
+ rollbackTail: [],
193
+ upstreamHead: EventId.ROOT,
194
+ localHead: e_0_0.id,
195
+ })
172
196
  expect(() => run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0, e_0_0] } })).toThrow()
173
197
  })
174
198
 
175
199
  it('should acknowledge pending event when receiving matching event', () => {
176
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
200
+ const syncState = new SyncState.SyncState({
201
+ pending: [e_0_0],
202
+ rollbackTail: [],
203
+ upstreamHead: EventId.ROOT,
204
+ localHead: e_0_0.id,
205
+ })
177
206
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
178
207
 
179
208
  expectAdvance(result)
180
209
  expectEventArraysEqual(result.newSyncState.pending, [])
181
210
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
182
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
211
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
183
212
  expect(result.newSyncState.localHead).toMatchObject(e_0_0.id)
184
213
  expect(result.newEvents).toStrictEqual([])
185
214
  })
186
215
 
187
216
  it('should acknowledge partial pending event when receiving matching event', () => {
188
- const syncState = {
217
+ const syncState = new SyncState.SyncState({
189
218
  pending: [e_0_0, e_1_0],
190
219
  rollbackTail: [],
191
220
  upstreamHead: EventId.ROOT,
192
221
  localHead: e_1_0.id,
193
- }
222
+ })
194
223
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
195
224
 
196
225
  expectAdvance(result)
197
226
  expectEventArraysEqual(result.newSyncState.pending, [e_1_0])
198
227
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
199
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
228
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
200
229
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
201
230
  expect(result.newEvents).toStrictEqual([])
202
231
  })
203
232
 
204
233
  it('should acknowledge pending event and add new event', () => {
205
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
234
+ const syncState = new SyncState.SyncState({
235
+ pending: [e_0_0],
236
+ rollbackTail: [],
237
+ upstreamHead: EventId.ROOT,
238
+ localHead: e_0_0.id,
239
+ })
206
240
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_1] } })
207
241
 
208
242
  expectAdvance(result)
209
243
  expectEventArraysEqual(result.newSyncState.pending, [])
210
244
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1])
211
- expect(result.newSyncState.upstreamHead).toBe(e_0_1.id)
245
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id)
212
246
  expect(result.newSyncState.localHead).toMatchObject(e_0_1.id)
213
247
  expect(result.newEvents).toStrictEqual([e_0_1])
214
248
  })
215
249
 
216
250
  it('should acknowledge pending event and add multiple new events', () => {
217
- const syncState = { pending: [e_0_1], rollbackTail: [], upstreamHead: e_0_0.id, localHead: e_0_1.id }
251
+ const syncState = new SyncState.SyncState({
252
+ pending: [e_0_1],
253
+ rollbackTail: [],
254
+ upstreamHead: e_0_0.id,
255
+ localHead: e_0_1.id,
256
+ })
218
257
  const result = run({
219
258
  syncState,
220
259
  payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1] },
@@ -223,18 +262,18 @@ describe('syncstate', () => {
223
262
  expectAdvance(result)
224
263
  expectEventArraysEqual(result.newSyncState.pending, [])
225
264
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1])
226
- expect(result.newSyncState.upstreamHead).toBe(e_1_1.id)
265
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1.id)
227
266
  expect(result.newSyncState.localHead).toMatchObject(e_1_1.id)
228
267
  expect(result.newEvents).toStrictEqual([e_0_2, e_0_3, e_1_0, e_1_1])
229
268
  })
230
269
 
231
270
  it('should ignore local events (incoming is subset of pending)', () => {
232
- const syncState = {
271
+ const syncState = new SyncState.SyncState({
233
272
  pending: [e_r_1, e_0_0],
234
273
  rollbackTail: [],
235
274
  upstreamHead: EventId.ROOT,
236
275
  localHead: e_0_0.id,
237
- }
276
+ })
238
277
  const result = run({
239
278
  syncState,
240
279
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
@@ -243,18 +282,18 @@ describe('syncstate', () => {
243
282
  expectAdvance(result)
244
283
  expectEventArraysEqual(result.newSyncState.pending, [])
245
284
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0])
246
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
285
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
247
286
  expect(result.newSyncState.localHead).toMatchObject(e_0_0.id)
248
287
  expect(result.newEvents).toStrictEqual([])
249
288
  })
250
289
 
251
290
  it('should ignore local events (incoming is subset of pending case 2)', () => {
252
- const syncState = {
291
+ const syncState = new SyncState.SyncState({
253
292
  pending: [e_r_1, e_0_0, e_1_0],
254
293
  rollbackTail: [],
255
294
  upstreamHead: EventId.ROOT,
256
295
  localHead: e_0_0.id,
257
- }
296
+ })
258
297
  const result = run({
259
298
  syncState,
260
299
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
@@ -263,18 +302,18 @@ describe('syncstate', () => {
263
302
  expectAdvance(result)
264
303
  expectEventArraysEqual(result.newSyncState.pending, [e_1_0])
265
304
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0])
266
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
305
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
267
306
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
268
307
  expect(result.newEvents).toStrictEqual([])
269
308
  })
270
309
 
271
310
  it('should ignore local events (incoming goes beyond pending)', () => {
272
- const syncState = {
311
+ const syncState = new SyncState.SyncState({
273
312
  pending: [e_r_1, e_0_0, e_0_1],
274
313
  rollbackTail: [],
275
314
  upstreamHead: EventId.ROOT,
276
315
  localHead: e_0_1.id,
277
- }
316
+ })
278
317
  const result = run({
279
318
  syncState,
280
319
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_1_0] },
@@ -284,7 +323,7 @@ describe('syncstate', () => {
284
323
  expectAdvance(result)
285
324
  expectEventArraysEqual(result.newSyncState.pending, [])
286
325
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0, e_0_1, e_1_0])
287
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
326
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
288
327
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
289
328
  expect(result.newEvents).toStrictEqual([e_1_0])
290
329
  })
@@ -292,7 +331,12 @@ describe('syncstate', () => {
292
331
 
293
332
  describe('upstream-advance: rebase', () => {
294
333
  it('should rebase single local event to end', () => {
295
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
334
+ const syncState = new SyncState.SyncState({
335
+ pending: [e_0_0],
336
+ rollbackTail: [],
337
+ upstreamHead: EventId.ROOT,
338
+ localHead: e_0_0.id,
339
+ })
296
340
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1] } })
297
341
 
298
342
  const e_0_0_e_0_2 = e_0_0.rebase_(e_0_1.id)
@@ -300,7 +344,7 @@ describe('syncstate', () => {
300
344
  expectRebase(result)
301
345
  expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_0_2])
302
346
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1])
303
- expect(result.newSyncState.upstreamHead).toBe(e_0_1.id)
347
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id)
304
348
  expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_0_2.id)
305
349
  expectEventArraysEqual(result.eventsToRollback, [e_0_0])
306
350
  expectEventArraysEqual(result.newEvents, [e_0_1, e_0_0_e_0_2])
@@ -308,7 +352,12 @@ describe('syncstate', () => {
308
352
 
309
353
  it('should rebase different event with same id (no rollback tail)', () => {
310
354
  const e_0_0_b = new TestEvent({ global: 0, local: 0 }, EventId.ROOT, '0_0_b', true)
311
- const syncState = { pending: [e_0_0_b], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0_b.id }
355
+ const syncState = new SyncState.SyncState({
356
+ pending: [e_0_0_b],
357
+ rollbackTail: [],
358
+ upstreamHead: EventId.ROOT,
359
+ localHead: e_0_0_b.id,
360
+ })
312
361
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
313
362
 
314
363
  const e_0_0_e_1_0 = e_0_0_b.rebase_(e_0_0.id)
@@ -318,18 +367,18 @@ describe('syncstate', () => {
318
367
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
319
368
  expectEventArraysEqual(result.newEvents, [e_0_0, e_0_0_e_1_0])
320
369
  expectEventArraysEqual(result.eventsToRollback, [e_0_0_b])
321
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
370
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
322
371
  expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_1_0.id)
323
372
  })
324
373
 
325
374
  it('should rebase different event with same id', () => {
326
375
  const e_1_0_b = new TestEvent({ global: 1, local: 0 }, e_0_0.id, '1_0_b', false)
327
- const syncState = {
376
+ const syncState = new SyncState.SyncState({
328
377
  pending: [e_1_0_b],
329
378
  rollbackTail: [e_0_0, e_0_1],
330
379
  upstreamHead: EventId.ROOT,
331
380
  localHead: e_1_0_b.id,
332
- }
381
+ })
333
382
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } })
334
383
  const e_1_0_e_2_0 = e_1_0_b.rebase_(e_1_0.id)
335
384
 
@@ -338,12 +387,17 @@ describe('syncstate', () => {
338
387
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1, e_1_0])
339
388
  expectEventArraysEqual(result.newEvents, [e_1_0, e_1_0_e_2_0])
340
389
  expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0_b])
341
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
390
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
342
391
  expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
343
392
  })
344
393
 
345
394
  it('should rebase single local event to end (more incoming events)', () => {
346
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
395
+ const syncState = new SyncState.SyncState({
396
+ pending: [e_0_0],
397
+ rollbackTail: [],
398
+ upstreamHead: EventId.ROOT,
399
+ localHead: e_0_0.id,
400
+ })
347
401
  const result = run({
348
402
  syncState,
349
403
  payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
@@ -354,17 +408,17 @@ describe('syncstate', () => {
354
408
  expectRebase(result)
355
409
  expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_2_0])
356
410
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0])
357
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
411
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
358
412
  expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_2_0.id)
359
413
  })
360
414
 
361
415
  it('should only rebase divergent events when first event matches', () => {
362
- const syncState = {
416
+ const syncState = new SyncState.SyncState({
363
417
  pending: [e_0_0, e_0_1],
364
418
  rollbackTail: [],
365
419
  upstreamHead: EventId.ROOT,
366
420
  localHead: e_0_0.id,
367
- }
421
+ })
368
422
  const result = run({
369
423
  syncState,
370
424
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_2, e_0_3, e_1_0] },
@@ -377,17 +431,17 @@ describe('syncstate', () => {
377
431
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_2, e_0_3, e_1_0])
378
432
  expectEventArraysEqual(result.eventsToRollback, [e_0_1])
379
433
  expectEventArraysEqual(result.newEvents, [e_0_2, e_0_3, e_1_0, e_0_1_e_1_1])
380
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
434
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
381
435
  expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_1.id)
382
436
  })
383
437
 
384
438
  it('should rebase all local events when incoming chain starts differently', () => {
385
- const syncState = {
439
+ const syncState = new SyncState.SyncState({
386
440
  pending: [e_0_0, e_0_1],
387
441
  rollbackTail: [],
388
442
  upstreamHead: EventId.ROOT,
389
443
  localHead: e_0_1.id,
390
- }
444
+ })
391
445
  const result = run({
392
446
  syncState,
393
447
  payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
@@ -401,20 +455,25 @@ describe('syncstate', () => {
401
455
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0])
402
456
  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])
403
457
  expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1])
404
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
458
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
405
459
  expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_2.id)
406
460
  })
407
461
 
408
462
  describe('local-push', () => {
409
463
  describe('advance', () => {
410
464
  it('should advance with new events', () => {
411
- const syncState = { pending: [e_0_0], rollbackTail: [], upstreamHead: EventId.ROOT, localHead: e_0_0.id }
465
+ const syncState = new SyncState.SyncState({
466
+ pending: [e_0_0],
467
+ rollbackTail: [],
468
+ upstreamHead: EventId.ROOT,
469
+ localHead: e_0_0.id,
470
+ })
412
471
  const result = run({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2, e_0_3] } })
413
472
 
414
473
  expectAdvance(result)
415
474
  expectEventArraysEqual(result.newSyncState.pending, [e_0_0, e_0_1, e_0_2, e_0_3])
416
475
  expectEventArraysEqual(result.newSyncState.rollbackTail, [])
417
- expect(result.newSyncState.upstreamHead).toBe(EventId.ROOT)
476
+ expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT)
418
477
  expect(result.newSyncState.localHead).toMatchObject(e_0_3.id)
419
478
  expectEventArraysEqual(result.newEvents, [e_0_1, e_0_2, e_0_3])
420
479
  })
@@ -422,12 +481,12 @@ describe('syncstate', () => {
422
481
 
423
482
  describe('reject', () => {
424
483
  it('should reject when new events are greater than pending events', () => {
425
- const syncState = {
484
+ const syncState = new SyncState.SyncState({
426
485
  pending: [e_0_0, e_0_1],
427
486
  rollbackTail: [],
428
487
  upstreamHead: EventId.ROOT,
429
488
  localHead: e_0_1.id,
430
- }
489
+ })
431
490
  const result = run({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2] } })
432
491
 
433
492
  expectReject(result)
@@ -39,19 +39,21 @@ import * as MutationEvent from '../schema/MutationEvent.js'
39
39
  * The `updateSyncState` function processes updates to the sync state based on incoming payloads,
40
40
  * handling cases such as upstream rebase, advance, local push, and rollback tail trimming.
41
41
  */
42
- export interface SyncState {
43
- pending: ReadonlyArray<MutationEvent.EncodedWithMeta>
44
- rollbackTail: ReadonlyArray<MutationEvent.EncodedWithMeta>
45
- upstreamHead: EventId.EventId
46
- localHead: EventId.EventId
47
- }
48
-
49
- export const SyncState = Schema.Struct({
42
+ export class SyncState extends Schema.Class<SyncState>('SyncState')({
50
43
  pending: Schema.Array(MutationEvent.EncodedWithMeta),
51
44
  rollbackTail: Schema.Array(MutationEvent.EncodedWithMeta),
52
45
  upstreamHead: EventId.EventId,
53
46
  localHead: EventId.EventId,
54
- }).annotations({ title: 'SyncState' })
47
+ }) {
48
+ toJSON = (): any => {
49
+ return {
50
+ pending: this.pending.map((e) => e.toJSON()),
51
+ rollbackTail: this.rollbackTail.map((e) => e.toJSON()),
52
+ upstreamHead: `(${this.upstreamHead.global},${this.upstreamHead.local})`,
53
+ localHead: `(${this.localHead.global},${this.localHead.local})`,
54
+ }
55
+ }
56
+ }
55
57
 
56
58
  export class PayloadUpstreamRebase extends Schema.TaggedStruct('upstream-rebase', {
57
59
  /** Rollback until this event in the rollback tail (inclusive). Starting from the end of the rollback tail. */
@@ -153,12 +155,12 @@ export const updateSyncState = ({
153
155
 
154
156
  return {
155
157
  _tag: 'rebase',
156
- newSyncState: {
158
+ newSyncState: new SyncState({
157
159
  pending: rebasedPending,
158
160
  rollbackTail: trimRollbackTail([...syncState.rollbackTail.slice(0, rollbackIndex), ...payload.newEvents]),
159
161
  upstreamHead: newUpstreamHead,
160
162
  localHead: rebasedPending.at(-1)?.id ?? newUpstreamHead,
161
- },
163
+ }),
162
164
  previousSyncState: syncState,
163
165
  newEvents: payload.newEvents,
164
166
  eventsToRollback,
@@ -169,12 +171,12 @@ export const updateSyncState = ({
169
171
  if (payload.newEvents.length === 0) {
170
172
  return {
171
173
  _tag: 'advance',
172
- newSyncState: {
174
+ newSyncState: new SyncState({
173
175
  pending: syncState.pending,
174
176
  rollbackTail: trimRollbackTail(syncState.rollbackTail),
175
177
  upstreamHead: syncState.upstreamHead,
176
178
  localHead: syncState.localHead,
177
- },
179
+ }),
178
180
  previousSyncState: syncState,
179
181
  newEvents: [],
180
182
  }
@@ -235,12 +237,12 @@ export const updateSyncState = ({
235
237
 
236
238
  return {
237
239
  _tag: 'advance',
238
- newSyncState: {
240
+ newSyncState: new SyncState({
239
241
  pending: pendingRemaining,
240
242
  rollbackTail: trimRollbackTail([...syncState.rollbackTail, ...pendingAndNewEvents]),
241
243
  upstreamHead: newUpstreamHead,
242
244
  localHead: pendingRemaining.at(-1)?.id ?? newUpstreamHead,
243
- },
245
+ }),
244
246
  previousSyncState: syncState,
245
247
  newEvents,
246
248
  }
@@ -262,12 +264,12 @@ export const updateSyncState = ({
262
264
 
263
265
  return {
264
266
  _tag: 'rebase',
265
- newSyncState: {
267
+ newSyncState: new SyncState({
266
268
  pending: rebasedPending,
267
269
  rollbackTail: trimRollbackTail([...syncState.rollbackTail, ...payload.newEvents]),
268
270
  upstreamHead: newUpstreamHead,
269
271
  localHead: rebasedPending.at(-1)!.id,
270
- },
272
+ }),
271
273
  previousSyncState: syncState,
272
274
  newEvents: [...payload.newEvents.slice(divergentNewEventsIndex), ...rebasedPending],
273
275
  eventsToRollback: [...syncState.rollbackTail, ...divergentPending],
@@ -289,12 +291,12 @@ export const updateSyncState = ({
289
291
  } else {
290
292
  return {
291
293
  _tag: 'advance',
292
- newSyncState: {
294
+ newSyncState: new SyncState({
293
295
  pending: [...syncState.pending, ...payload.newEvents],
294
296
  rollbackTail: syncState.rollbackTail,
295
297
  upstreamHead: syncState.upstreamHead,
296
298
  localHead: payload.newEvents.at(-1)!.id,
297
- },
299
+ }),
298
300
  previousSyncState: syncState,
299
301
  newEvents: payload.newEvents,
300
302
  }
package/src/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // import packageJson from '../package.json' with { type: 'json' }
3
3
  // export const liveStoreVersion = packageJson.version
4
4
 
5
- export const liveStoreVersion = '0.3.0-dev.8' as const
5
+ export const liveStoreVersion = '0.3.0-dev.9' as const
6
6
 
7
7
  /**
8
8
  * This version number is incremented whenever the internal storage format changes in a breaking way.