@livestore/common 0.3.0-dev.1 → 0.3.0-dev.10

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 (146) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-types.d.ts +26 -23
  3. package/dist/adapter-types.d.ts.map +1 -1
  4. package/dist/adapter-types.js.map +1 -1
  5. package/dist/derived-mutations.d.ts +4 -4
  6. package/dist/derived-mutations.d.ts.map +1 -1
  7. package/dist/derived-mutations.test.js.map +1 -1
  8. package/dist/devtools/devtools-bridge.d.ts +2 -1
  9. package/dist/devtools/devtools-bridge.d.ts.map +1 -1
  10. package/dist/devtools/devtools-messages.d.ts +98 -110
  11. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  12. package/dist/devtools/devtools-messages.js +9 -6
  13. package/dist/devtools/devtools-messages.js.map +1 -1
  14. package/dist/index.d.ts +0 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/leader-thread/LeaderSyncProcessor.d.ts +37 -0
  17. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -0
  18. package/dist/leader-thread/LeaderSyncProcessor.js +417 -0
  19. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -0
  20. package/dist/leader-thread/apply-mutation.d.ts +5 -2
  21. package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
  22. package/dist/leader-thread/apply-mutation.js +38 -26
  23. package/dist/leader-thread/apply-mutation.js.map +1 -1
  24. package/dist/leader-thread/leader-sync-processor.d.ts +2 -2
  25. package/dist/leader-thread/leader-sync-processor.d.ts.map +1 -1
  26. package/dist/leader-thread/leader-sync-processor.js +20 -12
  27. package/dist/leader-thread/leader-sync-processor.js.map +1 -1
  28. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  29. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  30. package/dist/leader-thread/leader-worker-devtools.js +22 -66
  31. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  32. package/dist/leader-thread/make-leader-thread-layer.d.ts +8 -7
  33. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  34. package/dist/leader-thread/make-leader-thread-layer.js +11 -5
  35. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  36. package/dist/leader-thread/mutationlog.d.ts +4 -17
  37. package/dist/leader-thread/mutationlog.d.ts.map +1 -1
  38. package/dist/leader-thread/mutationlog.js +2 -1
  39. package/dist/leader-thread/mutationlog.js.map +1 -1
  40. package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
  41. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  42. package/dist/leader-thread/recreate-db.js +9 -3
  43. package/dist/leader-thread/recreate-db.js.map +1 -1
  44. package/dist/leader-thread/types.d.ts +17 -9
  45. package/dist/leader-thread/types.d.ts.map +1 -1
  46. package/dist/leader-thread/types.js.map +1 -1
  47. package/dist/mutation.d.ts +9 -2
  48. package/dist/mutation.d.ts.map +1 -1
  49. package/dist/mutation.js +5 -5
  50. package/dist/mutation.js.map +1 -1
  51. package/dist/query-builder/impl.d.ts +1 -1
  52. package/dist/rehydrate-from-mutationlog.d.ts +2 -2
  53. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  54. package/dist/rehydrate-from-mutationlog.js +13 -19
  55. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  56. package/dist/schema/EventId.d.ts +16 -14
  57. package/dist/schema/EventId.d.ts.map +1 -1
  58. package/dist/schema/EventId.js +15 -7
  59. package/dist/schema/EventId.js.map +1 -1
  60. package/dist/schema/EventId.test.d.ts +2 -0
  61. package/dist/schema/EventId.test.d.ts.map +1 -0
  62. package/dist/schema/EventId.test.js +11 -0
  63. package/dist/schema/EventId.test.js.map +1 -0
  64. package/dist/schema/MutationEvent.d.ts +49 -80
  65. package/dist/schema/MutationEvent.d.ts.map +1 -1
  66. package/dist/schema/MutationEvent.js +32 -15
  67. package/dist/schema/MutationEvent.js.map +1 -1
  68. package/dist/schema/MutationEvent.test.d.ts +2 -0
  69. package/dist/schema/MutationEvent.test.d.ts.map +1 -0
  70. package/dist/schema/MutationEvent.test.js +2 -0
  71. package/dist/schema/MutationEvent.test.js.map +1 -0
  72. package/dist/schema/system-tables.d.ts +26 -26
  73. package/dist/schema/system-tables.d.ts.map +1 -1
  74. package/dist/schema/system-tables.js +19 -11
  75. package/dist/schema/system-tables.js.map +1 -1
  76. package/dist/schema-management/migrations.js +6 -6
  77. package/dist/schema-management/migrations.js.map +1 -1
  78. package/dist/sync/ClientSessionSyncProcessor.d.ts +45 -0
  79. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -0
  80. package/dist/sync/ClientSessionSyncProcessor.js +134 -0
  81. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -0
  82. package/dist/sync/client-session-sync-processor.d.ts +4 -4
  83. package/dist/sync/client-session-sync-processor.d.ts.map +1 -1
  84. package/dist/sync/index.d.ts +1 -1
  85. package/dist/sync/index.d.ts.map +1 -1
  86. package/dist/sync/index.js +1 -1
  87. package/dist/sync/index.js.map +1 -1
  88. package/dist/sync/next/history-dag-common.d.ts +1 -4
  89. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  90. package/dist/sync/next/history-dag-common.js +1 -1
  91. package/dist/sync/next/history-dag-common.js.map +1 -1
  92. package/dist/sync/next/rebase-events.d.ts +3 -3
  93. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  94. package/dist/sync/next/rebase-events.js +3 -2
  95. package/dist/sync/next/rebase-events.js.map +1 -1
  96. package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -1
  97. package/dist/sync/next/test/mutation-fixtures.js +3 -9
  98. package/dist/sync/next/test/mutation-fixtures.js.map +1 -1
  99. package/dist/sync/sync.d.ts +21 -11
  100. package/dist/sync/sync.d.ts.map +1 -1
  101. package/dist/sync/sync.js.map +1 -1
  102. package/dist/sync/syncstate.d.ts +45 -23
  103. package/dist/sync/syncstate.d.ts.map +1 -1
  104. package/dist/sync/syncstate.js +56 -12
  105. package/dist/sync/syncstate.js.map +1 -1
  106. package/dist/sync/syncstate.test.js +125 -69
  107. package/dist/sync/syncstate.test.js.map +1 -1
  108. package/dist/sync/validate-push-payload.d.ts +2 -2
  109. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  110. package/dist/sync/validate-push-payload.js +2 -2
  111. package/dist/sync/validate-push-payload.js.map +1 -1
  112. package/dist/version.d.ts +1 -1
  113. package/dist/version.d.ts.map +1 -1
  114. package/dist/version.js +1 -1
  115. package/dist/version.js.map +1 -1
  116. package/package.json +6 -5
  117. package/src/adapter-types.ts +22 -24
  118. package/src/derived-mutations.test.ts +1 -1
  119. package/src/derived-mutations.ts +9 -5
  120. package/src/devtools/devtools-bridge.ts +2 -1
  121. package/src/devtools/devtools-messages.ts +9 -6
  122. package/src/index.ts +0 -6
  123. package/src/leader-thread/{leader-sync-processor.ts → LeaderSyncProcessor.ts} +235 -230
  124. package/src/leader-thread/apply-mutation.ts +49 -31
  125. package/src/leader-thread/leader-worker-devtools.ts +30 -109
  126. package/src/leader-thread/make-leader-thread-layer.ts +24 -13
  127. package/src/leader-thread/mutationlog.ts +9 -5
  128. package/src/leader-thread/recreate-db.ts +9 -5
  129. package/src/leader-thread/types.ts +18 -11
  130. package/src/mutation.ts +17 -7
  131. package/src/rehydrate-from-mutationlog.ts +15 -23
  132. package/src/schema/EventId.test.ts +12 -0
  133. package/src/schema/EventId.ts +23 -9
  134. package/src/schema/MutationEvent.ts +46 -24
  135. package/src/schema/system-tables.ts +19 -11
  136. package/src/schema-management/migrations.ts +6 -6
  137. package/src/sync/{client-session-sync-processor.ts → ClientSessionSyncProcessor.ts} +11 -9
  138. package/src/sync/index.ts +1 -1
  139. package/src/sync/next/history-dag-common.ts +1 -1
  140. package/src/sync/next/rebase-events.ts +7 -7
  141. package/src/sync/next/test/mutation-fixtures.ts +3 -10
  142. package/src/sync/sync.ts +19 -6
  143. package/src/sync/syncstate.test.ts +127 -67
  144. package/src/sync/syncstate.ts +21 -19
  145. package/src/sync/validate-push-payload.ts +7 -4
  146. package/src/version.ts +1 -1
