@livestore/common 0.3.0-dev.26 → 0.3.0-dev.28

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