@livestore/common 0.0.0-snapshot-2b8a9de3ec1a701aca891ebc2c98eb328274ae9e → 0.0.0-snapshot-2c861249e50661661613204300b1fc0d902c2e46

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 (287) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +83 -221
  3. package/dist/__tests__/fixture.d.ts.map +1 -1
  4. package/dist/__tests__/fixture.js +33 -11
  5. package/dist/__tests__/fixture.js.map +1 -1
  6. package/dist/adapter-types.d.ts +36 -22
  7. package/dist/adapter-types.d.ts.map +1 -1
  8. package/dist/adapter-types.js +20 -8
  9. package/dist/adapter-types.js.map +1 -1
  10. package/dist/debug-info.d.ts.map +1 -1
  11. package/dist/debug-info.js +1 -0
  12. package/dist/debug-info.js.map +1 -1
  13. package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
  14. package/dist/devtools/devtools-messages-common.d.ts +13 -6
  15. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  16. package/dist/devtools/devtools-messages-common.js +6 -0
  17. package/dist/devtools/devtools-messages-common.js.map +1 -1
  18. package/dist/devtools/devtools-messages-leader.d.ts +46 -46
  19. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  20. package/dist/devtools/devtools-messages-leader.js +12 -13
  21. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  22. package/dist/index.d.ts +2 -5
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -5
  25. package/dist/index.js.map +1 -1
  26. package/dist/leader-thread/LeaderSyncProcessor.d.ts +34 -12
  27. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  28. package/dist/leader-thread/LeaderSyncProcessor.js +284 -226
  29. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  30. package/dist/leader-thread/apply-event.d.ts +16 -0
  31. package/dist/leader-thread/apply-event.d.ts.map +1 -0
  32. package/dist/leader-thread/apply-event.js +122 -0
  33. package/dist/leader-thread/apply-event.js.map +1 -0
  34. package/dist/leader-thread/eventlog.d.ts +27 -0
  35. package/dist/leader-thread/eventlog.d.ts.map +1 -0
  36. package/dist/leader-thread/eventlog.js +123 -0
  37. package/dist/leader-thread/eventlog.js.map +1 -0
  38. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  39. package/dist/leader-thread/leader-worker-devtools.js +22 -23
  40. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  41. package/dist/leader-thread/make-leader-thread-layer.d.ts +16 -4
  42. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  43. package/dist/leader-thread/make-leader-thread-layer.js +36 -41
  44. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  45. package/dist/leader-thread/mod.d.ts +1 -1
  46. package/dist/leader-thread/mod.d.ts.map +1 -1
  47. package/dist/leader-thread/mod.js +1 -1
  48. package/dist/leader-thread/mod.js.map +1 -1
  49. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  50. package/dist/leader-thread/recreate-db.js +7 -7
  51. package/dist/leader-thread/recreate-db.js.map +1 -1
  52. package/dist/leader-thread/types.d.ts +40 -25
  53. package/dist/leader-thread/types.d.ts.map +1 -1
  54. package/dist/leader-thread/types.js.map +1 -1
  55. package/dist/materializer-helper.d.ts +23 -0
  56. package/dist/materializer-helper.d.ts.map +1 -0
  57. package/dist/materializer-helper.js +70 -0
  58. package/dist/materializer-helper.js.map +1 -0
  59. package/dist/query-builder/api.d.ts +55 -50
  60. package/dist/query-builder/api.d.ts.map +1 -1
  61. package/dist/query-builder/api.js +3 -5
  62. package/dist/query-builder/api.js.map +1 -1
  63. package/dist/query-builder/astToSql.d.ts.map +1 -1
  64. package/dist/query-builder/astToSql.js +59 -37
  65. package/dist/query-builder/astToSql.js.map +1 -1
  66. package/dist/query-builder/impl.d.ts +2 -3
  67. package/dist/query-builder/impl.d.ts.map +1 -1
  68. package/dist/query-builder/impl.js +47 -43
  69. package/dist/query-builder/impl.js.map +1 -1
  70. package/dist/query-builder/impl.test.d.ts +86 -1
  71. package/dist/query-builder/impl.test.d.ts.map +1 -1
  72. package/dist/query-builder/impl.test.js +223 -36
  73. package/dist/query-builder/impl.test.js.map +1 -1
  74. package/dist/rehydrate-from-eventlog.d.ts +15 -0
  75. package/dist/rehydrate-from-eventlog.d.ts.map +1 -0
  76. package/dist/{rehydrate-from-mutationlog.js → rehydrate-from-eventlog.js} +27 -28
  77. package/dist/rehydrate-from-eventlog.js.map +1 -0
  78. package/dist/schema/EventDef.d.ts +136 -0
  79. package/dist/schema/EventDef.d.ts.map +1 -0
  80. package/dist/schema/EventDef.js +58 -0
  81. package/dist/schema/EventDef.js.map +1 -0
  82. package/dist/schema/EventId.d.ts +10 -1
  83. package/dist/schema/EventId.d.ts.map +1 -1
  84. package/dist/schema/EventId.js +24 -3
  85. package/dist/schema/EventId.js.map +1 -1
  86. package/dist/schema/LiveStoreEvent.d.ts +255 -0
  87. package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
  88. package/dist/schema/LiveStoreEvent.js +118 -0
  89. package/dist/schema/LiveStoreEvent.js.map +1 -0
  90. package/dist/schema/client-document-def.d.ts +223 -0
  91. package/dist/schema/client-document-def.d.ts.map +1 -0
  92. package/dist/schema/client-document-def.js +164 -0
  93. package/dist/schema/client-document-def.js.map +1 -0
  94. package/dist/schema/client-document-def.test.d.ts +2 -0
  95. package/dist/schema/client-document-def.test.d.ts.map +1 -0
  96. package/dist/schema/client-document-def.test.js +161 -0
  97. package/dist/schema/client-document-def.test.js.map +1 -0
  98. package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -1
  99. package/dist/schema/events.d.ts +2 -0
  100. package/dist/schema/events.d.ts.map +1 -0
  101. package/dist/schema/events.js +2 -0
  102. package/dist/schema/events.js.map +1 -0
  103. package/dist/schema/mod.d.ts +4 -3
  104. package/dist/schema/mod.d.ts.map +1 -1
  105. package/dist/schema/mod.js +4 -3
  106. package/dist/schema/mod.js.map +1 -1
  107. package/dist/schema/schema.d.ts +26 -22
  108. package/dist/schema/schema.d.ts.map +1 -1
  109. package/dist/schema/schema.js +45 -43
  110. package/dist/schema/schema.js.map +1 -1
  111. package/dist/schema/sqlite-state.d.ts +12 -0
  112. package/dist/schema/sqlite-state.d.ts.map +1 -0
  113. package/dist/schema/sqlite-state.js +36 -0
  114. package/dist/schema/sqlite-state.js.map +1 -0
  115. package/dist/schema/system-tables.d.ts +121 -85
  116. package/dist/schema/system-tables.d.ts.map +1 -1
  117. package/dist/schema/system-tables.js +68 -43
  118. package/dist/schema/system-tables.js.map +1 -1
  119. package/dist/schema/table-def.d.ts +26 -96
  120. package/dist/schema/table-def.d.ts.map +1 -1
  121. package/dist/schema/table-def.js +14 -64
  122. package/dist/schema/table-def.js.map +1 -1
  123. package/dist/schema/view.d.ts +3 -0
  124. package/dist/schema/view.d.ts.map +1 -0
  125. package/dist/schema/view.js +3 -0
  126. package/dist/schema/view.js.map +1 -0
  127. package/dist/schema-management/common.d.ts +4 -4
  128. package/dist/schema-management/common.d.ts.map +1 -1
  129. package/dist/schema-management/migrations.d.ts.map +1 -1
  130. package/dist/schema-management/migrations.js +6 -6
  131. package/dist/schema-management/migrations.js.map +1 -1
  132. package/dist/schema-management/validate-mutation-defs.d.ts +3 -3
  133. package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -1
  134. package/dist/schema-management/validate-mutation-defs.js +17 -17
  135. package/dist/schema-management/validate-mutation-defs.js.map +1 -1
  136. package/dist/sync/ClientSessionSyncProcessor.d.ts +16 -8
  137. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  138. package/dist/sync/ClientSessionSyncProcessor.js +50 -43
  139. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  140. package/dist/sync/next/facts.d.ts +19 -19
  141. package/dist/sync/next/facts.d.ts.map +1 -1
  142. package/dist/sync/next/facts.js +2 -2
  143. package/dist/sync/next/facts.js.map +1 -1
  144. package/dist/sync/next/history-dag-common.d.ts +3 -3
  145. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  146. package/dist/sync/next/history-dag-common.js +1 -1
  147. package/dist/sync/next/history-dag-common.js.map +1 -1
  148. package/dist/sync/next/history-dag.js +1 -1
  149. package/dist/sync/next/history-dag.js.map +1 -1
  150. package/dist/sync/next/rebase-events.d.ts +7 -7
  151. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  152. package/dist/sync/next/rebase-events.js +5 -5
  153. package/dist/sync/next/rebase-events.js.map +1 -1
  154. package/dist/sync/next/test/compact-events.calculator.test.js +38 -33
  155. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  156. package/dist/sync/next/test/compact-events.test.js +71 -71
  157. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  158. package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +25 -25
  159. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
  160. package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +60 -25
  161. package/dist/sync/next/test/event-fixtures.js.map +1 -0
  162. package/dist/sync/next/test/mod.d.ts +1 -1
  163. package/dist/sync/next/test/mod.d.ts.map +1 -1
  164. package/dist/sync/next/test/mod.js +1 -1
  165. package/dist/sync/next/test/mod.js.map +1 -1
  166. package/dist/sync/sync.d.ts +8 -7
  167. package/dist/sync/sync.d.ts.map +1 -1
  168. package/dist/sync/sync.js.map +1 -1
  169. package/dist/sync/syncstate.d.ts +69 -93
  170. package/dist/sync/syncstate.d.ts.map +1 -1
  171. package/dist/sync/syncstate.js +143 -146
  172. package/dist/sync/syncstate.js.map +1 -1
  173. package/dist/sync/syncstate.test.js +208 -289
  174. package/dist/sync/syncstate.test.js.map +1 -1
  175. package/dist/sync/validate-push-payload.d.ts +2 -2
  176. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  177. package/dist/sync/validate-push-payload.js.map +1 -1
  178. package/dist/version.d.ts +1 -1
  179. package/dist/version.js +1 -1
  180. package/package.json +2 -2
  181. package/src/__tests__/fixture.ts +36 -15
  182. package/src/adapter-types.ts +34 -23
  183. package/src/debug-info.ts +1 -0
  184. package/src/devtools/devtools-messages-common.ts +9 -0
  185. package/src/devtools/devtools-messages-leader.ts +14 -15
  186. package/src/index.ts +2 -5
  187. package/src/leader-thread/LeaderSyncProcessor.ts +485 -389
  188. package/src/leader-thread/apply-event.ts +197 -0
  189. package/src/leader-thread/eventlog.ts +199 -0
  190. package/src/leader-thread/leader-worker-devtools.ts +23 -25
  191. package/src/leader-thread/make-leader-thread-layer.ts +68 -61
  192. package/src/leader-thread/mod.ts +1 -1
  193. package/src/leader-thread/recreate-db.ts +7 -8
  194. package/src/leader-thread/types.ts +39 -29
  195. package/src/materializer-helper.ts +110 -0
  196. package/src/query-builder/api.ts +76 -102
  197. package/src/query-builder/astToSql.ts +68 -39
  198. package/src/query-builder/impl.test.ts +239 -42
  199. package/src/query-builder/impl.ts +66 -54
  200. package/src/{rehydrate-from-mutationlog.ts → rehydrate-from-eventlog.ts} +37 -40
  201. package/src/schema/EventDef.ts +216 -0
  202. package/src/schema/EventId.ts +30 -4
  203. package/src/schema/LiveStoreEvent.ts +239 -0
  204. package/src/schema/client-document-def.test.ts +188 -0
  205. package/src/schema/client-document-def.ts +436 -0
  206. package/src/schema/db-schema/dsl/mod.ts +0 -1
  207. package/src/schema/events.ts +1 -0
  208. package/src/schema/mod.ts +4 -3
  209. package/src/schema/schema.ts +78 -68
  210. package/src/schema/sqlite-state.ts +62 -0
  211. package/src/schema/system-tables.ts +54 -46
  212. package/src/schema/table-def.ts +51 -209
  213. package/src/schema/view.ts +2 -0
  214. package/src/schema-management/common.ts +4 -4
  215. package/src/schema-management/migrations.ts +8 -9
  216. package/src/schema-management/validate-mutation-defs.ts +22 -24
  217. package/src/sync/ClientSessionSyncProcessor.ts +66 -53
  218. package/src/sync/next/facts.ts +31 -32
  219. package/src/sync/next/history-dag-common.ts +4 -4
  220. package/src/sync/next/history-dag.ts +1 -1
  221. package/src/sync/next/rebase-events.ts +13 -13
  222. package/src/sync/next/test/compact-events.calculator.test.ts +45 -45
  223. package/src/sync/next/test/compact-events.test.ts +73 -73
  224. package/src/sync/next/test/event-fixtures.ts +219 -0
  225. package/src/sync/next/test/mod.ts +1 -1
  226. package/src/sync/sync.ts +9 -12
  227. package/src/sync/syncstate.test.ts +236 -323
  228. package/src/sync/syncstate.ts +218 -203
  229. package/src/sync/validate-push-payload.ts +2 -2
  230. package/src/version.ts +1 -1
  231. package/tsconfig.json +1 -0
  232. package/dist/derived-mutations.d.ts +0 -109
  233. package/dist/derived-mutations.d.ts.map +0 -1
  234. package/dist/derived-mutations.js +0 -54
  235. package/dist/derived-mutations.js.map +0 -1
  236. package/dist/derived-mutations.test.d.ts +0 -2
  237. package/dist/derived-mutations.test.d.ts.map +0 -1
  238. package/dist/derived-mutations.test.js +0 -93
  239. package/dist/derived-mutations.test.js.map +0 -1
  240. package/dist/init-singleton-tables.d.ts +0 -4
  241. package/dist/init-singleton-tables.d.ts.map +0 -1
  242. package/dist/init-singleton-tables.js +0 -16
  243. package/dist/init-singleton-tables.js.map +0 -1
  244. package/dist/leader-thread/apply-mutation.d.ts +0 -11
  245. package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
  246. package/dist/leader-thread/apply-mutation.js +0 -115
  247. package/dist/leader-thread/apply-mutation.js.map +0 -1
  248. package/dist/leader-thread/mutationlog.d.ts +0 -11
  249. package/dist/leader-thread/mutationlog.d.ts.map +0 -1
  250. package/dist/leader-thread/mutationlog.js +0 -31
  251. package/dist/leader-thread/mutationlog.js.map +0 -1
  252. package/dist/leader-thread/pull-queue-set.d.ts +0 -7
  253. package/dist/leader-thread/pull-queue-set.d.ts.map +0 -1
  254. package/dist/leader-thread/pull-queue-set.js +0 -48
  255. package/dist/leader-thread/pull-queue-set.js.map +0 -1
  256. package/dist/mutation.d.ts +0 -20
  257. package/dist/mutation.d.ts.map +0 -1
  258. package/dist/mutation.js +0 -68
  259. package/dist/mutation.js.map +0 -1
  260. package/dist/query-info.d.ts +0 -41
  261. package/dist/query-info.d.ts.map +0 -1
  262. package/dist/query-info.js +0 -7
  263. package/dist/query-info.js.map +0 -1
  264. package/dist/rehydrate-from-mutationlog.d.ts +0 -14
  265. package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
  266. package/dist/rehydrate-from-mutationlog.js.map +0 -1
  267. package/dist/schema/MutationEvent.d.ts +0 -202
  268. package/dist/schema/MutationEvent.d.ts.map +0 -1
  269. package/dist/schema/MutationEvent.js +0 -105
  270. package/dist/schema/MutationEvent.js.map +0 -1
  271. package/dist/schema/mutations.d.ts +0 -115
  272. package/dist/schema/mutations.d.ts.map +0 -1
  273. package/dist/schema/mutations.js +0 -42
  274. package/dist/schema/mutations.js.map +0 -1
  275. package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
  276. package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
  277. package/src/derived-mutations.test.ts +0 -101
  278. package/src/derived-mutations.ts +0 -170
  279. package/src/init-singleton-tables.ts +0 -24
  280. package/src/leader-thread/apply-mutation.ts +0 -187
  281. package/src/leader-thread/mutationlog.ts +0 -49
  282. package/src/leader-thread/pull-queue-set.ts +0 -67
  283. package/src/mutation.ts +0 -108
  284. package/src/query-info.ts +0 -83
  285. package/src/schema/MutationEvent.ts +0 -224
  286. package/src/schema/mutations.ts +0 -193
  287. package/src/sync/next/test/mutation-fixtures.ts +0 -228