@@ -7,14 +7,14 @@ import * as SyncState from './syncstate.js'
7
7
 
8
8
  class TestEvent extends MutationEvent.EncodedWithMeta {
9
9
  constructor(
10
- public readonly id: EventId.EventId,
11
- public readonly parentId: EventId.EventId,
10
+ id: EventId.EventId | typeof EventId.EventId.Encoded,
11
+ parentId: EventId.EventId,
12
12
  public readonly payload: string,
13
13
  public readonly isLocal: boolean,
14
14
  ) {
15
15
  super({
16
- id,
17
- parentId,
16
+ id: EventId.make(id),
17
+ parentId: EventId.make(parentId),
18
18
  mutation: 'a',
19
19
  args: payload,
20
20
  meta: {},
@@ -58,12 +58,12 @@ describe('syncstate', () => {
58
58
  'upstream-rebase (trimRollbackUntil: $trimRollbackUntil)',
59
59
  ({ trimRollbackUntil }) => {
60
60
  it('should rollback until start', () => {
61
- const syncState = {
61
+ const syncState = new SyncState.SyncState({
62
62
  pending: [e_1_0],
63
63
  rollbackTail: [e_0_0, e_0_1],
64
64
  upstreamHead: EventId.ROOT,
65
65
  localHead: e_1_0.id,
66
- }
66
+ })
67
67
  const e_0_0_e_1_0 = e_0_0.rebase_(e_1_0.id)
68
68
  const e_0_1_e_1_1 = e_0_1.rebase_(e_0_0_e_1_0.id)
69
69
  const result = run({
@@ -83,19 +83,19 @@ describe('syncstate', () => {
83
83
  } else {
84
84
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0_e_1_0, e_0_1_e_1_1])
85
85
  }
86
- 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)
87
87
  expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
88
88
  expectEventArraysEqual(result.newEvents, [e_0_0_e_1_0, e_0_1_e_1_1])
89
89
  expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0])
90
90
  })
91
91
 
92
92
  it('should rollback only to specified point', () => {
93
- const syncState = {
93
+ const syncState = new SyncState.SyncState({
94
94
  pending: [e_1_0],
95
95
  rollbackTail: [e_0_0, e_0_1],
96
96
  upstreamHead: EventId.ROOT,
97
97
  localHead: e_1_0.id,
98
- }
98
+ })
99
99
  const e_0_1_e_1_0 = e_0_1.rebase_(e_0_0.id)
100
100
  const result = run({
101
101
  syncState,
@@ -114,14 +114,19 @@ describe('syncstate', () => {
114
114
  } else {
115
115
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1_e_1_0])
116
116
  }
117
- 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)
118
118
  expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
119
119
  expectEventArraysEqual(result.newEvents, [e_0_1_e_1_0])
120
120
  expectEventArraysEqual(result.eventsToRollback, [e_0_1, e_1_0])
121
121
  })
122
122
 
123
123
  it('should work for empty pending', () => {
124
- 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
+ })
125
130
  const result = run({
126
131
  syncState,
127
132
  payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [e_1_0] },
@@ -129,13 +134,18 @@ describe('syncstate', () => {
129
134
  expectRebase(result)
130
135
  expectEventArraysEqual(result.newSyncState.pending, [])
131
136
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_1_0])
132
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
137
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
133
138
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
134
- expect(result.newEvents).toEqual([e_1_0])
139
+ expect(result.newEvents).toStrictEqual([e_1_0])
135
140
  })
136
141
 
137
142
  it('should fail for empty rollback tail', () => {
138
- 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
+ })
139
149
  expect(() =>
140
150
  run({
141
151
  syncState,
@@ -145,7 +155,12 @@ describe('syncstate', () => {
145
155
  })
146
156
 
147
157
  it('should work for empty incoming', () => {
148
- 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
+ })
149
164
  const result = run({
150
165
  syncState,
151
166
  payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [] },
@@ -153,67 +168,92 @@ describe('syncstate', () => {
153
168
  expectRebase(result)
154
169
  expectEventArraysEqual(result.newSyncState.pending, [])
155
170
  expectEventArraysEqual(result.newSyncState.rollbackTail, [])
156
- expect(result.newSyncState.upstreamHead).toBe(EventId.ROOT)
171
+ expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT)
157
172
  expect(result.newSyncState.localHead).toMatchObject(EventId.ROOT)
158
- expect(result.newEvents).toEqual([])
173
+ expect(result.newEvents).toStrictEqual([])
159
174
  })
160
175
  },
161
176
  )
162
177
 
163
178
  describe('upstream-advance: advance', () => {
164
179
  it('should throw error if newEvents are not sorted in ascending order by eventId (local)', () => {
165
- 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
+ })
166
186
  expect(() => run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_0] } })).toThrow()
167
187
  })
168
188
 
169
189
  it('should throw error if newEvents are not sorted in ascending order by eventId (global)', () => {
170
- 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
+ })
171
196
  expect(() => run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0, e_0_0] } })).toThrow()
