@livestore/common 0.0.0-snapshot-83c6b3d6e39244b59235009057fd5c48f6c3103f → 0.0.0-snapshot-2b8a9de3ec1a701aca891ebc2c98eb328274ae9e
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/adapter-types.d.ts +4 -2
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +1 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +24 -24
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +2 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +42 -38
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +1 -1
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +1 -0
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/mutationlog.d.ts +1 -0
- package/dist/leader-thread/mutationlog.d.ts.map +1 -1
- package/dist/leader-thread/mutationlog.js +1 -0
- package/dist/leader-thread/mutationlog.js.map +1 -1
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +13 -2
- package/dist/mutation.js.map +1 -1
- package/dist/query-builder/api.d.ts +118 -20
- package/dist/query-builder/api.d.ts.map +1 -1
- package/dist/query-builder/api.js.map +1 -1
- package/dist/query-builder/astToSql.d.ts +7 -0
- package/dist/query-builder/astToSql.d.ts.map +1 -0
- package/dist/query-builder/astToSql.js +168 -0
- package/dist/query-builder/astToSql.js.map +1 -0
- package/dist/query-builder/impl.d.ts +1 -5
- package/dist/query-builder/impl.d.ts.map +1 -1
- package/dist/query-builder/impl.js +130 -96
- package/dist/query-builder/impl.js.map +1 -1
- package/dist/query-builder/impl.test.js +94 -0
- package/dist/query-builder/impl.test.js.map +1 -1
- package/dist/query-builder/mod.d.ts +7 -0
- package/dist/query-builder/mod.d.ts.map +1 -1
- package/dist/query-builder/mod.js +7 -0
- package/dist/query-builder/mod.js.map +1 -1
- package/dist/query-info.d.ts +4 -1
- package/dist/query-info.d.ts.map +1 -1
- package/dist/query-info.js.map +1 -1
- package/dist/schema/MutationEvent.d.ts +17 -1
- package/dist/schema/MutationEvent.d.ts.map +1 -1
- package/dist/schema/MutationEvent.js +18 -2
- package/dist/schema/MutationEvent.js.map +1 -1
- package/dist/schema/db-schema/dsl/mod.d.ts +7 -5
- package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/db-schema/dsl/mod.js +6 -0
- package/dist/schema/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/mutations.d.ts +11 -2
- package/dist/schema/mutations.d.ts.map +1 -1
- package/dist/schema/mutations.js.map +1 -1
- package/dist/schema/table-def.d.ts +7 -3
- package/dist/schema/table-def.d.ts.map +1 -1
- package/dist/schema/table-def.js +7 -1
- package/dist/schema/table-def.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +2 -0
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +36 -33
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/sync.d.ts +17 -0
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/syncstate.d.ts +38 -16
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +110 -40
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +60 -29
- 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 +2 -2
- package/src/adapter-types.ts +4 -2
- package/src/leader-thread/LeaderSyncProcessor.ts +45 -39
- package/src/leader-thread/leader-worker-devtools.ts +1 -1
- package/src/leader-thread/make-leader-thread-layer.ts +1 -0
- package/src/leader-thread/mutationlog.ts +1 -0
- package/src/mutation.ts +20 -3
- package/src/query-builder/api.ts +192 -15
- package/src/query-builder/astToSql.ts +203 -0
- package/src/query-builder/impl.test.ts +104 -0
- package/src/query-builder/impl.ts +157 -113
- package/src/query-builder/mod.ts +7 -0
- package/src/query-info.ts +6 -1
- package/src/schema/MutationEvent.ts +18 -2
- package/src/schema/db-schema/dsl/mod.ts +30 -2
- package/src/schema/mutations.ts +12 -1
- package/src/schema/table-def.ts +14 -4
- package/src/sync/ClientSessionSyncProcessor.ts +39 -33
- package/src/sync/sync.ts +14 -0
- package/src/sync/syncstate.test.ts +72 -38
- package/src/sync/syncstate.ts +138 -58
- package/src/version.ts +1 -1
package/src/query-info.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { DbSchema } from './schema/mod.js'
|
|
|
9
9
|
*
|
|
10
10
|
* This information is currently only used for derived mutations.
|
|
11
11
|
*/
|
|
12
|
-
export type QueryInfo = QueryInfo.None | QueryInfo.Row | QueryInfo.Col | QueryInfo.ColJsonValue
|
|
12
|
+
export type QueryInfo = QueryInfo.None | QueryInfo.Row | QueryInfo.Col | QueryInfo.ColJsonValue | QueryInfo.Write
|
|
13
13
|
// export type QueryInfo<TTableDef extends DbSchema.TableDefBase = DbSchema.TableDefBase> =
|
|
14
14
|
// | QueryInfo.None
|
|
15
15
|
// | QueryInfo.Row<TTableDef>
|
|
@@ -45,6 +45,11 @@ export namespace QueryInfo {
|
|
|
45
45
|
jsonPath: string
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// NOTE Not yet used but we might want to use this in order to avoid write queries in read-only situations
|
|
49
|
+
export type Write = {
|
|
50
|
+
_tag: 'Write'
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
// NOTE maybe we want to bring back type-params back like below
|
|
49
54
|
// export type Row<TTableDef extends DbSchema.TableDefBase> = {
|
|
50
55
|
// _tag: 'Row'
|
|
@@ -178,10 +178,26 @@ export class EncodedWithMeta extends Schema.Class<EncodedWithMeta>('MutationEven
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Example: (global event)
|
|
183
|
+
* For event id (2,0) → (1,0) which should be rebased on event id (3,1) → (3,0)
|
|
184
|
+
* the resulting event id will be (4,0) → (3,0)
|
|
185
|
+
*
|
|
186
|
+
* Example: (client event)
|
|
187
|
+
* For event id (2,1) → (2,0) which should be rebased on event id (3,0) → (2,0)
|
|
188
|
+
* the resulting event id will be (3,1) → (3,0)
|
|
189
|
+
*
|
|
190
|
+
* Syntax: (2,1) → (2,0)
|
|
191
|
+
* ^ ^ ^ ^
|
|
192
|
+
* | | | +- client parent id
|
|
193
|
+
* | | +--- global parent id
|
|
194
|
+
* | +-- client id
|
|
195
|
+
* +---- global id
|
|
196
|
+
*/
|
|
197
|
+
rebase = (parentId: EventId.EventId, isClient: boolean) =>
|
|
182
198
|
new EncodedWithMeta({
|
|
183
199
|
...this,
|
|
184
|
-
...EventId.nextPair(parentId,
|
|
200
|
+
...EventId.nextPair(parentId, isClient),
|
|
185
201
|
})
|
|
186
202
|
|
|
187
203
|
static fromGlobal = (mutationEvent: AnyEncodedGlobal) =>
|
|
@@ -52,8 +52,13 @@ export type AnyIfConstained<In, Out> = '__constrained' extends keyof In ? any :
|
|
|
52
52
|
export type EmptyObjIfConstained<In> = '__constrained' extends keyof In ? {} : In
|
|
53
53
|
|
|
54
54
|
export type StructSchemaForColumns<TCols extends ConstraintColumns> = Schema.Schema<
|
|
55
|
-
AnyIfConstained<TCols,
|
|
56
|
-
AnyIfConstained<TCols,
|
|
55
|
+
AnyIfConstained<TCols, FromColumns.RowDecoded<TCols>>,
|
|
56
|
+
AnyIfConstained<TCols, FromColumns.RowEncoded<TCols>>
|
|
57
|
+
>
|
|
58
|
+
|
|
59
|
+
export type InsertStructSchemaForColumns<TCols extends ConstraintColumns> = Schema.Schema<
|
|
60
|
+
AnyIfConstained<TCols, FromColumns.InsertRowDecoded<TCols>>,
|
|
61
|
+
AnyIfConstained<TCols, FromColumns.InsertRowEncoded<TCols>>
|
|
57
62
|
>
|
|
58
63
|
|
|
59
64
|
export const structSchemaForTable = <TTableDefinition extends TableDefinition<any, any>>(
|
|
@@ -63,6 +68,20 @@ export const structSchemaForTable = <TTableDefinition extends TableDefinition<an
|
|
|
63
68
|
title: tableDef.name,
|
|
64
69
|
}) as any
|
|
65
70
|
|
|
71
|
+
export const insertStructSchemaForTable = <TTableDefinition extends TableDefinition<any, any>>(
|
|
72
|
+
tableDef: TTableDefinition,
|
|
73
|
+
): InsertStructSchemaForColumns<TTableDefinition['columns']> =>
|
|
74
|
+
Schema.Struct(
|
|
75
|
+
Object.fromEntries(
|
|
76
|
+
tableDef.ast.columns.map((column) => [
|
|
77
|
+
column.name,
|
|
78
|
+
column.nullable === true || column.default._tag === 'Some' ? Schema.optional(column.schema) : column.schema,
|
|
79
|
+
]),
|
|
80
|
+
),
|
|
81
|
+
).annotations({
|
|
82
|
+
title: tableDef.name,
|
|
83
|
+
}) as any
|
|
84
|
+
|
|
66
85
|
const columsToAst = (columns: Columns): ReadonlyArray<SqliteAst.Column> => {
|
|
67
86
|
return Object.entries(columns).map(([name, column]) => {
|
|
68
87
|
return {
|
|
@@ -161,6 +180,10 @@ export namespace FromColumns {
|
|
|
161
180
|
readonly [K in keyof TColumns]: Schema.Schema.Type<TColumns[K]['schema']>
|
|
162
181
|
}
|
|
163
182
|
|
|
183
|
+
export type RowEncodedAll<TColumns extends Columns> = {
|
|
184
|
+
readonly [K in keyof TColumns]: Schema.Schema.Encoded<TColumns[K]['schema']>
|
|
185
|
+
}
|
|
186
|
+
|
|
164
187
|
export type RowEncoded<TColumns extends Columns> = Types.Simplify<
|
|
165
188
|
Nullable<Pick<RowEncodeNonNullable<TColumns>, NullableColumnNames<TColumns>>> &
|
|
166
189
|
Omit<RowEncodeNonNullable<TColumns>, NullableColumnNames<TColumns>>
|
|
@@ -192,4 +215,9 @@ export namespace FromColumns {
|
|
|
192
215
|
Pick<RowDecodedAll<TColumns>, RequiredInsertColumnNames<TColumns>> &
|
|
193
216
|
Partial<Omit<RowDecodedAll<TColumns>, RequiredInsertColumnNames<TColumns>>>
|
|
194
217
|
>
|
|
218
|
+
|
|
219
|
+
export type InsertRowEncoded<TColumns extends Columns> = Types.Simplify<
|
|
220
|
+
Pick<RowEncodedAll<TColumns>, RequiredInsertColumnNames<TColumns>> &
|
|
221
|
+
Partial<Omit<RowEncodedAll<TColumns>, RequiredInsertColumnNames<TColumns>>>
|
|
222
|
+
>
|
|
195
223
|
}
|
package/src/schema/mutations.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Schema } from '@livestore/utils/effect'
|
|
2
2
|
|
|
3
|
+
import type { QueryBuilder } from '../query-builder/mod.js'
|
|
3
4
|
import type { BindValues } from '../sql-queries/sql-queries.js'
|
|
4
5
|
|
|
5
6
|
export type MutationDefMap = {
|
|
@@ -20,7 +21,10 @@ export type InternalMutationSchema<TRecord extends MutationDefRecord = MutationD
|
|
|
20
21
|
|
|
21
22
|
export type MutationDefSqlResult<TTo> =
|
|
22
23
|
| SingleOrReadonlyArray<string>
|
|
23
|
-
| ((
|
|
24
|
+
| ((
|
|
25
|
+
args: TTo,
|
|
26
|
+
context: { currentFacts: MutationEventFacts; clientOnly: boolean },
|
|
27
|
+
) => SingleOrReadonlyArray<
|
|
24
28
|
| string
|
|
25
29
|
| {
|
|
26
30
|
sql: string
|
|
@@ -28,8 +32,15 @@ export type MutationDefSqlResult<TTo> =
|
|
|
28
32
|
bindValues: BindValues
|
|
29
33
|
writeTables?: ReadonlySet<string>
|
|
30
34
|
}
|
|
35
|
+
| QueryBuilder.Any
|
|
31
36
|
>)
|
|
32
37
|
|
|
38
|
+
export type MutationHandlerResult = {
|
|
39
|
+
sql: string
|
|
40
|
+
bindValues: BindValues
|
|
41
|
+
writeTables?: ReadonlySet<string>
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
export type SingleOrReadonlyArray<T> = T | ReadonlyArray<T>
|
|
34
45
|
|
|
35
46
|
export type MutationDef<TName extends string, TFrom, TTo> = {
|
package/src/schema/table-def.ts
CHANGED
|
@@ -19,12 +19,12 @@ export type DefaultSqliteTableDefConstrained = SqliteDsl.TableDefinition<string,
|
|
|
19
19
|
export type TableDefBase<
|
|
20
20
|
TSqliteDef extends DefaultSqliteTableDef = DefaultSqliteTableDefConstrained,
|
|
21
21
|
TOptions extends TableOptions = TableOptions,
|
|
22
|
-
TSchema = SqliteDsl.StructSchemaForColumns<TSqliteDef['columns']>,
|
|
23
22
|
> = {
|
|
24
23
|
sqliteDef: TSqliteDef
|
|
25
24
|
options: TOptions
|
|
26
25
|
// Derived from `sqliteDef`, so only exposed for convenience
|
|
27
|
-
schema:
|
|
26
|
+
schema: SqliteDsl.StructSchemaForColumns<TSqliteDef['columns']>
|
|
27
|
+
insertSchema: SqliteDsl.InsertStructSchemaForColumns<TSqliteDef['columns']>
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export type TableDef<
|
|
@@ -47,7 +47,10 @@ export type TableDef<
|
|
|
47
47
|
options: TOptions
|
|
48
48
|
// Derived from `sqliteDef`, so only exposed for convenience
|
|
49
49
|
schema: TSchema
|
|
50
|
-
|
|
50
|
+
insertSchema: SqliteDsl.InsertStructSchemaForColumns<TSqliteDef['columns']>
|
|
51
|
+
query: QueryBuilder<ReadonlyArray<Schema.Schema.Type<TSchema>>, TableDefBase<TSqliteDef & {}, TOptions>>
|
|
52
|
+
readonly Type: Schema.Schema.Type<TSchema>
|
|
53
|
+
readonly Encoded: Schema.Schema.Encoded<TSchema>
|
|
51
54
|
} & (TOptions['deriveMutations']['enabled'] extends true
|
|
52
55
|
? DerivedMutationHelperFns<TSqliteDef['columns'], TOptions>
|
|
53
56
|
: {})
|
|
@@ -195,7 +198,14 @@ export const table = <
|
|
|
195
198
|
const isSingleColumn = SqliteDsl.isColumnDefinition(columnOrColumns) === true
|
|
196
199
|
|
|
197
200
|
const schema = SqliteDsl.structSchemaForTable(sqliteDef)
|
|
198
|
-
const
|
|
201
|
+
const insertSchema = SqliteDsl.insertStructSchemaForTable(sqliteDef)
|
|
202
|
+
const tableDef = {
|
|
203
|
+
sqliteDef,
|
|
204
|
+
options: options_,
|
|
205
|
+
schema,
|
|
206
|
+
insertSchema,
|
|
207
|
+
} satisfies TableDefBase
|
|
208
|
+
|
|
199
209
|
const query = makeQueryBuilder(tableDef)
|
|
200
210
|
// const tableDef = { ...tableDefBase, query } satisfies TableDef
|
|
201
211
|
|
|
@@ -17,6 +17,8 @@ import * as SyncState from './syncstate.js'
|
|
|
17
17
|
* - The goal is to never block the UI, so we'll interrupt rebasing if a new mutations is pushed by the client session.
|
|
18
18
|
* - We also want to avoid "backwards-jumping" in the UI, so we'll transactionally apply a read model changes during a rebase.
|
|
19
19
|
* - We might need to make the rebase behaviour configurable e.g. to let users manually trigger a rebase
|
|
20
|
+
*
|
|
21
|
+
* Longer term we should evalutate whether we can unify the ClientSessionSyncProcessor with the LeaderSyncProcessor.
|
|
20
22
|
*/
|
|
21
23
|
export const makeClientSessionSyncProcessor = ({
|
|
22
24
|
schema,
|
|
@@ -64,7 +66,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
|
|
67
|
-
const
|
|
69
|
+
const isClientEvent = (mutationEventEncoded: MutationEvent.EncodedWithMeta) =>
|
|
68
70
|
getMutationDef(schema, mutationEventEncoded.mutation).options.clientOnly
|
|
69
71
|
|
|
70
72
|
/** We're queuing push requests to reduce the number of messages sent to the leader by batching them */
|
|
@@ -88,31 +90,31 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
88
90
|
)
|
|
89
91
|
})
|
|
90
92
|
|
|
91
|
-
const
|
|
93
|
+
const mergeResult = SyncState.merge({
|
|
92
94
|
syncState: syncStateRef.current,
|
|
93
95
|
payload: { _tag: 'local-push', newEvents: encodedMutationEvents },
|
|
94
|
-
|
|
96
|
+
isClientEvent,
|
|
95
97
|
isEqualEvent: MutationEvent.isEqualEncoded,
|
|
96
98
|
})
|
|
97
99
|
|
|
98
|
-
if (
|
|
99
|
-
return shouldNeverHappen('Unexpected error in client-session-sync-processor',
|
|
100
|
+
if (mergeResult._tag === 'unexpected-error') {
|
|
101
|
+
return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.cause)
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
span.addEvent('local-push', {
|
|
103
105
|
batchSize: encodedMutationEvents.length,
|
|
104
|
-
|
|
106
|
+
mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
|
|
105
107
|
})
|
|
106
108
|
|
|
107
|
-
if (
|
|
108
|
-
return shouldNeverHappen(`Expected advance, got ${
|
|
109
|
+
if (mergeResult._tag !== 'advance') {
|
|
110
|
+
return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
syncStateRef.current =
|
|
112
|
-
syncStateUpdateQueue.offer(
|
|
113
|
+
syncStateRef.current = mergeResult.newSyncState
|
|
114
|
+
syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
|
|
113
115
|
|
|
114
116
|
const writeTables = new Set<string>()
|
|
115
|
-
for (const mutationEvent of
|
|
117
|
+
for (const mutationEvent of mergeResult.newEvents) {
|
|
116
118
|
// TODO avoid encoding and decoding here again
|
|
117
119
|
const decodedMutationEvent = Schema.decodeSync(mutationEventSchema)(mutationEvent)
|
|
118
120
|
const res = applyMutation(decodedMutationEvent, { otelContext, withChangeset: true })
|
|
@@ -166,7 +168,10 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
166
168
|
|
|
167
169
|
yield* FiberHandle.run(leaderPushingFiberHandle, backgroundLeaderPushing)
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
// NOTE We need to lazily call `.pull` as we want the cursor to be updated
|
|
172
|
+
yield* Stream.suspend(() =>
|
|
173
|
+
clientSession.leaderThread.mutations.pull({ cursor: syncStateRef.current.localHead }),
|
|
174
|
+
).pipe(
|
|
170
175
|
Stream.tap(({ payload, remaining }) =>
|
|
171
176
|
Effect.gen(function* () {
|
|
172
177
|
// console.log('pulled payload from leader', { payload, remaining })
|
|
@@ -174,29 +179,29 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
174
179
|
yield* clientSession.devtools.pullLatch.await
|
|
175
180
|
}
|
|
176
181
|
|
|
177
|
-
const
|
|
182
|
+
const mergeResult = SyncState.merge({
|
|
178
183
|
syncState: syncStateRef.current,
|
|
179
184
|
payload,
|
|
180
|
-
|
|
185
|
+
isClientEvent,
|
|
181
186
|
isEqualEvent: MutationEvent.isEqualEncoded,
|
|
182
187
|
})
|
|
183
188
|
|
|
184
|
-
if (
|
|
185
|
-
return yield* Effect.fail(
|
|
186
|
-
} else if (
|
|
187
|
-
return shouldNeverHappen('Unexpected reject in client-session-sync-processor',
|
|
189
|
+
if (mergeResult._tag === 'unexpected-error') {
|
|
190
|
+
return yield* Effect.fail(mergeResult.cause)
|
|
191
|
+
} else if (mergeResult._tag === 'reject') {
|
|
192
|
+
return shouldNeverHappen('Unexpected reject in client-session-sync-processor', mergeResult)
|
|
188
193
|
}
|
|
189
194
|
|
|
190
|
-
syncStateRef.current =
|
|
191
|
-
syncStateUpdateQueue.offer(
|
|
195
|
+
syncStateRef.current = mergeResult.newSyncState
|
|
196
|
+
syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
|
|
192
197
|
|
|
193
|
-
if (
|
|
198
|
+
if (mergeResult._tag === 'rebase') {
|
|
194
199
|
span.addEvent('pull:rebase', {
|
|
195
200
|
payloadTag: payload._tag,
|
|
196
201
|
payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
|
|
197
|
-
newEventsCount:
|
|
198
|
-
rollbackCount:
|
|
199
|
-
res: TRACE_VERBOSE ? JSON.stringify(
|
|
202
|
+
newEventsCount: mergeResult.newEvents.length,
|
|
203
|
+
rollbackCount: mergeResult.eventsToRollback.length,
|
|
204
|
+
res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
|
|
200
205
|
remaining,
|
|
201
206
|
})
|
|
202
207
|
|
|
@@ -212,36 +217,36 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
212
217
|
if (LS_DEV) {
|
|
213
218
|
Effect.logDebug(
|
|
214
219
|
'pull:rebase: rollback',
|
|
215
|
-
|
|
216
|
-
...
|
|
220
|
+
mergeResult.eventsToRollback.length,
|
|
221
|
+
...mergeResult.eventsToRollback.slice(0, 10).map((_) => _.toJSON()),
|
|
217
222
|
).pipe(Effect.provide(runtime), Effect.runSync)
|
|
218
223
|
}
|
|
219
224
|
|
|
220
|
-
for (let i =
|
|
221
|
-
const event =
|
|
225
|
+
for (let i = mergeResult.eventsToRollback.length - 1; i >= 0; i--) {
|
|
226
|
+
const event = mergeResult.eventsToRollback[i]!
|
|
222
227
|
if (event.meta.sessionChangeset) {
|
|
223
228
|
rollback(event.meta.sessionChangeset)
|
|
224
229
|
event.meta.sessionChangeset = undefined
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
232
|
|
|
228
|
-
yield* BucketQueue.offerAll(leaderPushQueue,
|
|
233
|
+
yield* BucketQueue.offerAll(leaderPushQueue, mergeResult.newSyncState.pending)
|
|
229
234
|
} else {
|
|
230
235
|
span.addEvent('pull:advance', {
|
|
231
236
|
payloadTag: payload._tag,
|
|
232
237
|
payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
|
|
233
|
-
newEventsCount:
|
|
234
|
-
res: TRACE_VERBOSE ? JSON.stringify(
|
|
238
|
+
newEventsCount: mergeResult.newEvents.length,
|
|
239
|
+
res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
|
|
235
240
|
remaining,
|
|
236
241
|
})
|
|
237
242
|
|
|
238
243
|
debugInfo.advanceCount++
|
|
239
244
|
}
|
|
240
245
|
|
|
241
|
-
if (
|
|
246
|
+
if (mergeResult.newEvents.length === 0) return
|
|
242
247
|
|
|
243
248
|
const writeTables = new Set<string>()
|
|
244
|
-
for (const mutationEvent of
|
|
249
|
+
for (const mutationEvent of mergeResult.newEvents) {
|
|
245
250
|
const decodedMutationEvent = Schema.decodeSync(mutationEventSchema)(mutationEvent)
|
|
246
251
|
const res = applyMutation(decodedMutationEvent, { otelContext, withChangeset: true })
|
|
247
252
|
for (const table of res.writeTables) {
|
|
@@ -259,6 +264,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
|
259
264
|
),
|
|
260
265
|
Stream.runDrain,
|
|
261
266
|
Effect.forever, // NOTE Whenever the leader changes, we need to re-start the stream
|
|
267
|
+
Effect.interruptible,
|
|
262
268
|
Effect.withSpan('client-session-sync-processor:pull'),
|
|
263
269
|
Effect.tapCauseLogPretty,
|
|
264
270
|
Effect.forkScoped,
|
package/src/sync/sync.ts
CHANGED
|
@@ -19,6 +19,16 @@ export type SyncOptions = {
|
|
|
19
19
|
backend?: SyncBackendConstructor<any>
|
|
20
20
|
/** @default { _tag: 'Skip' } */
|
|
21
21
|
initialSyncOptions?: InitialSyncOptions
|
|
22
|
+
/**
|
|
23
|
+
* What to do if there is an error during sync.
|
|
24
|
+
*
|
|
25
|
+
* Options:
|
|
26
|
+
* `shutdown` will stop the sync processor and cause the app to crash.
|
|
27
|
+
* `ignore` will log the error and let the app continue running acting as if it was offline.
|
|
28
|
+
*
|
|
29
|
+
* @default 'ignore'
|
|
30
|
+
* */
|
|
31
|
+
onSyncError?: 'shutdown' | 'ignore'
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
export type SyncBackendConstructor<TSyncMetadata = Schema.JsonValue> = (
|
|
@@ -59,6 +69,10 @@ export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
|
|
|
59
69
|
HttpClient.HttpClient
|
|
60
70
|
>
|
|
61
71
|
isConnected: SubscriptionRef.SubscriptionRef<boolean>
|
|
72
|
+
/**
|
|
73
|
+
* Metadata describing the sync backend.
|
|
74
|
+
*/
|
|
75
|
+
metadata: { name: string; description: string } & Record<string, Schema.JsonValue>
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
export class IsOfflineError extends Schema.TaggedError<IsOfflineError>()('IsOfflineError', {}) {}
|