@@ -5,14 +5,13 @@ import type { BootStatus, MakeSqliteDb, MigrationsReport, SqliteError } from '..
5
5
  import { UnexpectedError } from '../adapter-types.js'
6
6
  import type * as Devtools from '../devtools/mod.js'
7
7
  import type { LiveStoreSchema } from '../schema/mod.js'
8
- import { EventId, MutationEvent, mutationLogMetaTable, SYNC_STATUS_TABLE, syncStatusTable } from '../schema/mod.js'
9
- import { migrateTable } from '../schema-management/migrations.js'
8
+ import { LiveStoreEvent } from '../schema/mod.js'
10
9
  import type { InvalidPullError, IsOfflineError, SyncOptions } from '../sync/sync.js'
11
10
  import { sql } from '../util.js'
12
- import { execSql } from './connection.js'
11
+ import { makeApplyEvent } from './apply-event.js'
12
+ import * as Eventlog from './eventlog.js'
13
13
  import { bootDevtools } from './leader-worker-devtools.js'
14
14
  import { makeLeaderSyncProcessor } from './LeaderSyncProcessor.js'
15
- import { makePullQueueSet } from './pull-queue-set.js'
16
15
  import { recreateDb } from './recreate-db.js'
17
16
  import type { ShutdownChannel } from './shutdown-channel.js'