172
197
  })
173
198
 
174
199
  it('should acknowledge pending event when receiving matching event', () => {
175
- 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
+ })
176
206
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
177
207
 
178
208
  expectAdvance(result)
179
209
  expectEventArraysEqual(result.newSyncState.pending, [])
180
210
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
181
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
211
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
182
212
  expect(result.newSyncState.localHead).toMatchObject(e_0_0.id)
183
- expect(result.newEvents).toEqual([])
213
+ expect(result.newEvents).toStrictEqual([])
184
214
  })
185
215
 
186
216
  it('should acknowledge partial pending event when receiving matching event', () => {
187
- const syncState = {
217
+ const syncState = new SyncState.SyncState({
188
218
  pending: [e_0_0, e_1_0],
189
219
  rollbackTail: [],
190
220
  upstreamHead: EventId.ROOT,
191
221
  localHead: e_1_0.id,
192
- }
222
+ })
193
223
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
194
224
 
195
225
  expectAdvance(result)
196
226
  expectEventArraysEqual(result.newSyncState.pending, [e_1_0])
197
227
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
198
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
228
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
199
229
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
200
- expect(result.newEvents).toEqual([])
230
+ expect(result.newEvents).toStrictEqual([])
201
231
  })
202
232
 
203
233
  it('should acknowledge pending event and add new event', () => {
204
- 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
+ })
205
240
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_1] } })
206
241
 
