@livestore/common 0.0.0-snapshot-f6ec49b1a18859aad769f0a0d8edf8bae231ed07 → 0.0.0-snapshot-2ef046b02334f52613d31dbe06af53487685edc0

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