@livestore/common 0.3.1-dev.0 → 0.3.2-dev.0

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 (185) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +35 -0
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -0
  4. package/dist/ClientSessionLeaderThreadProxy.js +6 -0
  5. package/dist/ClientSessionLeaderThreadProxy.js.map +1 -0
  6. package/dist/adapter-types.d.ts +10 -156
  7. package/dist/adapter-types.d.ts.map +1 -1
  8. package/dist/adapter-types.js +5 -49
  9. package/dist/adapter-types.js.map +1 -1
  10. package/dist/defs.d.ts +20 -0
  11. package/dist/defs.d.ts.map +1 -0
  12. package/dist/defs.js +12 -0
  13. package/dist/defs.js.map +1 -0
  14. package/dist/devtools/devtools-messages-client-session.d.ts +23 -21
  15. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  16. package/dist/devtools/devtools-messages-common.d.ts +6 -6
  17. package/dist/devtools/devtools-messages-leader.d.ts +26 -24
  18. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  19. package/dist/errors.d.ts +50 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +36 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/leader-thread/LeaderSyncProcessor.d.ts +6 -7
  28. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  29. package/dist/leader-thread/LeaderSyncProcessor.js +122 -123
  30. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  31. package/dist/leader-thread/eventlog.d.ts +17 -6
  32. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  33. package/dist/leader-thread/eventlog.js +34 -17
  34. package/dist/leader-thread/eventlog.js.map +1 -1
  35. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  36. package/dist/leader-thread/leader-worker-devtools.js +1 -2
  37. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  38. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  39. package/dist/leader-thread/make-leader-thread-layer.js +37 -7
  40. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  41. package/dist/leader-thread/materialize-event.d.ts +3 -3
  42. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  43. package/dist/leader-thread/materialize-event.js +27 -10
  44. package/dist/leader-thread/materialize-event.js.map +1 -1
  45. package/dist/leader-thread/mod.d.ts +2 -0
  46. package/dist/leader-thread/mod.d.ts.map +1 -1
  47. package/dist/leader-thread/mod.js +2 -0
  48. package/dist/leader-thread/mod.js.map +1 -1
  49. package/dist/leader-thread/recreate-db.d.ts +13 -6
  50. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  51. package/dist/leader-thread/recreate-db.js +1 -3
  52. package/dist/leader-thread/recreate-db.js.map +1 -1
  53. package/dist/leader-thread/types.d.ts +6 -7
  54. package/dist/leader-thread/types.d.ts.map +1 -1
  55. package/dist/make-client-session.d.ts +1 -1
  56. package/dist/make-client-session.d.ts.map +1 -1
  57. package/dist/make-client-session.js +1 -1
  58. package/dist/make-client-session.js.map +1 -1
  59. package/dist/materializer-helper.d.ts +13 -2
  60. package/dist/materializer-helper.d.ts.map +1 -1
  61. package/dist/materializer-helper.js +25 -11
  62. package/dist/materializer-helper.js.map +1 -1
  63. package/dist/rematerialize-from-eventlog.d.ts +1 -1
  64. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  65. package/dist/rematerialize-from-eventlog.js +12 -4
  66. package/dist/rematerialize-from-eventlog.js.map +1 -1
  67. package/dist/schema/EventDef.d.ts +8 -3
  68. package/dist/schema/EventDef.d.ts.map +1 -1
  69. package/dist/schema/EventDef.js +5 -2
  70. package/dist/schema/EventDef.js.map +1 -1
  71. package/dist/schema/EventSequenceNumber.d.ts +20 -2
  72. package/dist/schema/EventSequenceNumber.d.ts.map +1 -1
  73. package/dist/schema/EventSequenceNumber.js +71 -19
  74. package/dist/schema/EventSequenceNumber.js.map +1 -1
  75. package/dist/schema/EventSequenceNumber.test.js +88 -3
  76. package/dist/schema/EventSequenceNumber.test.js.map +1 -1
  77. package/dist/schema/LiveStoreEvent.d.ts +56 -8
  78. package/dist/schema/LiveStoreEvent.d.ts.map +1 -1
  79. package/dist/schema/LiveStoreEvent.js +34 -8
  80. package/dist/schema/LiveStoreEvent.js.map +1 -1
  81. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +2 -2
  82. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
  83. package/dist/schema/state/sqlite/db-schema/hash.js +3 -1
  84. package/dist/schema/state/sqlite/db-schema/hash.js.map +1 -1
  85. package/dist/schema/state/sqlite/mod.d.ts +1 -1
  86. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  87. package/dist/schema/state/sqlite/query-builder/api.d.ts +36 -9
  88. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  89. package/dist/schema/state/sqlite/query-builder/api.js.map +1 -1
  90. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  91. package/dist/schema/state/sqlite/query-builder/impl.js +16 -11
  92. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  93. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts +1 -86
  94. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts.map +1 -1
  95. package/dist/schema/state/sqlite/query-builder/impl.test.js +34 -20
  96. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  97. package/dist/schema/state/sqlite/system-tables.d.ts +380 -432
  98. package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -1
  99. package/dist/schema/state/sqlite/system-tables.js +8 -17
  100. package/dist/schema/state/sqlite/system-tables.js.map +1 -1
  101. package/dist/schema/state/sqlite/table-def.d.ts +2 -2
  102. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  103. package/dist/schema-management/migrations.d.ts +3 -1
  104. package/dist/schema-management/migrations.d.ts.map +1 -1
  105. package/dist/schema-management/migrations.js.map +1 -1
  106. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  107. package/dist/sql-queries/sql-queries.js +2 -0
  108. package/dist/sql-queries/sql-queries.js.map +1 -1
  109. package/dist/sqlite-db-helper.d.ts +7 -0
  110. package/dist/sqlite-db-helper.d.ts.map +1 -0
  111. package/dist/sqlite-db-helper.js +29 -0
  112. package/dist/sqlite-db-helper.js.map +1 -0
  113. package/dist/sqlite-types.d.ts +72 -0
  114. package/dist/sqlite-types.d.ts.map +1 -0
  115. package/dist/sqlite-types.js +5 -0
  116. package/dist/sqlite-types.js.map +1 -0
  117. package/dist/sync/ClientSessionSyncProcessor.d.ts +12 -3
  118. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  119. package/dist/sync/ClientSessionSyncProcessor.js +37 -19
  120. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  121. package/dist/sync/next/graphology.d.ts.map +1 -1
  122. package/dist/sync/next/graphology.js +0 -6
  123. package/dist/sync/next/graphology.js.map +1 -1
  124. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  125. package/dist/sync/next/rebase-events.js +1 -0
  126. package/dist/sync/next/rebase-events.js.map +1 -1
  127. package/dist/sync/next/test/compact-events.test.js +1 -1
  128. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  129. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  130. package/dist/sync/next/test/event-fixtures.js +12 -3
  131. package/dist/sync/next/test/event-fixtures.js.map +1 -1
  132. package/dist/sync/sync.d.ts +2 -0
  133. package/dist/sync/sync.d.ts.map +1 -1
  134. package/dist/sync/sync.js +3 -0
  135. package/dist/sync/sync.js.map +1 -1
  136. package/dist/sync/syncstate.d.ts +13 -4
  137. package/dist/sync/syncstate.d.ts.map +1 -1
  138. package/dist/sync/syncstate.js +23 -10
  139. package/dist/sync/syncstate.js.map +1 -1
  140. package/dist/sync/syncstate.test.js +17 -17
  141. package/dist/sync/syncstate.test.js.map +1 -1
  142. package/dist/version.d.ts +1 -1
  143. package/dist/version.js +1 -1
  144. package/package.json +7 -6
  145. package/src/ClientSessionLeaderThreadProxy.ts +40 -0
  146. package/src/adapter-types.ts +19 -161
  147. package/src/defs.ts +17 -0
  148. package/src/errors.ts +49 -0
  149. package/src/index.ts +1 -0
  150. package/src/leader-thread/LeaderSyncProcessor.ts +157 -181
  151. package/src/leader-thread/eventlog.ts +78 -54
  152. package/src/leader-thread/leader-worker-devtools.ts +1 -2
  153. package/src/leader-thread/make-leader-thread-layer.ts +52 -8
  154. package/src/leader-thread/materialize-event.ts +33 -12
  155. package/src/leader-thread/mod.ts +2 -0
  156. package/src/leader-thread/recreate-db.ts +99 -91
  157. package/src/leader-thread/types.ts +10 -12
  158. package/src/make-client-session.ts +2 -2
  159. package/src/materializer-helper.ts +45 -19
  160. package/src/rematerialize-from-eventlog.ts +12 -4
  161. package/src/schema/EventDef.ts +16 -4
  162. package/src/schema/EventSequenceNumber.test.ts +120 -3
  163. package/src/schema/EventSequenceNumber.ts +95 -23
  164. package/src/schema/LiveStoreEvent.ts +49 -8
  165. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +2 -2
  166. package/src/schema/state/sqlite/db-schema/hash.ts +3 -3
  167. package/src/schema/state/sqlite/mod.ts +1 -1
  168. package/src/schema/state/sqlite/query-builder/api.ts +39 -9
  169. package/src/schema/state/sqlite/query-builder/impl.test.ts +60 -20
  170. package/src/schema/state/sqlite/query-builder/impl.ts +15 -12
  171. package/src/schema/state/sqlite/system-tables.ts +9 -22
  172. package/src/schema/state/sqlite/table-def.ts +2 -2
  173. package/src/schema-management/migrations.ts +3 -1
  174. package/src/sql-queries/sql-queries.ts +2 -0
  175. package/src/sqlite-db-helper.ts +41 -0
  176. package/src/sqlite-types.ts +76 -0
  177. package/src/sync/ClientSessionSyncProcessor.ts +51 -28
  178. package/src/sync/next/graphology.ts +0 -6
  179. package/src/sync/next/rebase-events.ts +1 -0
  180. package/src/sync/next/test/compact-events.test.ts +1 -1
  181. package/src/sync/next/test/event-fixtures.ts +12 -3
  182. package/src/sync/sync.ts +3 -0
  183. package/src/sync/syncstate.test.ts +17 -17
  184. package/src/sync/syncstate.ts +31 -10
  185. package/src/version.ts +1 -1