207
242
  expectAdvance(result)
208
243
  expectEventArraysEqual(result.newSyncState.pending, [])
209
244
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1])
210
- expect(result.newSyncState.upstreamHead).toBe(e_0_1.id)
245
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id)
211
246
  expect(result.newSyncState.localHead).toMatchObject(e_0_1.id)
212
- expect(result.newEvents).toEqual([e_0_1])
247
+ expect(result.newEvents).toStrictEqual([e_0_1])
213
248
  })
214
249
 
215
250
  it('should acknowledge pending event and add multiple new events', () => {
216
- 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
+ })
217
257
  const result = run({
218
258
  syncState,
219
259
  payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1] },
@@ -222,18 +262,18 @@ describe('syncstate', () => {
222
262
  expectAdvance(result)
223
263
  expectEventArraysEqual(result.newSyncState.pending, [])
224
264
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1])
225
- expect(result.newSyncState.upstreamHead).toBe(e_1_1.id)
265
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1.id)
226
266
  expect(result.newSyncState.localHead).toMatchObject(e_1_1.id)
227
- expect(result.newEvents).toEqual([e_0_2, e_0_3, e_1_0, e_1_1])
267
+ expect(result.newEvents).toStrictEqual([e_0_2, e_0_3, e_1_0, e_1_1])
228
268
  })
