@livestore/common 0.3.0-dev.7 → 0.3.0-dev.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/devtools/devtools-messages.d.ts +47 -47
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +37 -0
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -0
- package/dist/leader-thread/LeaderSyncProcessor.js +421 -0
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -0
- package/dist/leader-thread/apply-mutation.js +1 -1
- package/dist/leader-thread/apply-mutation.js.map +1 -1
- package/dist/leader-thread/leader-sync-processor.d.ts +2 -2
- package/dist/leader-thread/leader-sync-processor.d.ts.map +1 -1
- package/dist/leader-thread/leader-sync-processor.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +5 -6
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +7 -4
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
- package/dist/leader-thread/types.d.ts +11 -5
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/schema/EventId.test.d.ts +2 -0
- package/dist/schema/EventId.test.d.ts.map +1 -0
- package/dist/schema/EventId.test.js +11 -0
- package/dist/schema/EventId.test.js.map +1 -0
- package/dist/schema/MutationEvent.d.ts +6 -5
- package/dist/schema/MutationEvent.d.ts.map +1 -1
- package/dist/schema/MutationEvent.js +2 -2
- package/dist/schema/MutationEvent.js.map +1 -1
- package/dist/schema/MutationEvent.test.d.ts +2 -0
- package/dist/schema/MutationEvent.test.d.ts.map +1 -0
- package/dist/schema/MutationEvent.test.js +2 -0
- package/dist/schema/MutationEvent.test.js.map +1 -0
- package/dist/sync/ClientSessionSyncProcessor.d.ts +45 -0
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -0
- package/dist/sync/ClientSessionSyncProcessor.js +133 -0
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -0
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/sync.d.ts +15 -5
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/syncstate.d.ts +39 -17
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +56 -12
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +123 -63
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/index.ts +0 -6
- package/src/leader-thread/{leader-sync-processor.ts → LeaderSyncProcessor.ts} +205 -212
- package/src/leader-thread/apply-mutation.ts +1 -1
- package/src/leader-thread/make-leader-thread-layer.ts +9 -8
- package/src/leader-thread/types.ts +12 -4
- package/src/schema/EventId.test.ts +12 -0
- package/src/schema/MutationEvent.ts +7 -3
- package/src/sync/{client-session-sync-processor.ts → ClientSessionSyncProcessor.ts} +6 -5
- package/src/sync/index.ts +1 -1
- package/src/sync/sync.ts +15 -4
- package/src/sync/syncstate.test.ts +123 -63
- package/src/sync/syncstate.ts +21 -19
- package/src/version.ts +1 -1
@@ -6,6 +6,7 @@ import type {
|
|
6
6
|
Option,
|
7
7
|
Queue,
|
8
8
|
Scope,
|
9
|
+
Subscribable,
|
9
10
|
SubscriptionRef,
|
10
11
|
WebChannel,
|
11
12
|
} from '@livestore/utils/effect'
|
@@ -90,7 +91,7 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
|
|
90
91
|
mutationEventSchema: MutationEvent.ForMutationDefRecord<any>
|
91
92
|
// devtools: DevtoolsContext
|
92
93
|
syncBackend: SyncBackend | undefined
|
93
|
-
syncProcessor:
|
94
|
+
syncProcessor: LeaderSyncProcessor
|
94
95
|
connectedClientSessionPullQueues: PullQueueSet
|
95
96
|
/** e.g. used for `store.__dev` APIs */
|
96
97
|
extraIncomingMessagesQueue: Queue.Queue<Devtools.MessageToAppLeader>
|
@@ -107,17 +108,24 @@ export type PullQueueItem = {
|
|
107
108
|
remaining: number
|
108
109
|
}
|
109
110
|
|
110
|
-
export interface
|
111
|
+
export interface LeaderSyncProcessor {
|
111
112
|
push: (
|
112
113
|
/** `batch` needs to follow the same rules as `batch` in `SyncBackend.push` */
|
113
114
|
batch: ReadonlyArray<MutationEvent.EncodedWithMeta>,
|
114
|
-
|
115
|
+
options?: {
|
116
|
+
/**
|
117
|
+
* If true, the effect will only finish when the local push has been processed (i.e. succeeded or was rejected).
|
118
|
+
* @default false
|
119
|
+
*/
|
120
|
+
waitForProcessing?: boolean
|
121
|
+
},
|
122
|
+
) => Effect.Effect<void, InvalidPushError>
|
115
123
|
|
116
124
|
pushPartial: (mutationEvent: MutationEvent.PartialAnyEncoded) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx>
|
117
125
|
boot: (args: {
|
118
126
|
dbReady: Deferred.Deferred<void>
|
119
127
|
}) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient>
|
120
|
-
syncState:
|
128
|
+
syncState: Subscribable.Subscribable<SyncState.SyncState>
|
121
129
|
}
|
122
130
|
|
123
131
|
export interface PullQueueSet {
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Vitest } from '@livestore/utils/node-vitest'
|
2
|
+
import { expect } from 'vitest'
|
3
|
+
|
4
|
+
import { EventId } from './mod.js'
|
5
|
+
|
6
|
+
Vitest.describe('EventId', () => {
|
7
|
+
Vitest.test('nextPair', () => {
|
8
|
+
const e_0_0 = EventId.make({ global: 0, local: 0 })
|
9
|
+
expect(EventId.nextPair(e_0_0, false).id).toStrictEqual({ global: 1, local: 0 })
|
10
|
+
expect(EventId.nextPair(e_0_0, true).id).toStrictEqual({ global: 0, local: 1 })
|
11
|
+
})
|
12
|
+
})
|
@@ -2,6 +2,7 @@ import { memoizeByRef } from '@livestore/utils'
|
|
2
2
|
import type { Deferred } from '@livestore/utils/effect'
|
3
3
|
import { Schema } from '@livestore/utils/effect'
|
4
4
|
|
5
|
+
import type { InvalidPushError } from '../sync/sync.js'
|
5
6
|
import * as EventId from './EventId.js'
|
6
7
|
import type { MutationDef, MutationDefRecord } from './mutations.js'
|
7
8
|
import type { LiveStoreSchema } from './schema.js'
|
@@ -127,7 +128,7 @@ export const makeMutationEventPartialSchema = <TSchema extends LiveStoreSchema>(
|
|
127
128
|
args: def.schema,
|
128
129
|
}),
|
129
130
|
),
|
130
|
-
).annotations({ title: '
|
131
|
+
).annotations({ title: 'MutationEventPartial' }) as any
|
131
132
|
|
132
133
|
export const makeMutationEventSchemaMemo = memoizeByRef(makeMutationEventSchema)
|
133
134
|
|
@@ -138,7 +139,10 @@ export class EncodedWithMeta extends Schema.Class<EncodedWithMeta>('MutationEven
|
|
138
139
|
id: EventId.EventId,
|
139
140
|
parentId: EventId.EventId,
|
140
141
|
meta: Schema.optionalWith(
|
141
|
-
Schema.Any as Schema.Schema<{
|
142
|
+
Schema.Any as Schema.Schema<{
|
143
|
+
deferred?: Deferred.Deferred<void, InvalidPushError>
|
144
|
+
sessionChangeset?: Uint8Array
|
145
|
+
}>,
|
142
146
|
{ default: () => ({}) },
|
143
147
|
),
|
144
148
|
}) {
|
@@ -156,7 +160,7 @@ export class EncodedWithMeta extends Schema.Class<EncodedWithMeta>('MutationEven
|
|
156
160
|
rebase = (parentId: EventId.EventId, isLocal: boolean) =>
|
157
161
|
new EncodedWithMeta({
|
158
162
|
...this,
|
159
|
-
...EventId.nextPair(
|
163
|
+
...EventId.nextPair(parentId, isLocal),
|
160
164
|
})
|
161
165
|
|
162
166
|
static fromGlobal = (mutationEvent: AnyEncodedGlobal) =>
|
@@ -7,8 +7,7 @@ import type { ClientSessionLeaderThreadProxy, UnexpectedError } from '../adapter
|
|
7
7
|
import * as EventId from '../schema/EventId.js'
|
8
8
|
import { type LiveStoreSchema } from '../schema/mod.js'
|
9
9
|
import * as MutationEvent from '../schema/MutationEvent.js'
|
10
|
-
import
|
11
|
-
import { updateSyncState } from './syncstate.js'
|
10
|
+
import { SyncState, updateSyncState } from './syncstate.js'
|
12
11
|
|
13
12
|
/**
|
14
13
|
* Rebase behaviour:
|
@@ -42,19 +41,18 @@ export const makeClientSessionSyncProcessor = ({
|
|
42
41
|
}
|
43
42
|
rollback: (changeset: Uint8Array) => void
|
44
43
|
refreshTables: (tables: Set<string>) => void
|
45
|
-
// rebaseBehaviour: 'auto-rebase' | 'manual-rebase'
|
46
44
|
span: otel.Span
|
47
45
|
}): ClientSessionSyncProcessor => {
|
48
46
|
const mutationEventSchema = MutationEvent.makeMutationEventSchemaMemo(schema)
|
49
47
|
|
50
48
|
const syncStateRef = {
|
51
|
-
current: {
|
49
|
+
current: new SyncState({
|
52
50
|
localHead: initialLeaderHead,
|
53
51
|
upstreamHead: initialLeaderHead,
|
54
52
|
pending: [],
|
55
53
|
// TODO init rollbackTail from leader to be ready for backend rebasing
|
56
54
|
rollbackTail: [],
|
57
|
-
}
|
55
|
+
}),
|
58
56
|
}
|
59
57
|
|
60
58
|
const isLocalEvent = (mutationEventEncoded: MutationEvent.EncodedWithMeta) => {
|
@@ -104,6 +102,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
104
102
|
mutationEvent.meta.sessionChangeset = res.sessionChangeset
|
105
103
|
}
|
106
104
|
|
105
|
+
// console.debug('pushToLeader', encodedMutationEvents.length, ...encodedMutationEvents.map((_) => _.toJSON()))
|
107
106
|
pushToLeader(encodedMutationEvents)
|
108
107
|
|
109
108
|
return { writeTables }
|
@@ -155,6 +154,8 @@ export const makeClientSessionSyncProcessor = ({
|
|
155
154
|
event.meta.sessionChangeset = undefined
|
156
155
|
}
|
157
156
|
}
|
157
|
+
|
158
|
+
pushToLeader(updateResult.newSyncState.pending)
|
158
159
|
} else {
|
159
160
|
span.addEvent('pull:advance', {
|
160
161
|
payloadTag: payload._tag,
|
package/src/sync/index.ts
CHANGED
package/src/sync/sync.ts
CHANGED
@@ -1,12 +1,23 @@
|
|
1
|
-
import type { Effect, HttpClient, Option, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
1
|
+
import type { Effect, HttpClient, Option, Scope, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
2
2
|
import { Schema } from '@livestore/utils/effect'
|
3
3
|
|
4
|
+
import type { UnexpectedError } from '../adapter-types.js'
|
5
|
+
import type { InitialSyncOptions } from '../leader-thread/types.js'
|
4
6
|
import * as EventId from '../schema/EventId.js'
|
5
7
|
import type * as MutationEvent from '../schema/MutationEvent.js'
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
/**
|
10
|
+
* Those arguments can be used to implement multi-tenancy etc and are passed in from the store.
|
11
|
+
*/
|
12
|
+
export type MakeBackendArgs = {
|
13
|
+
storeId: string
|
14
|
+
clientId: string
|
15
|
+
}
|
16
|
+
|
17
|
+
export type SyncOptions = {
|
18
|
+
makeBackend: (args: MakeBackendArgs) => Effect.Effect<SyncBackend<any>, UnexpectedError, Scope.Scope>
|
19
|
+
/** @default { _tag: 'Skip' } */
|
20
|
+
initialSyncOptions?: InitialSyncOptions
|
10
21
|
}
|
11
22
|
|
12
23
|
export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
|
@@ -58,12 +58,12 @@ describe('syncstate', () => {
|
|
58
58
|
'upstream-rebase (trimRollbackUntil: $trimRollbackUntil)',
|
59
59
|
({ trimRollbackUntil }) => {
|
60
60
|
it('should rollback until start', () => {
|
61
|
-
const syncState = {
|
61
|
+
const syncState = new SyncState.SyncState({
|
62
62
|
pending: [e_1_0],
|
63
63
|
rollbackTail: [e_0_0, e_0_1],
|
64
64
|
upstreamHead: EventId.ROOT,
|
65
65
|
localHead: e_1_0.id,
|
66
|
-
}
|
66
|
+
})
|
67
67
|
const e_0_0_e_1_0 = e_0_0.rebase_(e_1_0.id)
|
68
68
|
const e_0_1_e_1_1 = e_0_1.rebase_(e_0_0_e_1_0.id)
|
69
69
|
const result = run({
|
@@ -83,19 +83,19 @@ describe('syncstate', () => {
|
|
83
83
|
} else {
|
84
84
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0_e_1_0, e_0_1_e_1_1])
|
85
85
|
}
|
86
|
-
expect(result.newSyncState.upstreamHead).
|
86
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1_e_1_1.id)
|
87
87
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
|
88
88
|
expectEventArraysEqual(result.newEvents, [e_0_0_e_1_0, e_0_1_e_1_1])
|
89
89
|
expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0])
|
90
90
|
})
|
91
91
|
|
92
92
|
it('should rollback only to specified point', () => {
|
93
|
-
const syncState = {
|
93
|
+
const syncState = new SyncState.SyncState({
|
94
94
|
pending: [e_1_0],
|
95
95
|
rollbackTail: [e_0_0, e_0_1],
|
96
96
|
upstreamHead: EventId.ROOT,
|
97
97
|
localHead: e_1_0.id,
|
98
|
-
}
|
98
|
+
})
|
99
99
|
const e_0_1_e_1_0 = e_0_1.rebase_(e_0_0.id)
|
100
100
|
const result = run({
|
101
101
|
syncState,
|
@@ -114,14 +114,19 @@ describe('syncstate', () => {
|
|
114
114
|
} else {
|
115
115
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1_e_1_0])
|
116
116
|
}
|
117
|
-
expect(result.newSyncState.upstreamHead).
|
117
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1_e_1_0.id)
|
118
118
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
|
119
119
|
expectEventArraysEqual(result.newEvents, [e_0_1_e_1_0])
|
120
120
|
expectEventArraysEqual(result.eventsToRollback, [e_0_1, e_1_0])
|
121
121
|
})
|
122
122
|
|
123
123
|
it('should work for empty pending', () => {
|
124
|
-
const syncState =
|
124
|
+
const syncState = new SyncState.SyncState({
|
125
|
+
pending: [],
|
126
|
+
rollbackTail: [e_0_0],
|
127
|
+
upstreamHead: EventId.ROOT,
|
128
|
+
localHead: e_0_0.id,
|
129
|
+
})
|
125
130
|
const result = run({
|
126
131
|
syncState,
|
127
132
|
payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [e_1_0] },
|
@@ -129,13 +134,18 @@ describe('syncstate', () => {
|
|
129
134
|
expectRebase(result)
|
130
135
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
131
136
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_1_0])
|
132
|
-
expect(result.newSyncState.upstreamHead).
|
137
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
|
133
138
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
|
134
|
-
expect(result.newEvents).
|
139
|
+
expect(result.newEvents).toStrictEqual([e_1_0])
|
135
140
|
})
|
136
141
|
|
137
142
|
it('should fail for empty rollback tail', () => {
|
138
|
-
const syncState =
|
143
|
+
const syncState = new SyncState.SyncState({
|
144
|
+
pending: [],
|
145
|
+
rollbackTail: [],
|
146
|
+
upstreamHead: EventId.ROOT,
|
147
|
+
localHead: e_0_0.id,
|
148
|
+
})
|
139
149
|
expect(() =>
|
140
150
|
run({
|
141
151
|
syncState,
|
@@ -145,7 +155,12 @@ describe('syncstate', () => {
|
|
145
155
|
})
|
146
156
|
|
147
157
|
it('should work for empty incoming', () => {
|
148
|
-
const syncState =
|
158
|
+
const syncState = new SyncState.SyncState({
|
159
|
+
pending: [],
|
160
|
+
rollbackTail: [e_0_0],
|
161
|
+
upstreamHead: EventId.ROOT,
|
162
|
+
localHead: e_0_0.id,
|
163
|
+
})
|
149
164
|
const result = run({
|
150
165
|
syncState,
|
151
166
|
payload: { _tag: 'upstream-rebase', rollbackUntil: e_0_0.id, newEvents: [] },
|
@@ -153,67 +168,92 @@ describe('syncstate', () => {
|
|
153
168
|
expectRebase(result)
|
154
169
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
155
170
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [])
|
156
|
-
expect(result.newSyncState.upstreamHead).
|
171
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT)
|
157
172
|
expect(result.newSyncState.localHead).toMatchObject(EventId.ROOT)
|
158
|
-
expect(result.newEvents).
|
173
|
+
expect(result.newEvents).toStrictEqual([])
|
159
174
|
})
|
160
175
|
},
|
161
176
|
)
|
162
177
|
|
163
178
|
describe('upstream-advance: advance', () => {
|
164
179
|
it('should throw error if newEvents are not sorted in ascending order by eventId (local)', () => {
|
165
|
-
const syncState =
|
180
|
+
const syncState = new SyncState.SyncState({
|
181
|
+
pending: [e_0_0],
|
182
|
+
rollbackTail: [],
|
183
|
+
upstreamHead: EventId.ROOT,
|
184
|
+
localHead: e_0_0.id,
|
185
|
+
})
|
166
186
|
expect(() => run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_0] } })).toThrow()
|
167
187
|
})
|
168
188
|
|
169
189
|
it('should throw error if newEvents are not sorted in ascending order by eventId (global)', () => {
|
170
|
-
const syncState =
|
190
|
+
const syncState = new SyncState.SyncState({
|
191
|
+
pending: [e_0_0],
|
192
|
+
rollbackTail: [],
|
193
|
+
upstreamHead: EventId.ROOT,
|
194
|
+
localHead: e_0_0.id,
|
195
|
+
})
|
171
196
|
expect(() => run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0, e_0_0] } })).toThrow()
|
172
197
|
})
|
173
198
|
|
174
199
|
it('should acknowledge pending event when receiving matching event', () => {
|
175
|
-
const syncState =
|
200
|
+
const syncState = new SyncState.SyncState({
|
201
|
+
pending: [e_0_0],
|
202
|
+
rollbackTail: [],
|
203
|
+
upstreamHead: EventId.ROOT,
|
204
|
+
localHead: e_0_0.id,
|
205
|
+
})
|
176
206
|
const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
|
177
207
|
|
178
208
|
expectAdvance(result)
|
179
209
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
180
210
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
|
181
|
-
expect(result.newSyncState.upstreamHead).
|
211
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
|
182
212
|
expect(result.newSyncState.localHead).toMatchObject(e_0_0.id)
|
183
|
-
expect(result.newEvents).
|
213
|
+
expect(result.newEvents).toStrictEqual([])
|
184
214
|
})
|
185
215
|
|
186
216
|
it('should acknowledge partial pending event when receiving matching event', () => {
|
187
|
-
const syncState = {
|
217
|
+
const syncState = new SyncState.SyncState({
|
188
218
|
pending: [e_0_0, e_1_0],
|
189
219
|
rollbackTail: [],
|
190
220
|
upstreamHead: EventId.ROOT,
|
191
221
|
localHead: e_1_0.id,
|
192
|
-
}
|
222
|
+
})
|
193
223
|
const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
|
194
224
|
|
195
225
|
expectAdvance(result)
|
196
226
|
expectEventArraysEqual(result.newSyncState.pending, [e_1_0])
|
197
227
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
|
198
|
-
expect(result.newSyncState.upstreamHead).
|
228
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
|
199
229
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
|
200
|
-
expect(result.newEvents).
|
230
|
+
expect(result.newEvents).toStrictEqual([])
|
201
231
|
})
|
202
232
|
|
203
233
|
it('should acknowledge pending event and add new event', () => {
|
204
|
-
const syncState =
|
234
|
+
const syncState = new SyncState.SyncState({
|
235
|
+
pending: [e_0_0],
|
236
|
+
rollbackTail: [],
|
237
|
+
upstreamHead: EventId.ROOT,
|
238
|
+
localHead: e_0_0.id,
|
239
|
+
})
|
205
240
|
const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_1] } })
|
206
241
|
|
207
242
|
expectAdvance(result)
|
208
243
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
209
244
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1])
|
210
|
-
expect(result.newSyncState.upstreamHead).
|
245
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id)
|
211
246
|
expect(result.newSyncState.localHead).toMatchObject(e_0_1.id)
|
212
|
-
expect(result.newEvents).
|
247
|
+
expect(result.newEvents).toStrictEqual([e_0_1])
|
213
248
|
})
|
214
249
|
|
215
250
|
it('should acknowledge pending event and add multiple new events', () => {
|
216
|
-
const syncState =
|
251
|
+
const syncState = new SyncState.SyncState({
|
252
|
+
pending: [e_0_1],
|
253
|
+
rollbackTail: [],
|
254
|
+
upstreamHead: e_0_0.id,
|
255
|
+
localHead: e_0_1.id,
|
256
|
+
})
|
217
257
|
const result = run({
|
218
258
|
syncState,
|
219
259
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1] },
|
@@ -222,18 +262,18 @@ describe('syncstate', () => {
|
|
222
262
|
expectAdvance(result)
|
223
263
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
224
264
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0, e_1_1])
|
225
|
-
expect(result.newSyncState.upstreamHead).
|
265
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_1.id)
|
226
266
|
expect(result.newSyncState.localHead).toMatchObject(e_1_1.id)
|
227
|
-
expect(result.newEvents).
|
267
|
+
expect(result.newEvents).toStrictEqual([e_0_2, e_0_3, e_1_0, e_1_1])
|
228
268
|
})
|
229
269
|
|
230
270
|
it('should ignore local events (incoming is subset of pending)', () => {
|
231
|
-
const syncState = {
|
271
|
+
const syncState = new SyncState.SyncState({
|
232
272
|
pending: [e_r_1, e_0_0],
|
233
273
|
rollbackTail: [],
|
234
274
|
upstreamHead: EventId.ROOT,
|
235
275
|
localHead: e_0_0.id,
|
236
|
-
}
|
276
|
+
})
|
237
277
|
const result = run({
|
238
278
|
syncState,
|
239
279
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
|
@@ -242,18 +282,18 @@ describe('syncstate', () => {
|
|
242
282
|
expectAdvance(result)
|
243
283
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
244
284
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0])
|
245
|
-
expect(result.newSyncState.upstreamHead).
|
285
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
|
246
286
|
expect(result.newSyncState.localHead).toMatchObject(e_0_0.id)
|
247
|
-
expect(result.newEvents).
|
287
|
+
expect(result.newEvents).toStrictEqual([])
|
248
288
|
})
|
249
289
|
|
250
290
|
it('should ignore local events (incoming is subset of pending case 2)', () => {
|
251
|
-
const syncState = {
|
291
|
+
const syncState = new SyncState.SyncState({
|
252
292
|
pending: [e_r_1, e_0_0, e_1_0],
|
253
293
|
rollbackTail: [],
|
254
294
|
upstreamHead: EventId.ROOT,
|
255
295
|
localHead: e_0_0.id,
|
256
|
-
}
|
296
|
+
})
|
257
297
|
const result = run({
|
258
298
|
syncState,
|
259
299
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_0] },
|
@@ -262,18 +302,18 @@ describe('syncstate', () => {
|
|
262
302
|
expectAdvance(result)
|
263
303
|
expectEventArraysEqual(result.newSyncState.pending, [e_1_0])
|
264
304
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0])
|
265
|
-
expect(result.newSyncState.upstreamHead).
|
305
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
|
266
306
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
|
267
|
-
expect(result.newEvents).
|
307
|
+
expect(result.newEvents).toStrictEqual([])
|
268
308
|
})
|
269
309
|
|
270
310
|
it('should ignore local events (incoming goes beyond pending)', () => {
|
271
|
-
const syncState = {
|
311
|
+
const syncState = new SyncState.SyncState({
|
272
312
|
pending: [e_r_1, e_0_0, e_0_1],
|
273
313
|
rollbackTail: [],
|
274
314
|
upstreamHead: EventId.ROOT,
|
275
315
|
localHead: e_0_1.id,
|
276
|
-
}
|
316
|
+
})
|
277
317
|
const result = run({
|
278
318
|
syncState,
|
279
319
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_1_0] },
|
@@ -283,15 +323,20 @@ describe('syncstate', () => {
|
|
283
323
|
expectAdvance(result)
|
284
324
|
expectEventArraysEqual(result.newSyncState.pending, [])
|
285
325
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_r_1, e_0_0, e_0_1, e_1_0])
|
286
|
-
expect(result.newSyncState.upstreamHead).
|
326
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
|
287
327
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0.id)
|
288
|
-
expect(result.newEvents).
|
328
|
+
expect(result.newEvents).toStrictEqual([e_1_0])
|
289
329
|
})
|
290
330
|
})
|
291
331
|
|
292
332
|
describe('upstream-advance: rebase', () => {
|
293
333
|
it('should rebase single local event to end', () => {
|
294
|
-
const syncState =
|
334
|
+
const syncState = new SyncState.SyncState({
|
335
|
+
pending: [e_0_0],
|
336
|
+
rollbackTail: [],
|
337
|
+
upstreamHead: EventId.ROOT,
|
338
|
+
localHead: e_0_0.id,
|
339
|
+
})
|
295
340
|
const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_1] } })
|
296
341
|
|
297
342
|
const e_0_0_e_0_2 = e_0_0.rebase_(e_0_1.id)
|
@@ -299,7 +344,7 @@ describe('syncstate', () => {
|
|
299
344
|
expectRebase(result)
|
300
345
|
expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_0_2])
|
301
346
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1])
|
302
|
-
expect(result.newSyncState.upstreamHead).
|
347
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_1.id)
|
303
348
|
expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_0_2.id)
|
304
349
|
expectEventArraysEqual(result.eventsToRollback, [e_0_0])
|
305
350
|
expectEventArraysEqual(result.newEvents, [e_0_1, e_0_0_e_0_2])
|
@@ -307,7 +352,12 @@ describe('syncstate', () => {
|
|
307
352
|
|
308
353
|
it('should rebase different event with same id (no rollback tail)', () => {
|
309
354
|
const e_0_0_b = new TestEvent({ global: 0, local: 0 }, EventId.ROOT, '0_0_b', true)
|
310
|
-
const syncState =
|
355
|
+
const syncState = new SyncState.SyncState({
|
356
|
+
pending: [e_0_0_b],
|
357
|
+
rollbackTail: [],
|
358
|
+
upstreamHead: EventId.ROOT,
|
359
|
+
localHead: e_0_0_b.id,
|
360
|
+
})
|
311
361
|
const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_0_0] } })
|
312
362
|
|
313
363
|
const e_0_0_e_1_0 = e_0_0_b.rebase_(e_0_0.id)
|
@@ -317,18 +367,18 @@ describe('syncstate', () => {
|
|
317
367
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0])
|
318
368
|
expectEventArraysEqual(result.newEvents, [e_0_0, e_0_0_e_1_0])
|
319
369
|
expectEventArraysEqual(result.eventsToRollback, [e_0_0_b])
|
320
|
-
expect(result.newSyncState.upstreamHead).
|
370
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_0_0.id)
|
321
371
|
expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_1_0.id)
|
322
372
|
})
|
323
373
|
|
324
374
|
it('should rebase different event with same id', () => {
|
325
375
|
const e_1_0_b = new TestEvent({ global: 1, local: 0 }, e_0_0.id, '1_0_b', false)
|
326
|
-
const syncState = {
|
376
|
+
const syncState = new SyncState.SyncState({
|
327
377
|
pending: [e_1_0_b],
|
328
378
|
rollbackTail: [e_0_0, e_0_1],
|
329
379
|
upstreamHead: EventId.ROOT,
|
330
380
|
localHead: e_1_0_b.id,
|
331
|
-
}
|
381
|
+
})
|
332
382
|
const result = run({ syncState, payload: { _tag: 'upstream-advance', newEvents: [e_1_0] } })
|
333
383
|
const e_1_0_e_2_0 = e_1_0_b.rebase_(e_1_0.id)
|
334
384
|
|
@@ -337,12 +387,17 @@ describe('syncstate', () => {
|
|
337
387
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_1, e_1_0])
|
338
388
|
expectEventArraysEqual(result.newEvents, [e_1_0, e_1_0_e_2_0])
|
339
389
|
expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1, e_1_0_b])
|
340
|
-
expect(result.newSyncState.upstreamHead).
|
390
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
|
341
391
|
expect(result.newSyncState.localHead).toMatchObject(e_1_0_e_2_0.id)
|
342
392
|
})
|
343
393
|
|
344
394
|
it('should rebase single local event to end (more incoming events)', () => {
|
345
|
-
const syncState =
|
395
|
+
const syncState = new SyncState.SyncState({
|
396
|
+
pending: [e_0_0],
|
397
|
+
rollbackTail: [],
|
398
|
+
upstreamHead: EventId.ROOT,
|
399
|
+
localHead: e_0_0.id,
|
400
|
+
})
|
346
401
|
const result = run({
|
347
402
|
syncState,
|
348
403
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
|
@@ -353,17 +408,17 @@ describe('syncstate', () => {
|
|
353
408
|
expectRebase(result)
|
354
409
|
expectEventArraysEqual(result.newSyncState.pending, [e_0_0_e_2_0])
|
355
410
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0])
|
356
|
-
expect(result.newSyncState.upstreamHead).
|
411
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
|
357
412
|
expect(result.newSyncState.localHead).toMatchObject(e_0_0_e_2_0.id)
|
358
413
|
})
|
359
414
|
|
360
415
|
it('should only rebase divergent events when first event matches', () => {
|
361
|
-
const syncState = {
|
416
|
+
const syncState = new SyncState.SyncState({
|
362
417
|
pending: [e_0_0, e_0_1],
|
363
418
|
rollbackTail: [],
|
364
419
|
upstreamHead: EventId.ROOT,
|
365
420
|
localHead: e_0_0.id,
|
366
|
-
}
|
421
|
+
})
|
367
422
|
const result = run({
|
368
423
|
syncState,
|
369
424
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_0, e_0_2, e_0_3, e_1_0] },
|
@@ -376,17 +431,17 @@ describe('syncstate', () => {
|
|
376
431
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_0, e_0_2, e_0_3, e_1_0])
|
377
432
|
expectEventArraysEqual(result.eventsToRollback, [e_0_1])
|
378
433
|
expectEventArraysEqual(result.newEvents, [e_0_2, e_0_3, e_1_0, e_0_1_e_1_1])
|
379
|
-
expect(result.newSyncState.upstreamHead).
|
434
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
|
380
435
|
expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_1.id)
|
381
436
|
})
|
382
437
|
|
383
438
|
it('should rebase all local events when incoming chain starts differently', () => {
|
384
|
-
const syncState = {
|
439
|
+
const syncState = new SyncState.SyncState({
|
385
440
|
pending: [e_0_0, e_0_1],
|
386
441
|
rollbackTail: [],
|
387
442
|
upstreamHead: EventId.ROOT,
|
388
443
|
localHead: e_0_1.id,
|
389
|
-
}
|
444
|
+
})
|
390
445
|
const result = run({
|
391
446
|
syncState,
|
392
447
|
payload: { _tag: 'upstream-advance', newEvents: [e_0_1, e_0_2, e_0_3, e_1_0] },
|
@@ -400,20 +455,25 @@ describe('syncstate', () => {
|
|
400
455
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [e_0_1, e_0_2, e_0_3, e_1_0])
|
401
456
|
expectEventArraysEqual(result.newEvents, [e_0_1, e_0_2, e_0_3, e_1_0, e_0_0_e_1_1, e_0_1_e_1_2])
|
402
457
|
expectEventArraysEqual(result.eventsToRollback, [e_0_0, e_0_1])
|
403
|
-
expect(result.newSyncState.upstreamHead).
|
458
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(e_1_0.id)
|
404
459
|
expect(result.newSyncState.localHead).toMatchObject(e_0_1_e_1_2.id)
|
405
460
|
})
|
406
461
|
|
407
462
|
describe('local-push', () => {
|
408
463
|
describe('advance', () => {
|
409
464
|
it('should advance with new events', () => {
|
410
|
-
const syncState =
|
465
|
+
const syncState = new SyncState.SyncState({
|
466
|
+
pending: [e_0_0],
|
467
|
+
rollbackTail: [],
|
468
|
+
upstreamHead: EventId.ROOT,
|
469
|
+
localHead: e_0_0.id,
|
470
|
+
})
|
411
471
|
const result = run({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2, e_0_3] } })
|
412
472
|
|
413
473
|
expectAdvance(result)
|
414
474
|
expectEventArraysEqual(result.newSyncState.pending, [e_0_0, e_0_1, e_0_2, e_0_3])
|
415
475
|
expectEventArraysEqual(result.newSyncState.rollbackTail, [])
|
416
|
-
expect(result.newSyncState.upstreamHead).
|
476
|
+
expect(result.newSyncState.upstreamHead).toMatchObject(EventId.ROOT)
|
417
477
|
expect(result.newSyncState.localHead).toMatchObject(e_0_3.id)
|
418
478
|
expectEventArraysEqual(result.newEvents, [e_0_1, e_0_2, e_0_3])
|
419
479
|
})
|
@@ -421,12 +481,12 @@ describe('syncstate', () => {
|
|
421
481
|
|
422
482
|
describe('reject', () => {
|
423
483
|
it('should reject when new events are greater than pending events', () => {
|
424
|
-
const syncState = {
|
484
|
+
const syncState = new SyncState.SyncState({
|
425
485
|
pending: [e_0_0, e_0_1],
|
426
486
|
rollbackTail: [],
|
427
487
|
upstreamHead: EventId.ROOT,
|
428
488
|
localHead: e_0_1.id,
|
429
|
-
}
|
489
|
+
})
|
430
490
|
const result = run({ syncState, payload: { _tag: 'local-push', newEvents: [e_0_1, e_0_2] } })
|
431
491
|
|
432
492
|
expectReject(result)
|
@@ -444,10 +504,10 @@ const expectEventArraysEqual = (
|
|
444
504
|
) => {
|
445
505
|
expect(actual.length).toBe(expected.length)
|
446
506
|
actual.forEach((event, i) => {
|
447
|
-
expect(event.id).
|
448
|
-
expect(event.parentId).
|
449
|
-
expect(event.mutation).
|
450
|
-
expect(event.args).
|
507
|
+
expect(event.id).toStrictEqual(expected[i]!.id)
|
508
|
+
expect(event.parentId).toStrictEqual(expected[i]!.parentId)
|
509
|
+
expect(event.mutation).toStrictEqual(expected[i]!.mutation)
|
510
|
+
expect(event.args).toStrictEqual(expected[i]!.args)
|
451
511
|
})
|
452
512
|
}
|
453
513
|
|