@@ -1,14 +1,13 @@
1
1
  /// <reference lib="dom" />
2
2
  import { LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
3
- import type { Runtime, Scope } from '@livestore/utils/effect'
3
+ import { Option, type Runtime, type Scope } from '@livestore/utils/effect'
4
4
  import { BucketQueue, Effect, FiberHandle, Queue, Schema, Stream, Subscribable } from '@livestore/utils/effect'
5
5
  import * as otel from '@opentelemetry/api'
6
6
 
7
- import type { ClientSession, UnexpectedError } from '../adapter-types.js'
7
+ import { type ClientSession, SyncError, type UnexpectedError } from '../adapter-types.js'
8
8
  import * as EventSequenceNumber from '../schema/EventSequenceNumber.js'
9
9
  import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
10
- import { getEventDef, type LiveStoreSchema, SystemTables } from '../schema/mod.js'
11
- import { sql } from '../util.js'
10
+ import { getEventDef, type LiveStoreSchema } from '../schema/mod.js'
12
11
  import * as SyncState from './syncstate.js'
13
12
 
14
13
  /**
@@ -21,6 +20,10 @@ import * as SyncState from './syncstate.js'
21
20
  * - We might need to make the rebase behaviour configurable e.g. to let users manually trigger a rebase
22
21
  *
23
22
  * Longer term we should evalutate whether we can unify the ClientSessionSyncProcessor with the LeaderSyncProcessor.
23
+ *
24
+ * The session and leader sync processor are different in the following ways:
25
+ * - The leader sync processor pulls regular LiveStore events, while the session sync processor pulls SyncState.PayloadUpstream items
26
+ * - The session sync processor has no downstream nodes.
24
27
  */
25
28
  export const makeClientSessionSyncProcessor = ({
26
29
  schema,
@@ -37,11 +40,12 @@ export const makeClientSessionSyncProcessor = ({
37
40
  clientSession: ClientSession
38
41
  runtime: Runtime.Runtime<Scope.Scope>
39
42
  materializeEvent: (
40
- eventDecoded: LiveStoreEvent.PartialAnyDecoded,
41
- options: { otelContext: otel.Context; withChangeset: boolean },
43
+ eventDecoded: LiveStoreEvent.AnyDecoded,
44
+ options: { otelContext: otel.Context; withChangeset: boolean; materializerHashLeader: Option.Option<number> },
42
45
  ) => {
43
46
  writeTables: Set<string>
44
47
  sessionChangeset: { _tag: 'sessionChangeset'; data: Uint8Array; debug: any } | { _tag: 'no-op' } | { _tag: 'unset' }
48
+ materializerHash: Option.Option<number>
45
49
  }
46
50
  rollback: (changeset: Uint8Array) => void
47
51
  refreshTables: (tables: Set<string>) => void
@@ -67,6 +71,7 @@ export const makeClientSessionSyncProcessor = ({
67
71
  }),
68
72
  }
69
73
 
74
+ /** Only used for debugging / observability, it's not relied upon for correctness of the sync processor. */
70
75
  const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
71
76
  const isClientEvent = (eventEncoded: LiveStoreEvent.EncodedWithMeta) =>
72
77
  getEventDef(schema, eventEncoded.name).eventDef.options.clientOnly
@@ -80,7 +85,10 @@ export const makeClientSessionSyncProcessor = ({
80
85
  let baseEventSequenceNumber = syncStateRef.current.localHead
81
86
  const encodedEventDefs = batch.map(({ name, args }) => {
82
87
  const eventDef = getEventDef(schema, name)
83
- const nextNumPair = EventSequenceNumber.nextPair(baseEventSequenceNumber, eventDef.eventDef.options.clientOnly)
88
+ const nextNumPair = EventSequenceNumber.nextPair({
89
+ seqNum: baseEventSequenceNumber,
90
+ isClient: eventDef.eventDef.options.clientOnly,
91
+ })
84
92
  baseEventSequenceNumber = nextNumPair.seqNum
85
93
  return new LiveStoreEvent.EncodedWithMeta(
86
94
  Schema.encodeUnknownSync(eventSchema)({
@@ -101,7 +109,7 @@ export const makeClientSessionSyncProcessor = ({
101
109
  })
102
110
 
103
111
  if (mergeResult._tag === 'unexpected-error') {
104
- return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.cause)
112
+ return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.message)
105
113
  }
106
114
 
107
115
  span.addEvent('local-push', {
@@ -116,17 +124,28 @@ export const makeClientSessionSyncProcessor = ({
116
124
  syncStateRef.current = mergeResult.newSyncState
117
125
  syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
118
126
 
127
+ // Materialize events to state
119
128
  const writeTables = new Set<string>()
120
129
  for (const event of mergeResult.newEvents) {
121
130
  // TODO avoid encoding and decoding here again
122
131
  const decodedEventDef = Schema.decodeSync(eventSchema)(event)
123
- const res = materializeEvent(decodedEventDef, { otelContext, withChangeset: true })
124
- for (const table of res.writeTables) {
132
+ const {
133
+ writeTables: newWriteTables,
134
+ sessionChangeset,
135
+ materializerHash,
136
+ } = materializeEvent(decodedEventDef, {
137
+ otelContext,
138
+ withChangeset: true,
139
+ materializerHashLeader: Option.none(),
140
+ })
141
+ for (const table of newWriteTables) {
125
142
  writeTables.add(table)
126
143
  }
127
- event.meta.sessionChangeset = res.sessionChangeset
144
+ event.meta.sessionChangeset = sessionChangeset
145
+ event.meta.materializerHashSession = materializerHash
128
146
  }
129
147
 
148
+ // Trigger push to leader
130
149
  // console.debug('pushToLeader', encodedEventDefs.length, ...encodedEventDefs.map((_) => _.toJSON()))
131
150
  BucketQueue.offerAll(leaderPushQueue, encodedEventDefs).pipe(Effect.runSync)
132
151
 
@@ -171,18 +190,11 @@ export const makeClientSessionSyncProcessor = ({
171
190
 
172
191
  yield* FiberHandle.run(leaderPushingFiberHandle, backgroundLeaderPushing)
173
192
 
174
- const getMergeCounter = () =>
175
- clientSession.sqliteDb.select<{ mergeCounter: number }>(
176
- sql`SELECT mergeCounter FROM ${SystemTables.LEADER_MERGE_COUNTER_TABLE} WHERE id = 0`,
177
- )[0]?.mergeCounter ?? 0
178
-
179
193
  // NOTE We need to lazily call `.pull` as we want the cursor to be updated
180
194
  yield* Stream.suspend(() =>
181
- clientSession.leaderThread.events.pull({
182
- cursor: { mergeCounter: getMergeCounter(), eventNum: syncStateRef.current.localHead },
183
- }),
195
+ clientSession.leaderThread.events.pull({ cursor: syncStateRef.current.upstreamHead }),
184
196
  ).pipe(
185
- Stream.tap(({ payload, mergeCounter: leaderMergeCounter }) =>
197
+ Stream.tap(({ payload }) =>
186
198
  Effect.gen(function* () {
187
199
  // yield* Effect.logDebug('ClientSessionSyncProcessor:pull', payload)
188
200
 
@@ -198,13 +210,13 @@ export const makeClientSessionSyncProcessor = ({
198
210
  })
199
211
 
200
212
  if (mergeResult._tag === 'unexpected-error') {
201
- return yield* Effect.fail(mergeResult.cause)
213
+ return yield* new SyncError({ cause: mergeResult.message })
202
214
  } else if (mergeResult._tag === 'reject') {
203
215
  return shouldNeverHappen('Unexpected reject in client-session-sync-processor', mergeResult)
204
216
  }
205
217
 
206
218
  syncStateRef.current = mergeResult.newSyncState
207
- syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
219
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
208
220
 
209
221
  if (mergeResult._tag === 'rebase') {
210
222
  span.addEvent('merge:pull:rebase', {
@@ -213,7 +225,7 @@ export const makeClientSessionSyncProcessor = ({
213
225
  newEventsCount: mergeResult.newEvents.length,
214
226
  rollbackCount: mergeResult.rollbackEvents.length,
215
227
  res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
216
- leaderMergeCounter,
228
+ rebaseGeneration: mergeResult.newSyncState.localHead.rebaseGeneration,
217
229
  })
218
230
 
219
231
  debugInfo.rebaseCount++
@@ -230,7 +242,6 @@ export const makeClientSessionSyncProcessor = ({
230
242
  'merge:pull:rebase: rollback',
231
243
  mergeResult.rollbackEvents.length,
232
244
  ...mergeResult.rollbackEvents.slice(0, 10).map((_) => _.toJSON()),
233
- { leaderMergeCounter },
234
245
  ).pipe(Effect.provide(runtime), Effect.runSync)
235
246
  }
236
247
 
@@ -242,6 +253,7 @@ export const makeClientSessionSyncProcessor = ({
242
253
  }
243
254
  }
244
255
 
256
+ // Pushing rebased pending events to leader
245
257
  yield* BucketQueue.offerAll(leaderPushQueue, mergeResult.newSyncState.pending)
246
258
  } else {
247
259
  span.addEvent('merge:pull:advance', {
@@ -249,7 +261,6 @@ export const makeClientSessionSyncProcessor = ({
249
261
  payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
250
262
  newEventsCount: mergeResult.newEvents.length,
251
263
  res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
252
- leaderMergeCounter,
253
264
  })
254
265
 
255
266
  debugInfo.advanceCount++
@@ -261,12 +272,21 @@ export const makeClientSessionSyncProcessor = ({
261
272
  for (const event of mergeResult.newEvents) {
262
273
  // TODO apply changeset if available (will require tracking of write tables as well)
263
274
  const decodedEventDef = Schema.decodeSync(eventSchema)(event)
264
- const res = materializeEvent(decodedEventDef, { otelContext, withChangeset: true })
265
- for (const table of res.writeTables) {
275
+ const {
276
+ writeTables: newWriteTables,
277
+ sessionChangeset,
278
+ materializerHash,
279
+ } = materializeEvent(decodedEventDef, {
280
+ otelContext,
281
+ withChangeset: true,
282
+ materializerHashLeader: event.meta.materializerHashLeader,
283
+ })
284
+ for (const table of newWriteTables) {
266
285
  writeTables.add(table)
267
286
  }
268
287
 
269
- event.meta.sessionChangeset = res.sessionChangeset
288
+ event.meta.sessionChangeset = sessionChangeset
289
+ event.meta.materializerHashSession = materializerHash
270
290
  }
271
291
 
272
292
  refreshTables(writeTables)
@@ -321,6 +341,9 @@ export interface ClientSessionSyncProcessor {
321
341
  writeTables: Set<string>
322
342
  }
323
343
  boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
344
+ /**
345
+ * Only used for debugging / observability.
346
+ */
324
347
  syncState: Subscribable.Subscribable<SyncState.SyncState>
325
348
  debug: {
326
349
  print: () => void
@@ -14,15 +14,9 @@ export declare class IGraph<
14
14
  }
15
15
 
16
16
  export const DirectedGraph = class DirectedGraph extends graphology.DirectedGraph {
17
- constructor(options?: graphologyTypes.GraphOptions) {
18
- super(options)
19
- }
20
17
  } as typeof IGraph
21
18
 
22
19
  export const Graph = class Graph extends graphology.Graph {
23
- constructor(options?: graphologyTypes.GraphOptions) {
24
- super(options)
25
- }
26
20
  } as typeof IGraph
27
21
 
28
22
  // export const graphology = graphology_ as graphologyTypes
@@ -38,6 +38,7 @@ export const defaultRebaseFn: RebaseFn = ({ pendingLocalEvents }) => {
38
38
  return { rebasedLocalEvents: pendingLocalEvents }
39
39
  }
40
40
 
41
+ // TODO replace in favour of current rebase impl
41
42
  export const rebaseEvents = ({
42
43
  rebaseFn,
43
44
  pendingLocalEvents,
@@ -64,7 +64,7 @@ const factsSetToString = (facts: EventDefFacts, prefix: string) =>
64
64
  export const customSerializer = {
65
65
  test: (val: unknown) => Array.isArray(val),
66
66
  print: (val: unknown[], _serialize: (item: unknown) => string) => {
67
- return '[\n' + (val as any[]).map((item) => ' ' + customStringify(item)).join('\n') + '\n]'
67
+ return `[\n${(val as any[]).map((item) => ` ${customStringify(item)}`).join('\n')}\n]`
68
68
  },
69
69
  } as any
70
70
 
@@ -144,7 +144,10 @@ export const toEventNodes = (
144
144
 
145
145
  const eventNodes = partialEvents.map((partialEvent) => {
146
146
  const eventDef = eventDefs[partialEvent.name]!
147
- const eventNum = EventSequenceNumber.nextPair(currentEventSequenceNumber, eventDef.options.clientOnly).seqNum
147
+ const eventNum = EventSequenceNumber.nextPair({
148
+ seqNum: currentEventSequenceNumber,
149
+ isClient: eventDef.options.clientOnly,
150
+ }).seqNum
148
151
  currentEventSequenceNumber = eventNum
149
152
 
150
153
  const factsSnapshot = factsSnapshotForDag(historyDagFromNodes(nodesAcc, { skipFactsCheck: true }), undefined)
@@ -221,8 +224,14 @@ const getParentNum = (eventNum: EventSequenceNumber.EventSequenceNumber): EventS
221
224
  const clientParentNum = eventNum.client - 1
222
225
 
223
226
  if (clientParentNum < 0) {
224
- return EventSequenceNumber.make({ global: globalParentNum - 1, client: EventSequenceNumber.clientDefault })
227
+ return EventSequenceNumber.make({
228
+ global: globalParentNum - 1,
229
+ client: EventSequenceNumber.clientDefault,
230
+ })
225
231
  }
226
232
 
227
- return EventSequenceNumber.make({ global: globalParentNum, client: clientParentNum })
233
+ return EventSequenceNumber.make({
234
+ global: globalParentNum,
235
+ client: clientParentNum,
236
+ })
228
237
  }
package/src/sync/sync.ts CHANGED
@@ -80,6 +80,7 @@ export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
80
80
 
81
81
  export class IsOfflineError extends Schema.TaggedError<IsOfflineError>()('IsOfflineError', {}) {}
82
82
 
83
+ // TODO gt rid of this error in favour of SyncError
83
84
  export class InvalidPushError extends Schema.TaggedError<InvalidPushError>()('InvalidPushError', {
84
85
  reason: Schema.Union(
85
86
  Schema.TaggedStruct('Unexpected', {
@@ -92,10 +93,12 @@ export class InvalidPushError extends Schema.TaggedError<InvalidPushError>()('In
92
93
  ),
93
94
  }) {}
94
95
 
96
+ // TODO gt rid of this error in favour of SyncError
95
97
  export class InvalidPullError extends Schema.TaggedError<InvalidPullError>()('InvalidPullError', {
96
98
  message: Schema.String,
97
99
  }) {}
98
100
 
101
+ // TODO gt rid of this error in favour of SyncError
99
102
  export class LeaderAheadError extends Schema.TaggedError<LeaderAheadError>()('LeaderAheadError', {
100
103
  minimumExpectedNum: EventSequenceNumber.EventSequenceNumber,
101
104
  providedNum: EventSequenceNumber.EventSequenceNumber,
@@ -7,10 +7,10 @@ import * as SyncState from './syncstate.js'
7
7
 
8
8
  class TestEvent extends LiveStoreEvent.EncodedWithMeta {
9
9
  constructor(
10
- seqNum: EventSequenceNumber.EventSequenceNumber | typeof EventSequenceNumber.EventSequenceNumber.Encoded,
11
- parentSeqNum: EventSequenceNumber.EventSequenceNumber,
10
+ seqNum: EventSequenceNumber.EventSequenceNumberInput,
11
+ parentSeqNum: EventSequenceNumber.EventSequenceNumberInput,
12
12
  public readonly payload: string,
13
- public readonly isLocal: boolean,
13
+ public readonly isClient: boolean,
14
14
  ) {
15
15
  super({
16
16
  seqNum: EventSequenceNumber.make(seqNum),
@@ -22,8 +22,8 @@ class TestEvent extends LiveStoreEvent.EncodedWithMeta {
22
22
  })
23
23
  }
24
24
 
25
- rebase_ = (parentSeqNum: EventSequenceNumber.EventSequenceNumber) => {
26
- return this.rebase(parentSeqNum, this.isLocal)
25
+ rebase_ = (parentSeqNum: EventSequenceNumber.EventSequenceNumber, rebaseGeneration: number) => {
26
+ return this.rebase({ parentSeqNum, isClient: this.isClient, rebaseGeneration })
27
27
  }
28
28
 
29
29
  // Only used for Vitest printing
@@ -41,7 +41,7 @@ const e2_1 = new TestEvent({ global: 2, client: 1 }, e2_0.seqNum, 'a', true)
41
41
 
42
42
  const isEqualEvent = LiveStoreEvent.isEqualEncoded
43
43
 
44
- const isClientEvent = (event: LiveStoreEvent.EncodedWithMeta) => (event as TestEvent).isLocal
44
+ const isClientEvent = (event: LiveStoreEvent.EncodedWithMeta) => (event as TestEvent).isClient
45
45
 
46
46
  describe('syncstate', () => {
47
47
  describe('merge', () => {
@@ -62,8 +62,8 @@ describe('syncstate', () => {
62
62
  upstreamHead: EventSequenceNumber.ROOT,
63
63
  localHead: e2_0.seqNum,
64
64
  })
65
- const e1_0_e2_0 = e1_0.rebase_(e2_0.seqNum)
66
- const e1_1_e2_1 = e1_1.rebase_(e1_0_e2_0.seqNum)
65
+ const e1_0_e2_0 = e1_0.rebase_(e2_0.seqNum, 0)
66
+ const e1_1_e2_1 = e1_1.rebase_(e1_0_e2_0.seqNum, 0)
67
67
  const result = merge({
68
68
  syncState,
69
69
  payload: SyncState.PayloadUpstreamRebase.make({
@@ -71,7 +71,7 @@ describe('syncstate', () => {
71
71
  newEvents: [e1_0_e2_0, e1_1_e2_1],
72
72
  }),
73
73
  })
74
- const e2_0_e3_0 = e2_0.rebase_(e1_0_e2_0.seqNum)
74
+ const e2_0_e3_0 = e2_0.rebase_(e1_0_e2_0.seqNum, 1)
75
75
  expectRebase(result)
76
76
  expectEventArraysEqual(result.newSyncState.pending, [e2_0_e3_0])
77
77
  expect(result.newSyncState.upstreamHead).toMatchObject(e1_1_e2_1.seqNum)
@@ -86,7 +86,7 @@ describe('syncstate', () => {
86
86
  upstreamHead: EventSequenceNumber.ROOT,
87
87
  localHead: e2_0.seqNum,
88
88
  })
89
- const e1_1_e2_0 = e1_1.rebase_(e1_0.seqNum)
89
+ const e1_1_e2_0 = e1_1.rebase_(e1_0.seqNum, 0)
90
90
  const result = merge({
91
91
  syncState,
92
92
  payload: SyncState.PayloadUpstreamRebase.make({
@@ -94,7 +94,7 @@ describe('syncstate', () => {
94
94
  rollbackEvents: [e1_1],
95
95
  }),
96
96
  })
97
- const e2_0_e3_0 = e2_0.rebase_(e1_1_e2_0.seqNum)
97
+ const e2_0_e3_0 = e2_0.rebase_(e1_1_e2_0.seqNum, 1)
98
98
  expectRebase(result)
99
99
  expectEventArraysEqual(result.newSyncState.pending, [e2_0_e3_0])
100
100
  expect(result.newSyncState.upstreamHead).toMatchObject(e1_1_e2_0.seqNum)
@@ -326,7 +326,7 @@ describe('syncstate', () => {
326
326
  })
327
327
  const result = merge({ syncState, payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e1_1] }) })
328
328
 
329
- const e1_0_e1_2 = e1_0.rebase_(e1_1.seqNum)
329
+ const e1_0_e1_2 = e1_0.rebase_(e1_1.seqNum, 1)
330
330
 
331
331
  expectRebase(result)
332
332
  expectEventArraysEqual(result.newSyncState.pending, [e1_0_e1_2])
@@ -344,7 +344,7 @@ describe('syncstate', () => {
344
344
  localHead: e2_0_b.seqNum,
345
345
  })
346
346
  const result = merge({ syncState, payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e2_0] }) })
347
- const e2_0_e3_0 = e2_0_b.rebase_(e2_0.seqNum)
347
+ const e2_0_e3_0 = e2_0_b.rebase_(e2_0.seqNum, 1)
348
348
 
349
349
  expectRebase(result)
350
350
  expectEventArraysEqual(result.newSyncState.pending, [e2_0_e3_0])
@@ -365,7 +365,7 @@ describe('syncstate', () => {
365
365
  payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e1_1, e1_2, e1_3, e2_0] }),
366
366
  })
367
367
 
368
- const e1_0_e3_0 = e1_0.rebase_(e2_0.seqNum)
368
+ const e1_0_e3_0 = e1_0.rebase_(e2_0.seqNum, 1)
369
369
 
370
370
  expectRebase(result)
371
371
  expectEventArraysEqual(result.newSyncState.pending, [e1_0_e3_0])
@@ -384,7 +384,7 @@ describe('syncstate', () => {
384
384
  payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e1_0, e1_2, e1_3, e2_0] }),
385
385
  })
386
386
 
387
- const e1_1_e2_1 = e1_1.rebase_(e2_0.seqNum)
387
+ const e1_1_e2_1 = e1_1.rebase_(e2_0.seqNum, 1)
388
388
 
389
389
  expectRebase(result)
390
390
  expectEventArraysEqual(result.newSyncState.pending, [e1_1_e2_1])
@@ -405,8 +405,8 @@ describe('syncstate', () => {
405
405
  payload: SyncState.PayloadUpstreamAdvance.make({ newEvents: [e1_1, e1_2, e1_3, e2_0] }),
406
406
  })
407
407
 
408
- const e1_0_e2_1 = e1_0.rebase_(e2_0.seqNum)
409
- const e1_1_e2_2 = e1_1.rebase_(e1_0_e2_1.seqNum)
408
+ const e1_0_e2_1 = e1_0.rebase_(e2_0.seqNum, 1)
409
+ const e1_1_e2_2 = e1_1.rebase_(e1_0_e2_1.seqNum, 1)
410
410
 
411
411
  expectRebase(result)
412
412
  expectEventArraysEqual(result.newSyncState.pending, [e1_0_e2_1, e1_1_e2_2])
@@ -1,7 +1,6 @@
1
1
  import { casesHandled, LS_DEV, shouldNeverHappen } from '@livestore/utils'
2
2
  import { Match, ReadonlyArray, Schema } from '@livestore/utils/effect'
3
3
 
4
- import { UnexpectedError } from '../adapter-types.js'
5
4
  import * as EventSequenceNumber from '../schema/EventSequenceNumber.js'
6
5
  import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
7
6
 
@@ -162,7 +161,7 @@ export class MergeResultReject extends Schema.Class<MergeResultReject>('MergeRes
162
161
 
163
162
  export class MergeResultUnexpectedError extends Schema.Class<MergeResultUnexpectedError>('MergeResultUnexpectedError')({
164
163
  _tag: Schema.Literal('unexpected-error'),
165
- cause: UnexpectedError,
164
+ message: Schema.String,
166
165
  }) {}
167
166
 
168
167
  export class MergeResult extends Schema.Union(
@@ -172,15 +171,29 @@ export class MergeResult extends Schema.Union(
172
171
  MergeResultUnexpectedError,
173
172
  ) {}
174
173
 
175
- const unexpectedError = (cause: unknown): MergeResultUnexpectedError => {
174
+ export const payloadFromMergeResult = (
175
+ mergeResult: typeof MergeResultAdvance.Type | typeof MergeResultRebase.Type,
176
+ ): typeof PayloadUpstream.Type =>
177
+ Match.value(mergeResult).pipe(
178
+ Match.tag('advance', (result) => ({
179
+ _tag: 'upstream-advance' as const,
180
+ newEvents: result.newEvents,
181
+ })),
182
+ Match.tag('rebase', (result) => ({
183
+ _tag: 'upstream-rebase' as const,
184
+ newEvents: result.newEvents,
185
+ rollbackEvents: result.rollbackEvents,
186
+ })),
187
+ Match.exhaustive,
188
+ )
189
+
190
+ const unexpectedError = (message: string): MergeResultUnexpectedError => {
176
191
  if (LS_DEV) {
192
+ // biome-ignore lint/suspicious/noDebugger: debug
177
193
  debugger
178
194
  }
179
195
 
180
- return MergeResultUnexpectedError.make({
181
- _tag: 'unexpected-error',
182
- cause: new UnexpectedError({ cause }),
183
- })
196
+ return MergeResultUnexpectedError.make({ _tag: 'unexpected-error', message })
184
197
  }
185
198
 
186
199
  // TODO Idea: call merge recursively through hierarchy levels
@@ -382,7 +395,10 @@ export const merge = ({
382
395
  EventSequenceNumber.isGreaterThan(newEventsFirst.seqNum, syncState.localHead) === false
383
396
 
384
397
  if (invalidEventSequenceNumber) {
385
- const expectedMinimumId = EventSequenceNumber.nextPair(syncState.localHead, true).seqNum
398
+ const expectedMinimumId = EventSequenceNumber.nextPair({
399
+ seqNum: syncState.localHead,
400
+ isClient: true,
401
+ }).seqNum
386
402
  return validateMergeResult(
387
403
  MergeResultReject.make({
388
404
  _tag: 'reject',
@@ -466,9 +482,14 @@ const rebaseEvents = ({
466
482
  isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
467
483
  }): ReadonlyArray<LiveStoreEvent.EncodedWithMeta> => {
468
484
  let prevEventSequenceNumber = baseEventSequenceNumber
485
+ const rebaseGeneration = baseEventSequenceNumber.rebaseGeneration + 1
469
486
  return events.map((event) => {
470
- const isLocal = isClientEvent(event)
471
- const newEvent = event.rebase(prevEventSequenceNumber, isLocal)
487
+ const isClient = isClientEvent(event)
488
+ const newEvent = event.rebase({
489
+ parentSeqNum: prevEventSequenceNumber,
490
+ isClient,
491
+ rebaseGeneration,
492
+ })
472
493
  prevEventSequenceNumber = newEvent.seqNum
473
494
  return newEvent
474
495
  })
package/src/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // import packageJson from '../package.json' with { type: 'json' }
3
3
  // export const liveStoreVersion = packageJson.version
4
4
 
5
- export const liveStoreVersion = '0.3.1-dev.0' as const
5
+ export const liveStoreVersion = '0.3.2-dev.0' as const
6
6
 
7
7
  /**
8
8
  * This version number is incremented whenever the internal storage format changes in a breaking way.