229
269
 
230
270
  it('should ignore local events (incoming is subset of pending)', () => {
231
- const syncState = {
271
+ const syncState = new SyncState.SyncState({
232
272
  pending: [e_r_1, e_0_0],
233
273
  rollbackTail: [],
234
274
  upstreamHead: EventId.ROOT,
235
275
  localHead: e_0_0.id,
236
- }
276
+ })
237
277
  const result = run({
238
278
  syncState,
239
279
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
@@ -242,18 +282,18 @@ describe('syncstate', () => {
242
282
  expectAdvance(result)
243
283
  expectEventArraysEqual(result.newSyncState.pending, [])
244
284
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0])
245
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
285
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
246
286
  expect(result.newSyncState.localHead).toMatchObject(e_0_0.id)
247
- expect(result.newEvents).toEqual([])
287
+ expect(result.newEvents).toStrictEqual([])
248
288
  })
249
289
 
250
290
  it('should ignore local events (incoming is subset of pending case 2)', () => {
251
- const syncState = {
291
+ const syncState = new SyncState.SyncState({
252
292
  pending: [e_r_1, e_0_0, e_1_0],
253
293
  rollbackTail: [],
254
294
  upstreamHead: EventId.ROOT,
255
295
  localHead: e_0_0.id,
256
- }
296
+ })
257
297
  const result = run({
258
298
  syncState,
259
299
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
@@ -262,18 +302,18 @@ describe('syncstate', () => {
262
302
  expectAdvance(result)
263
303
  expectEventArraysEqual(result.newSyncState.pending, [e_1_0])
264
304
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0])
265
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
305
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
266
306
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
267
- expect(result.newEvents).toEqual([])
307
+ expect(result.newEvents).toStrictEqual([])
268
308
  })
269
309
 
270
310
  it('should ignore local events (incoming goes beyond pending)', () => {
271
- const syncState = {
311
+ const syncState = new SyncState.SyncState({
272
312
  pending: [e_r_1, e_0_0, e_0_1],
273
313
  rollbackTail: [],
274
314
  upstreamHead: EventId.ROOT,
275
315
  localHead: e_0_1.id,
276
- }
316
+ })
277
317
  const result = run({
278
318
  syncState,
279
319
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_1_0] },
@@ -283,15 +323,20 @@ describe('syncstate', () => {
283
323
  expectAdvance(result)
284
324
  expectEventArraysEqual(result.newSyncState.pending, [])
285
325
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0, e_0_1, e_1_0])
286
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
326
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
287
327
  expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
288
- expect(result.newEvents).toEqual([e_1_0])
328
+ expect(result.newEvents).toStrictEqual([e_1_0])
289
329
  })
290
330
  })
291
331
 
292
332
  describe('upstream-advance: rebase', () => {
293
333
  it('should rebase single local event to end', () => {
294
- 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
+ })
295
340
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1] } })
296
341
 
297
342
  const e_0_0_e_0_2 = e_0_0.rebase_(e_0_1.id)
@@ -299,7 +344,7 @@ describe('syncstate', () => {
299
344
  expectRebase(result)
300
345
  expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_0_2])
301
346
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1])
302
- expect(result.newSyncState.upstreamHead).toBe(e_0_1.id)
347
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id)
303
348
  expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_0_2.id)