18
17
  import type {
@@ -24,6 +23,30 @@ import type {
24
23
  } from './types.js'
25
24
  import { LeaderThreadCtx } from './types.js'
26
25
 
26
+ export interface MakeLeaderThreadLayerParams {
27
+ storeId: string
28
+ syncPayload: Schema.JsonValue | undefined
29
+ clientId: string
30
+ schema: LiveStoreSchema
31
+ makeSqliteDb: MakeSqliteDb
32
+ syncOptions: SyncOptions | undefined
33
+ dbReadModel: LeaderSqliteDb
34
+ dbEventlog: LeaderSqliteDb
35
+ devtoolsOptions: DevtoolsOptions
36
+ shutdownChannel: ShutdownChannel
37
+ params?: {
38
+ localPushBatchSize?: number
39
+ backendPushBatchSize?: number
40
+ }
41
+ testing?: {
42
+ syncProcessor?: {
43
+ delays?: {
44
+ localPushProcessing?: Effect.Effect<void>
45
+ }
46
+ }
47
+ }
48
+ }
49
+
27
50
  export const makeLeaderThreadLayer = ({
28
51
  schema,
29
52
  storeId,
@@ -32,27 +55,21 @@ export const makeLeaderThreadLayer = ({
32
55
  makeSqliteDb,
33
56
  syncOptions,
34
57
  dbReadModel,
35
- dbMutationLog,
58
+ dbEventlog,
36
59
  devtoolsOptions,
37
60
  shutdownChannel,
38
- }: {
39
- storeId: string
40
- syncPayload: Schema.JsonValue | undefined
41
- clientId: string
42
- schema: LiveStoreSchema
43
- makeSqliteDb: MakeSqliteDb
44
- syncOptions: SyncOptions | undefined
45
- dbReadModel: LeaderSqliteDb
46
- dbMutationLog: LeaderSqliteDb
47
- devtoolsOptions: DevtoolsOptions
48
- shutdownChannel: ShutdownChannel
49
- }): Layer.Layer<LeaderThreadCtx, UnexpectedError, Scope.Scope | HttpClient.HttpClient> =>
61
+ params,
62
+ testing,
63
+ }: MakeLeaderThreadLayerParams): Layer.Layer<LeaderThreadCtx, UnexpectedError, Scope.Scope | HttpClient.HttpClient> =>
50
64
  Effect.gen(function* () {
51
65
  const bootStatusQueue = yield* Queue.unbounded<BootStatus>().pipe(Effect.acquireRelease(Queue.shutdown))
52
66
 
53
67
  // TODO do more validation here than just checking the count of tables
54
68
  // Either happens on initial boot or if schema changes
55
- const dbMissing =
69
+ const dbEventlogMissing =
70
+ dbEventlog.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
71
+
72
+ const dbReadModelMissing =
56
73
  dbReadModel.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
57
74
 
58
75
  const syncBackend =
@@ -60,6 +77,11 @@ export const makeLeaderThreadLayer = ({
60
77
  ? undefined
61
78
  : yield* syncOptions.backend({ storeId, clientId, payload: syncPayload })
62
79
 
80
+ if (syncBackend !== undefined) {
81
+ // We're already connecting to the sync backend concurrently
82
+ yield* syncBackend.connect.pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
83
+ }
84
+
63
85
  const initialBlockingSyncContext = yield* makeInitialBlockingSyncContext({
64
86
  initialSyncOptions: syncOptions?.initialSyncOptions ?? { _tag: 'Skip' },
65
87
  bootStatusQueue,
@@ -67,11 +89,19 @@ export const makeLeaderThreadLayer = ({
67
89
 
68
90
  const syncProcessor = yield* makeLeaderSyncProcessor({
69
91
  schema,
70
- dbMissing,
71
- dbMutationLog,
92
+ dbEventlogMissing,
93
+ dbEventlog,
94
+ dbReadModel,
95
+ dbReadModelMissing,
72
96
  initialBlockingSyncContext,
73
- clientId,
74
97
  onError: syncOptions?.onSyncError ?? 'ignore',
98
+ params: {
99
+ localPushBatchSize: params?.localPushBatchSize,
100
+ backendPushBatchSize: params?.backendPushBatchSize,
101
+ },
102
+ testing: {
103
+ delays: testing?.syncProcessor?.delays,
104
+ },
75
105
  })
76
106
 
77
107
  const extraIncomingMessagesQueue = yield* Queue.unbounded<Devtools.Leader.MessageToApp>().pipe(
@@ -86,20 +116,22 @@ export const makeLeaderThreadLayer = ({
86
116
  }
87
117
  : { enabled: false as const }
88
118
 
119
+ const applyEvent = yield* makeApplyEvent({ schema, dbReadModel, dbEventlog })
120
+
89
121
  const ctx = {
90
122
  schema,
91
123
  bootStatusQueue,
92
124
  storeId,
93
125
  clientId,
94
126
  dbReadModel,
95
- dbMutationLog,
127
+ dbEventlog,
96
128
  makeSqliteDb,
97
- mutationEventSchema: MutationEvent.makeMutationEventSchema(schema),
129
+ eventSchema: LiveStoreEvent.makeEventDefSchema(schema),
98
130
  shutdownStateSubRef: yield* SubscriptionRef.make<ShutdownState>('running'),
99
131
  shutdownChannel,
100
132
  syncBackend,
101
133
  syncProcessor,
102
- connectedClientSessionPullQueues: yield* makePullQueueSet,
134
+ applyEvent,
103
135
  extraIncomingMessagesQueue,
104
136
  devtools: devtoolsContext,
105
137
  // State will be set during `bootLeaderThread`
@@ -112,7 +144,7 @@ export const makeLeaderThreadLayer = ({
112
144
  const layer = Layer.succeed(LeaderThreadCtx, ctx)
113
145
 
114
146
  ctx.initialState = yield* bootLeaderThread({
115
- dbMissing,
147
+ dbReadModelMissing,
116
148
  initialBlockingSyncContext,
117
149
  devtoolsOptions,
118
150
  }).pipe(Effect.provide(layer))
@@ -122,6 +154,7 @@ export const makeLeaderThreadLayer = ({
122
154
  Effect.withSpan('@livestore/common:leader-thread:boot'),
123
155
  Effect.withSpanScoped('@livestore/common:leader-thread'),
124
156
  UnexpectedError.mapToUnexpectedError,
157
+ Effect.tapCauseLogPretty,
125
158
  Layer.unwrapScoped,
126
159
  )
127
160
 
@@ -135,7 +168,7 @@ const makeInitialBlockingSyncContext = ({
135
168
  Effect.gen(function* () {
136
169
  const ctx = {
137
170
  isDone: false,
138
- processedMutations: 0,
171
+ processedEvents: 0,
139
172
  total: -1,
140
173
  }
141
174
 
@@ -158,10 +191,10 @@ const makeInitialBlockingSyncContext = ({
158
191
  ctx.total = remaining + processed
159
192
  }
160
193
 
161
- ctx.processedMutations += processed
194
+ ctx.processedEvents += processed
162
195
  yield* Queue.offer(bootStatusQueue, {
163
196
  stage: 'syncing',
164
- progress: { done: ctx.processedMutations, total: ctx.total },
197
+ progress: { done: ctx.processedEvents, total: ctx.total },
165
198
  })
166
199
 
167
200
  if (remaining === 0 && blockingDeferred !== undefined) {
@@ -177,11 +210,11 @@ const makeInitialBlockingSyncContext = ({
177
210
  * It also starts various background processes (e.g. syncing)
178
211
  */
179
212
  const bootLeaderThread = ({
180
- dbMissing,
213
+ dbReadModelMissing,
181
214
  initialBlockingSyncContext,
182
215
  devtoolsOptions,
183
216
  }: {
184
- dbMissing: boolean
217
+ dbReadModelMissing: boolean
185
218
  initialBlockingSyncContext: InitialBlockingSyncContext
186
219
  devtoolsOptions: DevtoolsOptions
187
220
  }): Effect.Effect<
@@ -190,46 +223,20 @@ const bootLeaderThread = ({
190
223
  LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
191
224
  > =>
192
225
  Effect.gen(function* () {
193
- const { dbMutationLog, bootStatusQueue, syncProcessor } = yield* LeaderThreadCtx
194
-
195
- yield* migrateTable({
196
- db: dbMutationLog,
197
- behaviour: 'create-if-not-exists',
198
- tableAst: mutationLogMetaTable.sqliteDef.ast,
199
- skipMetaTable: true,
200
- })
201
-
202
- yield* migrateTable({
203
- db: dbMutationLog,
204
- behaviour: 'create-if-not-exists',
205
- tableAst: syncStatusTable.sqliteDef.ast,
206
- skipMetaTable: true,
207
- })
208
-
209
- // Create sync status row if it doesn't exist
210
- yield* execSql(
211
- dbMutationLog,
212
- sql`INSERT INTO ${SYNC_STATUS_TABLE} (head)
213
- SELECT ${EventId.ROOT.global}
214
- WHERE NOT EXISTS (SELECT 1 FROM ${SYNC_STATUS_TABLE})`,
215
- {},
216
- )
217
-
218
- const dbReady = yield* Deferred.make<void>()
226
+ const { dbEventlog, bootStatusQueue, syncProcessor } = yield* LeaderThreadCtx
219
227
 
220
- // We're already starting pulling from the sync backend concurrently but wait until the db is ready before
221
- // processing any incoming mutations
222
- const { initialLeaderHead } = yield* syncProcessor.boot({ dbReady })
228
+ yield* Eventlog.initEventlogDb(dbEventlog)
223
229
 
224
230
  let migrationsReport: MigrationsReport
225
- if (dbMissing) {
231
+ if (dbReadModelMissing) {
226
232
  const recreateResult = yield* recreateDb
227
233
  migrationsReport = recreateResult.migrationsReport
228
234
  } else {
229
235
  migrationsReport = { migrations: [] }
230
236
  }
231
237
 
232
- yield* Deferred.succeed(dbReady, void 0)
238
+ // NOTE the sync processor depends on the dbs being initialized properly
239
+ const { initialLeaderHead } = yield* syncProcessor.boot
233
240
 
234
241
  if (initialBlockingSyncContext.blockingDeferred !== undefined) {
235
242
  // Provides a syncing status right away before the first pull response comes in
@@ -3,4 +3,4 @@ export * from './types.js'
3
3
  export * as ShutdownChannel from './shutdown-channel.js'
4
4
  export * from './leader-worker-devtools.js'
5
5
  export * from './make-leader-thread-layer.js'
6
- export * from './mutationlog.js'
6
+ export * as Eventlog from './eventlog.js'
@@ -3,7 +3,7 @@ import type { HttpClient } from '@livestore/utils/effect'
3
3
  import { Effect, Queue } from '@livestore/utils/effect'
4
4
 
5
5
  import type { InvalidPullError, IsOfflineError, MigrationHooks, MigrationsReport, SqliteError } from '../index.js'
6
- import { initializeSingletonTables, migrateDb, rehydrateFromMutationLog, UnexpectedError } from '../index.js'
6
+ import { migrateDb, rehydrateFromEventlog, UnexpectedError } from '../index.js'
7
7
  import { configureConnection } from './connection.js'
8
8
  import { LeaderThreadCtx } from './types.js'
9
9
 
@@ -12,7 +12,7 @@ export const recreateDb: Effect.Effect<
12
12
  UnexpectedError | SqliteError | IsOfflineError | InvalidPullError,
13
13
  LeaderThreadCtx | HttpClient.HttpClient
14
14
  > = Effect.gen(function* () {
15
- const { dbReadModel, dbMutationLog, schema, bootStatusQueue } = yield* LeaderThreadCtx
15
+ const { dbReadModel, dbEventlog, schema, bootStatusQueue, applyEvent } = yield* LeaderThreadCtx
16
16
 
17
17
  const migrationOptions = schema.migrationOptions
18
18
  let migrationsReport: MigrationsReport
@@ -41,25 +41,24 @@ export const recreateDb: Effect.Effect<
41
41
  Queue.offer(bootStatusQueue, { stage: 'migrating', progress: { done, total } }),
42
42
  })
43
43
 
44
- initializeSingletonTables(schema, tmpDb)
45
-
46
44
  yield* Effect.tryAll(() => hooks?.pre?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
47
45
 
48
46
  return { migrationsReport, tmpDb }
49
47
  })
50
48
 
51
49
  switch (migrationOptions.strategy) {
52
- case 'from-mutation-log': {
50
+ case 'from-eventlog': {
53
51
  const hooks = migrationOptions.hooks
54
52
  const initResult = yield* initDb(hooks)
55
53
 
56
54
  migrationsReport = initResult.migrationsReport
57
55
 
58
- yield* rehydrateFromMutationLog({
59
- db: initResult.tmpDb,
60
- logDb: dbMutationLog,
56
+ yield* rehydrateFromEventlog({
57
+ // db: initResult.tmpDb,
58
+ dbEventlog,
61
59
  schema,
62
60
  migrationOptions,
61
+ applyEvent,
63
62
  onProgress: ({ done, total }) =>
64
63
  Queue.offer(bootStatusQueue, { stage: 'rehydrating', progress: { done, total } }),
65
64
  })
@@ -5,12 +5,14 @@ import type {
5
5
  Option,
6
6
  Queue,
7
7
  Scope,
8
+ Stream,
8
9
  Subscribable,
9
10
  SubscriptionRef,
10
11
  WebChannel,
11
12
  } from '@livestore/utils/effect'
12
13
  import { Context, Schema } from '@livestore/utils/effect'
13
14
 
15
+ import type { LeaderPullCursor, SqliteError } from '../adapter-types.js'
14
16
  import type {
15
17
  BootStatus,
16
18
  Devtools,
@@ -22,7 +24,7 @@ import type {
22
24
  SyncBackend,
23
25
  UnexpectedError,
24
26
  } from '../index.js'
25
- import type { EventId, LiveStoreSchema, MutationEvent } from '../schema/mod.js'
27
+ import type { EventId, LiveStoreEvent, LiveStoreSchema } from '../schema/mod.js'
26
28
  import type * as SyncState from '../sync/syncstate.js'
27
29
  import type { ShutdownChannel } from './shutdown-channel.js'
28
30
 
@@ -50,7 +52,7 @@ export type InitialSyncInfo = Option.Option<{
50
52
  // | { _tag: 'Reuse'; syncInfo: InitialSyncInfo }
51
53
 
52
54
  export type LeaderSqliteDb = SqliteDb<{ dbPointer: number; persistenceInfo: PersistenceInfo }>
53
- export type PersistenceInfoPair = { readModel: PersistenceInfo; mutationLog: PersistenceInfo }
55
+ export type PersistenceInfoPair = { readModel: PersistenceInfo; eventlog: PersistenceInfo }
54
56
 
55
57
  export type DevtoolsOptions =
56
58
  | {
@@ -88,16 +90,16 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
88
90
  clientId: string
89
91
  makeSqliteDb: MakeSqliteDb
90
92
  dbReadModel: LeaderSqliteDb
91
- dbMutationLog: LeaderSqliteDb
93
+ dbEventlog: LeaderSqliteDb
92
94
  bootStatusQueue: Queue.Queue<BootStatus>
93
95
  // TODO we should find a more elegant way to handle cases which need this ref for their implementation
94
96
  shutdownStateSubRef: SubscriptionRef.SubscriptionRef<ShutdownState>
95
97
  shutdownChannel: ShutdownChannel
96
- mutationEventSchema: MutationEvent.ForMutationDefRecord<any>
98
+ eventSchema: LiveStoreEvent.ForEventDefRecord<any>
97
99
  devtools: DevtoolsContext
98
100
  syncBackend: SyncBackend | undefined
99
101
  syncProcessor: LeaderSyncProcessor
100
- connectedClientSessionPullQueues: PullQueueSet
102
+ applyEvent: ApplyEvent
101
103
  initialState: {
102
104
  leaderHead: EventId.EventId
103
105
  migrationsReport: MigrationsReport
@@ -111,27 +113,41 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
111
113
  }
112
114
  >() {}
113
115
 
116
+ export type ApplyEvent = (
117
+ eventEncoded: LiveStoreEvent.EncodedWithMeta,
118
+ options?: {
119
+ /** Needed for rehydrateFromEventlog */
120
+ skipEventlog?: boolean
121
+ },
122
+ ) => Effect.Effect<
123
+ { sessionChangeset: { _tag: 'sessionChangeset'; data: Uint8Array; debug: any } | { _tag: 'no-op' } },
124
+ SqliteError | UnexpectedError
125
+ >
126
+
114
127
  export type InitialBlockingSyncContext = {
115
128
  blockingDeferred: Deferred.Deferred<void> | undefined
116
129
  update: (_: { remaining: number; processed: number }) => Effect.Effect<void>
117
130
  }
118
131
 
119
- export type PullQueueItem = {
120
- payload: SyncState.PayloadUpstream
121
- remaining: number
122
- }
123
-
124
132
  export interface LeaderSyncProcessor {
133
+ /** Used by client sessions to subscribe to upstream sync state changes */
134
+ pull: (args: {
135
+ cursor: LeaderPullCursor
136
+ }) => Stream.Stream<{ payload: typeof SyncState.PayloadUpstream.Type; mergeCounter: number }, UnexpectedError>
137
+ /** The `pullQueue` API can be used instead of `pull` when more convenient */
138
+ pullQueue: (args: {
139
+ cursor: LeaderPullCursor
140
+ }) => Effect.Effect<
141
+ Queue.Queue<{ payload: typeof SyncState.PayloadUpstream.Type; mergeCounter: number }>,
142
+ UnexpectedError,
143
+ Scope.Scope
144
+ >
145
+
146
+ /** Used by client sessions to push events to the leader thread */
125
147
  push: (
126
148
  /** `batch` needs to follow the same rules as `batch` in `SyncBackend.push` */
127
- batch: ReadonlyArray<MutationEvent.EncodedWithMeta>,
149
+ batch: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>,
128
150
  options?: {
129
- /**
130
- * This generation number is used to automatically reject subsequent pushes
131
- * of a previously rejected push from a client session. This might occur in
132
- * certain concurrent scenarios.
133
- */
134
- // generation: number
135
151
  /**
136
152
  * If true, the effect will only finish when the local push has been processed (i.e. succeeded or was rejected).
137
153
  * @default false
@@ -140,24 +156,18 @@ export interface LeaderSyncProcessor {
140
156
  },
141
157
  ) => Effect.Effect<void, LeaderAheadError>
142
158
 
159
+ /** Currently only used by devtools which don't provide their own event numbers */
143
160
  pushPartial: (args: {
144
- mutationEvent: MutationEvent.PartialAnyEncoded
161
+ event: LiveStoreEvent.PartialAnyEncoded
145
162
  clientId: string
146
163
  sessionId: string
147
- }) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx>
148
- boot: (args: {
149
- dbReady: Deferred.Deferred<void>
150
- }) => Effect.Effect<
164
+ }) => Effect.Effect<void, UnexpectedError>
165
+
166
+ boot: Effect.Effect<
151
167
  { initialLeaderHead: EventId.EventId },
152
168
  UnexpectedError,
153
169
  LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
154
170
  >
155
171
  syncState: Subscribable.Subscribable<SyncState.SyncState>
156
- }
157
-
158
- export interface PullQueueSet {
159
- makeQueue: (
160
- since: EventId.EventId,
161
- ) => Effect.Effect<Queue.Queue<PullQueueItem>, UnexpectedError, Scope.Scope | LeaderThreadCtx>
162
- offer: (item: PullQueueItem) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx>
172
+ getMergeCounter: () => number
163
173
  }
@@ -0,0 +1,110 @@
1
+ import { isReadonlyArray } from '@livestore/utils'
2
+ import { Schema } from '@livestore/utils/effect'
3
+
4
+ import { SessionIdSymbol } from './adapter-types.js'
5
+ import { isQueryBuilder } from './query-builder/api.js'
6
+ import type { EventDef, Materializer, MaterializerResult } from './schema/EventDef.js'
7
+ import type * as LiveStoreEvent from './schema/LiveStoreEvent.js'
8
+ import type { BindValues } from './sql-queries/sql-queries.js'
9
+ import type { PreparedBindValues } from './util.js'
10
+ import { prepareBindValues } from './util.js'
11
+
12
+ export const getExecArgsFromEvent = ({
13
+ eventDef: { eventDef, materializer },
14
+ event,
15
+ }: {
16
+ eventDef: {
17
+ eventDef: EventDef.AnyWithoutFn
18
+ materializer: Materializer
19
+ }
20
+ /** Both encoded and decoded events are supported to reduce the number of times we need to decode/encode */
21
+ event:
22
+ | {
23
+ decoded: LiveStoreEvent.AnyDecoded | LiveStoreEvent.PartialAnyDecoded
24
+ encoded: undefined
25
+ }
26
+ | {
27
+ decoded: undefined
28
+ encoded: LiveStoreEvent.AnyEncoded | LiveStoreEvent.PartialAnyEncoded
29
+ }
30
+ }): ReadonlyArray<{
31
+ statementSql: string
32
+ bindValues: PreparedBindValues
33
+ writeTables: ReadonlySet<string> | undefined
34
+ }> => {
35
+ const eventArgsDecoded = event.decoded?.args ?? Schema.decodeUnknownSync(eventDef.schema)(event.encoded!.args)
36
+
37
+ const res = materializer(eventArgsDecoded, {
38
+ clientOnly: eventDef.options.clientOnly,
39
+ // TODO properly implement this
40
+ currentFacts: new Map(),
41
+ })
42
+
43
+ const statementRes = mapMaterializerResult(res)
44
+
45
+ return statementRes.map((statementRes) => {
46
+ const statementSql = statementRes.sql
47
+
48
+ const eventArgsEncoded = event.encoded?.args ?? Schema.encodeUnknownSync(eventDef.schema)(event.decoded!.args)
49
+ const bindValues = typeof statementRes === 'string' ? eventArgsEncoded : statementRes.bindValues
50
+
51
+ const writeTables = typeof statementRes === 'string' ? undefined : statementRes.writeTables
52
+
53
+ return { statementSql, bindValues: prepareBindValues(bindValues ?? {}, statementSql), writeTables }
54
+ })
55
+ }
56
+
57
+ const mapMaterializerResult = (
58
+ materializerResult: MaterializerResult | ReadonlyArray<MaterializerResult>,
59
+ ): ReadonlyArray<{
60
+ sql: string
61
+ bindValues: BindValues
62
+ writeTables: ReadonlySet<string> | undefined
63
+ }> => {
64
+ if (isReadonlyArray(materializerResult)) {
65
+ return materializerResult.flatMap(mapMaterializerResult)
66
+ }
67
+ if (isQueryBuilder(materializerResult)) {
68
+ const { query, bindValues } = materializerResult.asSql()
69
+ return [{ sql: query, bindValues: bindValues as BindValues, writeTables: undefined }]
70
+ } else if (typeof materializerResult === 'string') {
71
+ return [{ sql: materializerResult, bindValues: {} as BindValues, writeTables: undefined }]
72
+ } else {
73
+ return [
74
+ {
75
+ sql: materializerResult.sql,
76
+ bindValues: materializerResult.bindValues,
77
+ writeTables: materializerResult.writeTables,
78
+ },
79
+ ]
80
+ }
81
+ }
82
+
83
+ // NOTE we should explore whether there is a more elegant solution
84
+ // e.g. by leveraging the schema to replace the sessionIdSymbol
85
+ export const replaceSessionIdSymbol = (
86
+ bindValues: Record<string, unknown> | ReadonlyArray<unknown>,
87
+ sessionId: string,
88
+ ) => {
89
+ deepReplaceValue(bindValues, SessionIdSymbol, sessionId)
90
+ }
91
+
92
+ const deepReplaceValue = <S, R>(input: any, searchValue: S, replaceValue: R): void => {
93
+ if (Array.isArray(input)) {
94
+ for (const i in input) {
95
+ if (input[i] === searchValue) {
96
+ input[i] = replaceValue
97
+ } else {
98
+ deepReplaceValue(input[i], searchValue, replaceValue)
99
+ }
100
+ }
101
+ } else if (typeof input === 'object' && input !== null) {
102
+ for (const key in input) {
103
+ if (input[key] === searchValue) {
104
+ input[key] = replaceValue
105
+ } else {
106
+ deepReplaceValue(input[key], searchValue, replaceValue)
107
+ }
108
+ }
109
+ }
110
+ }