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