304
349
  expectEventArraysEqual(result.eventsToRollback, [e_0_0])
305
350
  expectEventArraysEqual(result.newEvents, [e_0_1, e_0_0_e_0_2])
@@ -307,7 +352,12 @@ describe('syncstate', () => {
307
352
 
308
353
  it('should rebase different event with same id (no rollback tail)', () => {
309
354
  const e_0_0_b = new TestEvent({ global: 0, local: 0 }, EventId.ROOT, '0_0_b', true)
310
- 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
+ })
311
361
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
312
362
 
313
363
  const e_0_0_e_1_0 = e_0_0_b.rebase_(e_0_0.id)
@@ -317,18 +367,18 @@ describe('syncstate', () => {
317
367
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
318
368
  expectEventArraysEqual(result.newEvents, [e_0_0, e_0_0_e_1_0])
319
369
  expectEventArraysEqual(result.eventsToRollback, [e_0_0_b])
320
- expect(result.newSyncState.upstreamHead).toBe(e_0_0.id)
370
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
321
371
  expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_1_0.id)
322
372
  })
323
373
 
324
374
  it('should rebase different event with same id', () => {
325
375
  const e_1_0_b = new TestEvent({ global: 1, local: 0 }, e_0_0.id, '1_0_b', false)
326
- const syncState = {
376
+ const syncState = new SyncState.SyncState({
327
377
  pending: [e_1_0_b],
328
378
  rollbackTail: [e_0_0, e_0_1],
329
379
  upstreamHead: EventId.ROOT,
330
380
  localHead: e_1_0_b.id,
331
- }
381
+ })
332
382
  const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } })
333
383
  const e_1_0_e_2_0 = e_1_0_b.rebase_(e_1_0.id)
334
384
 
@@ -337,12 +387,17 @@ describe('syncstate', () => {
337
387
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1, e_1_0])
338
388
  expectEventArraysEqual(result.newEvents, [e_1_0, e_1_0_e_2_0])
339
389
  expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0_b])
340
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
390
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
341
391
  expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
342
392
  })
343
393
 
344
394
  it('should rebase single local event to end (more incoming events)', () => {
345
- 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
+ })
346
401
  const result = run({
347
402
  syncState,
348
403
  payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
@@ -353,17 +408,17 @@ describe('syncstate', () => {
353
408
  expectRebase(result)
354
409
  expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_2_0])
355
410
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0])
356
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
411
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
357
412
  expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_2_0.id)
358
413
  })
359
414
 
360
415
  it('should only rebase divergent events when first event matches', () => {
361
- const syncState = {
416
+ const syncState = new SyncState.SyncState({
362
417
  pending: [e_0_0, e_0_1],
363
418
  rollbackTail: [],
364
419
  upstreamHead: EventId.ROOT,
365
420
  localHead: e_0_0.id,
366
- }
421
+ })
367
422
  const result = run({
368
423
  syncState,
369
424
  payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_2, e_0_3, e_1_0] },
@@ -376,17 +431,17 @@ describe('syncstate', () => {
376
431
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_2, e_0_3, e_1_0])
377
432
  expectEventArraysEqual(result.eventsToRollback, [e_0_1])
378
433
  expectEventArraysEqual(result.newEvents, [e_0_2, e_0_3, e_1_0, e_0_1_e_1_1])
379
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
434
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
380
435
  expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_1.id)
381
436
  })
382
437
 
383
438
  it('should rebase all local events when incoming chain starts differently', () => {
384
- const syncState = {
439
+ const syncState = new SyncState.SyncState({
385
440
  pending: [e_0_0, e_0_1],
386
441
  rollbackTail: [],
387
442
  upstreamHead: EventId.ROOT,
388
443
  localHead: e_0_1.id,
389
- }
444
+ })
390
445
  const result = run({
391
446
  syncState,
392
447
  payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
@@ -400,20 +455,25 @@ describe('syncstate', () => {
400
455
  expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0])
401
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])
402
457
  expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1])
403
- expect(result.newSyncState.upstreamHead).toBe(e_1_0.id)
458
+ expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
404
459
  expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_2.id)
405
460
  })
406
461
 
407
462
  describe('local-push', () => {
408
463
  describe('advance', () => {
409
464
  it('should advance with new events', () => {
410
- 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
+ })
411
471
  const result = run({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2, e_0_3] } })
412
472
 
413
473
  expectAdvance(result)
414
474
  expectEventArraysEqual(result.newSyncState.pending, [e_0_0, e_0_1, e_0_2, e_0_3])
415
475
  expectEventArraysEqual(result.newSyncState.rollbackTail, [])
416
- expect(result.newSyncState.upstreamHead).toBe(EventId.ROOT)
476
+ expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT)
417
477
  expect(result.newSyncState.localHead).toMatchObject(e_0_3.id)
418
478
  expectEventArraysEqual(result.newEvents, [e_0_1, e_0_2, e_0_3])
419
479
  })
@@ -421,12 +481,12 @@ describe('syncstate', () => {
421
481
 
422
482
  describe('reject', () => {
423
483
  it('should reject when new events are greater than pending events', () => {
424
- const syncState = {
484
+ const syncState = new SyncState.SyncState({
425
485
  pending: [e_0_0, e_0_1],
426
486
  rollbackTail: [],
427
487
  upstreamHead: EventId.ROOT,
428
488
  localHead: e_0_1.id,
429
- }
489
+ })
430
490
  const result = run({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2] } })
431
491
 
432
492
  expectReject(result)
@@ -444,10 +504,10 @@ const expectEventArraysEqual = (
444
504
  ) => {
445
505
  expect(actual.length).toBe(expected.length)
446
506
  actual.forEach((event, i) => {
447
- expect(event.id).toEqual(expected[i]!.id)
448
- expect(event.parentId).toEqual(expected[i]!.parentId)
449
- expect(event.mutation).toEqual(expected[i]!.mutation)
450
- expect(event.args).toEqual(expected[i]!.args)
507
+ expect(event.id).toStrictEqual(expected[i]!.id)
508
+ expect(event.parentId).toStrictEqual(expected[i]!.parentId)
509
+ expect(event.mutation).toStrictEqual(expected[i]!.mutation)
510
+ expect(event.args).toStrictEqual(expected[i]!.args)
451
511
  })
452
512
  }
453
513
 
@@ -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
  }
@@ -1,17 +1,20 @@
1
1
  import { Effect } from '@livestore/utils/effect'
2
2
 
3
- import type { MutationEvent } from '../schema/mod.js'
3
+ import type { EventId, MutationEvent } from '../schema/mod.js'
4
4
  import { InvalidPushError } from './sync.js'
5
5
 
6
6
  // TODO proper batch validation
7
- export const validatePushPayload = (batch: ReadonlyArray<MutationEvent.AnyEncoded>, currentEventId: number) =>
7
+ export const validatePushPayload = (
8
+ batch: ReadonlyArray<MutationEvent.AnyEncodedGlobal>,
9
+ currentEventId: EventId.GlobalEventId,
10
+ ) =>
8
11
  Effect.gen(function* () {
9
- if (batch[0]!.id.global <= currentEventId) {
12
+ if (batch[0]!.id <= currentEventId) {
10
13
  return yield* InvalidPushError.make({
11
14
  reason: {
12
15
  _tag: 'ServerAhead',
13
16
  minimumExpectedId: currentEventId + 1,
14
- providedId: batch[0]!.id.global,
17
+ providedId: batch[0]!.id,
15
18
  },
16
19
  })
17
20
  }
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.0' as const
5
+ export const liveStoreVersion = '0.3.0-dev.10' as const
6
6
 
7
7
  /**
8
8
  * This version number is incremented whenever the internal storage format changes in a breaking way.