@livestore/common 0.3.0-dev.11 → 0.3.0-dev.2

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 (181) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-types.d.ts +35 -47
  3. package/dist/adapter-types.d.ts.map +1 -1
  4. package/dist/adapter-types.js.map +1 -1
  5. package/dist/derived-mutations.d.ts +4 -4
  6. package/dist/derived-mutations.d.ts.map +1 -1
  7. package/dist/derived-mutations.test.js.map +1 -1
  8. package/dist/devtools/devtools-bridge.d.ts +1 -2
  9. package/dist/devtools/devtools-bridge.d.ts.map +1 -1
  10. package/dist/devtools/devtools-messages.d.ts +592 -3
  11. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  12. package/dist/devtools/devtools-messages.js +171 -3
  13. package/dist/devtools/devtools-messages.js.map +1 -1
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/init-singleton-tables.d.ts +2 -2
  17. package/dist/init-singleton-tables.d.ts.map +1 -1
  18. package/dist/init-singleton-tables.js.map +1 -1
  19. package/dist/leader-thread/apply-mutation.d.ts +2 -5
  20. package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
  21. package/dist/leader-thread/apply-mutation.js +29 -41
  22. package/dist/leader-thread/apply-mutation.js.map +1 -1
  23. package/dist/leader-thread/connection.d.ts +4 -4
  24. package/dist/leader-thread/connection.d.ts.map +1 -1
  25. package/dist/leader-thread/connection.js +5 -5
  26. package/dist/leader-thread/connection.js.map +1 -1
  27. package/dist/leader-thread/leader-sync-processor.d.ts +2 -2
  28. package/dist/leader-thread/leader-sync-processor.d.ts.map +1 -1
  29. package/dist/leader-thread/leader-sync-processor.js +12 -20
  30. package/dist/leader-thread/leader-sync-processor.js.map +1 -1
  31. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  32. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  33. package/dist/leader-thread/leader-worker-devtools.js +81 -37
  34. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  35. package/dist/leader-thread/make-leader-thread-layer.d.ts +11 -12
  36. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  37. package/dist/leader-thread/make-leader-thread-layer.js +14 -33
  38. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  39. package/dist/leader-thread/mutationlog.d.ts +19 -6
  40. package/dist/leader-thread/mutationlog.d.ts.map +1 -1
  41. package/dist/leader-thread/mutationlog.js +6 -7
  42. package/dist/leader-thread/mutationlog.js.map +1 -1
  43. package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
  44. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  45. package/dist/leader-thread/recreate-db.js +18 -24
  46. package/dist/leader-thread/recreate-db.js.map +1 -1
  47. package/dist/leader-thread/types.d.ts +16 -36
  48. package/dist/leader-thread/types.d.ts.map +1 -1
  49. package/dist/leader-thread/types.js.map +1 -1
  50. package/dist/mutation.d.ts +2 -9
  51. package/dist/mutation.d.ts.map +1 -1
  52. package/dist/mutation.js +5 -5
  53. package/dist/mutation.js.map +1 -1
  54. package/dist/query-builder/impl.d.ts +1 -1
  55. package/dist/rehydrate-from-mutationlog.d.ts +5 -5
  56. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  57. package/dist/rehydrate-from-mutationlog.js +19 -13
  58. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  59. package/dist/schema/EventId.d.ts +14 -16
  60. package/dist/schema/EventId.d.ts.map +1 -1
  61. package/dist/schema/EventId.js +7 -15
  62. package/dist/schema/EventId.js.map +1 -1
  63. package/dist/schema/MutationEvent.d.ts +80 -49
  64. package/dist/schema/MutationEvent.d.ts.map +1 -1
  65. package/dist/schema/MutationEvent.js +15 -32
  66. package/dist/schema/MutationEvent.js.map +1 -1
  67. package/dist/schema/system-tables.d.ts +26 -26
  68. package/dist/schema/system-tables.d.ts.map +1 -1
  69. package/dist/schema/system-tables.js +11 -19
  70. package/dist/schema/system-tables.js.map +1 -1
  71. package/dist/schema-management/common.d.ts +3 -3
  72. package/dist/schema-management/common.d.ts.map +1 -1
  73. package/dist/schema-management/common.js.map +1 -1
  74. package/dist/schema-management/migrations.d.ts +4 -4
  75. package/dist/schema-management/migrations.d.ts.map +1 -1
  76. package/dist/schema-management/migrations.js +6 -6
  77. package/dist/schema-management/migrations.js.map +1 -1
  78. package/dist/sync/client-session-sync-processor.d.ts +4 -4
  79. package/dist/sync/client-session-sync-processor.d.ts.map +1 -1
  80. package/dist/sync/index.d.ts +1 -1
  81. package/dist/sync/index.d.ts.map +1 -1
  82. package/dist/sync/index.js +1 -1
  83. package/dist/sync/index.js.map +1 -1
  84. package/dist/sync/next/history-dag-common.d.ts +4 -1
  85. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  86. package/dist/sync/next/history-dag-common.js +1 -1
  87. package/dist/sync/next/history-dag-common.js.map +1 -1
  88. package/dist/sync/next/rebase-events.d.ts +3 -3
  89. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  90. package/dist/sync/next/rebase-events.js +2 -3
  91. package/dist/sync/next/rebase-events.js.map +1 -1
  92. package/dist/sync/next/test/mutation-fixtures.d.ts +7 -7
  93. package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -1
  94. package/dist/sync/next/test/mutation-fixtures.js +9 -3
  95. package/dist/sync/next/test/mutation-fixtures.js.map +1 -1
  96. package/dist/sync/sync.d.ts +11 -21
  97. package/dist/sync/sync.d.ts.map +1 -1
  98. package/dist/sync/sync.js.map +1 -1
  99. package/dist/sync/syncstate.d.ts +23 -45
  100. package/dist/sync/syncstate.d.ts.map +1 -1
  101. package/dist/sync/syncstate.js +12 -56
  102. package/dist/sync/syncstate.js.map +1 -1
  103. package/dist/sync/syncstate.test.js +69 -125
  104. package/dist/sync/syncstate.test.js.map +1 -1
  105. package/dist/sync/validate-push-payload.d.ts +2 -2
  106. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  107. package/dist/sync/validate-push-payload.js +2 -2
  108. package/dist/sync/validate-push-payload.js.map +1 -1
  109. package/dist/version.d.ts +1 -1
  110. package/dist/version.d.ts.map +1 -1
  111. package/dist/version.js +1 -1
  112. package/dist/version.js.map +1 -1
  113. package/package.json +5 -6
  114. package/src/adapter-types.ts +40 -39
  115. package/src/derived-mutations.test.ts +1 -1
  116. package/src/derived-mutations.ts +5 -9
  117. package/src/devtools/devtools-bridge.ts +1 -2
  118. package/src/devtools/devtools-messages.ts +243 -3
  119. package/src/index.ts +6 -0
  120. package/src/init-singleton-tables.ts +2 -2
  121. package/src/leader-thread/apply-mutation.ts +35 -53
  122. package/src/leader-thread/connection.ts +7 -7
  123. package/src/leader-thread/{LeaderSyncProcessor.ts → leader-sync-processor.ts} +268 -306
  124. package/src/leader-thread/leader-worker-devtools.ts +124 -52
  125. package/src/leader-thread/make-leader-thread-layer.ts +30 -62
  126. package/src/leader-thread/mutationlog.ts +10 -14
  127. package/src/leader-thread/recreate-db.ts +20 -24
  128. package/src/leader-thread/types.ts +20 -41
  129. package/src/mutation.ts +7 -17
  130. package/src/rehydrate-from-mutationlog.ts +26 -18
  131. package/src/schema/EventId.ts +9 -23
  132. package/src/schema/MutationEvent.ts +24 -46
  133. package/src/schema/system-tables.ts +11 -19
  134. package/src/schema-management/common.ts +3 -3
  135. package/src/schema-management/migrations.ts +10 -10
  136. package/src/sync/{ClientSessionSyncProcessor.ts → client-session-sync-processor.ts} +19 -26
  137. package/src/sync/index.ts +1 -1
  138. package/src/sync/next/history-dag-common.ts +1 -1
  139. package/src/sync/next/rebase-events.ts +7 -7
  140. package/src/sync/next/test/mutation-fixtures.ts +10 -3
  141. package/src/sync/sync.ts +6 -19
  142. package/src/sync/syncstate.test.ts +67 -127
  143. package/src/sync/syncstate.ts +19 -21
  144. package/src/sync/validate-push-payload.ts +4 -7
  145. package/src/version.ts +1 -1
  146. package/dist/devtools/devtool-message-leader.d.ts +0 -2
  147. package/dist/devtools/devtool-message-leader.d.ts.map +0 -1
  148. package/dist/devtools/devtool-message-leader.js +0 -2
  149. package/dist/devtools/devtool-message-leader.js.map +0 -1
  150. package/dist/devtools/devtools-messages-client-session.d.ts +0 -297
  151. package/dist/devtools/devtools-messages-client-session.d.ts.map +0 -1
  152. package/dist/devtools/devtools-messages-client-session.js +0 -61
  153. package/dist/devtools/devtools-messages-client-session.js.map +0 -1
  154. package/dist/devtools/devtools-messages-common.d.ts +0 -65
  155. package/dist/devtools/devtools-messages-common.d.ts.map +0 -1
  156. package/dist/devtools/devtools-messages-common.js +0 -35
  157. package/dist/devtools/devtools-messages-common.js.map +0 -1
  158. package/dist/devtools/devtools-messages-leader.d.ts +0 -261
  159. package/dist/devtools/devtools-messages-leader.d.ts.map +0 -1
  160. package/dist/devtools/devtools-messages-leader.js +0 -85
  161. package/dist/devtools/devtools-messages-leader.js.map +0 -1
  162. package/dist/leader-thread/LeaderSyncProcessor.d.ts +0 -37
  163. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +0 -1
  164. package/dist/leader-thread/LeaderSyncProcessor.js +0 -432
  165. package/dist/leader-thread/LeaderSyncProcessor.js.map +0 -1
  166. package/dist/schema/EventId.test.d.ts +0 -2
  167. package/dist/schema/EventId.test.d.ts.map +0 -1
  168. package/dist/schema/EventId.test.js +0 -11
  169. package/dist/schema/EventId.test.js.map +0 -1
  170. package/dist/schema/MutationEvent.test.d.ts +0 -2
  171. package/dist/schema/MutationEvent.test.d.ts.map +0 -1
  172. package/dist/schema/MutationEvent.test.js +0 -2
  173. package/dist/schema/MutationEvent.test.js.map +0 -1
  174. package/dist/sync/ClientSessionSyncProcessor.d.ts +0 -43
  175. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +0 -1
  176. package/dist/sync/ClientSessionSyncProcessor.js +0 -141
  177. package/dist/sync/ClientSessionSyncProcessor.js.map +0 -1
  178. package/src/devtools/devtools-messages-client-session.ts +0 -109
  179. package/src/devtools/devtools-messages-common.ts +0 -52
  180. package/src/devtools/devtools-messages-leader.ts +0 -115
  181. package/src/schema/EventId.test.ts +0 -12
@@ -1,11 +1,18 @@
1
- import { Effect, FiberMap, Option, Stream, SubscriptionRef } from '@livestore/utils/effect'
1
+ import { Effect, FiberMap, Option, PubSub, Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
2
2
 
3
3
  import { Devtools, IntentionalShutdownCause, liveStoreVersion, UnexpectedError } from '../index.js'
4
4
  import { MUTATION_LOG_META_TABLE, SCHEMA_META_TABLE, SCHEMA_MUTATIONS_META_TABLE } from '../schema/mod.js'
5
+ import type { ShutdownChannel } from './shutdown-channel.js'
5
6
  import type { DevtoolsOptions, PersistenceInfoPair } from './types.js'
6
7
  import { LeaderThreadCtx } from './types.js'
7
8
 
8
- type SendMessageToDevtools = (message: Devtools.MessageFromAppLeader) => Effect.Effect<void>
9
+ type SendMessageToDevtools = (
10
+ message: Devtools.MessageFromAppLeader,
11
+ options?: {
12
+ /** Send message even if not connected (e.g. for initial broadcast messages) */
13
+ force: boolean
14
+ },
15
+ ) => Effect.Effect<void>
9
16
 
10
17
  // TODO bind scope to the webchannel lifetime
11
18
  export const bootDevtools = (options: DevtoolsOptions) =>
@@ -14,24 +21,44 @@ export const bootDevtools = (options: DevtoolsOptions) =>
14
21
  return
15
22
  }
16
23
 
17
- const { connectedClientSessionPullQueues, syncProcessor, extraIncomingMessagesQueue } = yield* LeaderThreadCtx
24
+ const { persistenceInfo, shutdownChannel, devtoolsWebChannel } = yield* options.makeContext
18
25
 
19
- yield* listenToDevtools({
20
- incomingMessages: Stream.fromQueue(extraIncomingMessagesQueue),
21
- sendMessage: () => Effect.void,
22
- }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
26
+ const isConnected = yield* SubscriptionRef.make(true)
27
+
28
+ const incomingMessagesPubSub = yield* PubSub.unbounded<Devtools.MessageToAppLeader>().pipe(
29
+ Effect.acquireRelease(PubSub.shutdown),
30
+ )
23
31
 
24
- const { persistenceInfo, devtoolsWebChannel } = yield* options.makeBootContext
32
+ const incomingMessages = Stream.fromPubSub(incomingMessagesPubSub)
25
33
 
26
- const sendMessage: SendMessageToDevtools = (message) =>
27
- devtoolsWebChannel
28
- .send(message)
29
- .pipe(
30
- Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
31
- Effect.interruptible,
32
- Effect.ignoreLogged,
33
- )
34
+ const outgoingMessagesQueue = yield* Queue.unbounded<Devtools.MessageFromAppLeader>().pipe(
35
+ Effect.acquireRelease(Queue.shutdown),
36
+ )
34
37
 
38
+ const devtoolsCoordinatorChannel = devtoolsWebChannel
39
+ // coordinatorMessagePortOrChannel instanceof MessagePort
40
+ // ? yield* WebChannel.messagePortChannel({
41
+ // port: coordinatorMessagePortOrChannel,
42
+ // schema: { send: Devtools.MessageFromAppLeader, listen: Devtools.MessageToAppLeader },
43
+ // })
44
+ // : coordinatorMessagePortOrChannel
45
+
46
+ const sendMessage: SendMessageToDevtools = (message, options) =>
47
+ Effect.gen(function* () {
48
+ if (options?.force === true || (yield* isConnected)) {
49
+ yield* devtoolsCoordinatorChannel.send(message)
50
+ } else {
51
+ yield* Queue.offer(outgoingMessagesQueue, message)
52
+ }
53
+ }).pipe(
54
+ Effect.withSpan('@livestore/common:leader-thread:devtools:sendToDevtools'),
55
+ Effect.interruptible,
56
+ Effect.ignoreLogged,
57
+ )
58
+
59
+ // broadcastCallbacks.add((message) => sendMessage(message))
60
+
61
+ const { connectedClientSessionPullQueues, syncProcessor } = yield* LeaderThreadCtx
35
62
  const { localHead } = yield* syncProcessor.syncState
36
63
 
37
64
  // TODO close queue when devtools disconnects
@@ -42,8 +69,13 @@ export const bootDevtools = (options: DevtoolsOptions) =>
42
69
  Effect.gen(function* () {
43
70
  if (msg.payload._tag === 'upstream-advance') {
44
71
  for (const mutationEventEncoded of msg.payload.newEvents) {
45
- // TODO refactor with push semantics
46
- yield* sendMessage(Devtools.MutationBroadcast.make({ mutationEventEncoded, liveStoreVersion }))
72
+ yield* sendMessage(
73
+ Devtools.MutationBroadcast.make({
74
+ mutationEventEncoded,
75
+
76
+ liveStoreVersion,
77
+ }),
78
+ )
47
79
  }
48
80
  } else {
49
81
  yield* Effect.logWarning('TODO implement rebases in devtools')
@@ -54,32 +86,68 @@ export const bootDevtools = (options: DevtoolsOptions) =>
54
86
  Effect.forkScoped,
55
87
  )
56
88
 
89
+ yield* devtoolsCoordinatorChannel.listen.pipe(
90
+ Stream.flatten(),
91
+ // Stream.tapLogWithLabel('@livestore/common:leader-thread:devtools:onPortMessage'),
92
+ Stream.tap((msg) =>
93
+ Effect.gen(function* () {
94
+ // yield* Effect.logDebug(`[@livestore/common:leader-thread:devtools] message from port: ${msg._tag}`, msg)
95
+ // if (msg._tag === 'LSD.MessagePortForStoreRes') {
96
+ // yield* Deferred.succeed(storeMessagePortDeferred, msg.port)
97
+ // } else {
98
+ yield* PubSub.publish(incomingMessagesPubSub, msg)
99
+ // }
100
+ }),
101
+ ),
102
+ Stream.runDrain,
103
+ Effect.withSpan(`@livestore/common:leader-thread:devtools:onPortMessage`),
104
+ Effect.ignoreLogged,
105
+ Effect.forkScoped,
106
+ )
107
+
108
+ // yield* sendMessage(Devtools.AppHostReady.make({ appHostId, liveStoreVersion, isLeader }), { force: true })
109
+
110
+ // yield* sendMessage(Devtools.MessagePortForStoreReq.make({ appHostId, liveStoreVersion, requestId: nanoid() }), {
111
+ // force: true,
112
+ // })
113
+
57
114
  yield* listenToDevtools({
58
- incomingMessages: devtoolsWebChannel.listen.pipe(Stream.flatten(), Stream.orDie),
115
+ incomingMessages,
59
116
  sendMessage,
117
+ // isConnected,
118
+ // disconnect,
119
+ // storeId,
120
+ // appHostId,
121
+ // isLeader,
60
122
  persistenceInfo,
61
- }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
123
+ shutdownChannel,
124
+ })
62
125
  }).pipe(Effect.withSpan('@livestore/common:leader-thread:devtools:boot'))
63
126
 
64
127
  const listenToDevtools = ({
65
128
  incomingMessages,
66
129
  sendMessage,
130
+ // isConnected,
131
+ // disconnect,
132
+ // appHostId,
133
+ // storeId,
134
+ // isLeader,
67
135
  persistenceInfo,
136
+ shutdownChannel,
68
137
  }: {
69
138
  incomingMessages: Stream.Stream<Devtools.MessageToAppLeader>
70
139
  sendMessage: SendMessageToDevtools
71
- persistenceInfo?: PersistenceInfoPair
140
+ // isConnected: SubscriptionRef.SubscriptionRef<boolean>
141
+ // disconnect: Effect.Effect<void>
142
+ // appHostId: string
143
+ // storeId: string
144
+ // isLeader: boolean
145
+ persistenceInfo: PersistenceInfoPair
146
+ shutdownChannel: ShutdownChannel
72
147
  }) =>
73
148
  Effect.gen(function* () {
74
- const {
75
- syncBackend,
76
- makeSqliteDb,
77
- dbReadModel,
78
- dbMutationLog,
79
- shutdownStateSubRef,
80
- shutdownChannel,
81
- syncProcessor,
82
- } = yield* LeaderThreadCtx
149
+ const innerWorkerCtx = yield* LeaderThreadCtx
150
+ const { syncBackend, makeSyncDb, db, dbLog, shutdownStateSubRef, syncProcessor } = innerWorkerCtx
83
151
 
84
152
  type RequestId = string
85
153
  const subscriptionFiberMap = yield* FiberMap.make<RequestId>()
@@ -90,6 +158,15 @@ const listenToDevtools = ({
90
158
  // yield* Effect.logDebug('[@livestore/common:leader-thread:devtools] incomingMessage', decodedEvent)
91
159
 
92
160
  if (decodedEvent._tag === 'LSD.Disconnect') {
161
+ // yield* SubscriptionRef.set(isConnected, false)
162
+
163
+ // yield* disconnect
164
+
165
+ // TODO is there a better place for this?
166
+ // yield* sendMessage(Devtools.AppHostReady.make({ appHostId, liveStoreVersion, isLeader }), {
167
+ // force: true,
168
+ // })
169
+
93
170
  return
94
171
  }
95
172
 
@@ -102,7 +179,7 @@ const listenToDevtools = ({
102
179
  return
103
180
  }
104
181
  case 'LSD.Leader.SnapshotReq': {
105
- const snapshot = dbReadModel.export()
182
+ const snapshot = db.export()
106
183
 
107
184
  yield* sendMessage(Devtools.SnapshotRes.make({ snapshot, ...reqPayload }))
108
185
 
@@ -114,15 +191,15 @@ const listenToDevtools = ({
114
191
  let tableNames: Set<string>
115
192
 
116
193
  try {
117
- const tmpDb = yield* makeSqliteDb({ _tag: 'in-memory' })
118
- tmpDb.import(data)
119
- const tableNameResults = tmpDb.select<{ name: string }>(
194
+ const tmpSyncDb = yield* makeSyncDb({ _tag: 'in-memory' })
195
+ tmpSyncDb.import(data)
196
+ const tableNameResults = tmpSyncDb.select<{ name: string }>(
120
197
  `select name from sqlite_master where type = 'table'`,
121
198
  )
122
199
 
123
200
  tableNames = new Set(tableNameResults.map((_) => _.name))
124
201
 
125
- tmpDb.close()
202
+ tmpSyncDb.close()
126
203
  } catch (e) {
127
204
  yield* Effect.logError(`Error importing database file`, e)
128
205
  yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-file' }))
@@ -133,15 +210,15 @@ const listenToDevtools = ({
133
210
  if (tableNames.has(MUTATION_LOG_META_TABLE)) {
134
211
  yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
135
212
 
136
- dbMutationLog.import(data)
213
+ dbLog.import(data)
137
214
 
138
- dbReadModel.destroy()
215
+ db.destroy()
139
216
  } else if (tableNames.has(SCHEMA_META_TABLE) && tableNames.has(SCHEMA_MUTATIONS_META_TABLE)) {
140
217
  yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
141
218
 
142
- dbReadModel.import(data)
219
+ db.import(data)
143
220
 
144
- dbMutationLog.destroy()
221
+ dbLog.destroy()
145
222
  } else {
146
223
  yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-database' }))
147
224
  return
@@ -149,7 +226,7 @@ const listenToDevtools = ({
149
226
 
150
227
  yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'ok' }))
151
228
 
152
- yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' })) ?? Effect.void
229
+ yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' }))
153
230
 
154
231
  return
155
232
  }
@@ -158,31 +235,26 @@ const listenToDevtools = ({
158
235
 
159
236
  yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
160
237
 
161
- dbReadModel.destroy()
238
+ db.destroy()
162
239
 
163
240
  if (mode === 'all-data') {
164
- dbMutationLog.destroy()
241
+ dbLog.destroy()
165
242
  }
166
243
 
167
244
  yield* sendMessage(Devtools.ResetAllDataRes.make({ ...reqPayload }))
168
245
 
169
- yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-reset' })) ?? Effect.void
246
+ yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-reset' }))
170
247
 
171
248
  return
172
249
  }
173
250
  case 'LSD.Leader.DatabaseFileInfoReq': {
174
- if (persistenceInfo === undefined) {
175
- console.log('[@livestore/common:leader-thread:devtools] persistenceInfo is required for this request')
176
- return
177
- }
178
-
179
251
  const dbSizeQuery = `SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();`
180
- const dbFileSize = dbReadModel.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
181
- const mutationLogFileSize = dbMutationLog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
252
+ const dbFileSize = db.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
253
+ const mutationLogFileSize = dbLog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
182
254
 
183
255
  yield* sendMessage(
184
256
  Devtools.DatabaseFileInfoRes.make({
185
- readModel: { fileSize: dbFileSize, persistenceInfo: persistenceInfo.readModel },
257
+ db: { fileSize: dbFileSize, persistenceInfo: persistenceInfo.db },
186
258
  mutationLog: { fileSize: mutationLogFileSize, persistenceInfo: persistenceInfo.mutationLog },
187
259
  ...reqPayload,
188
260
  }),
@@ -191,7 +263,7 @@ const listenToDevtools = ({
191
263
  return
192
264
  }
193
265
  case 'LSD.Leader.MutationLogReq': {
194
- const mutationLog = dbMutationLog.export()
266
+ const mutationLog = dbLog.export()
195
267
 
196
268
  yield* sendMessage(Devtools.MutationLogRes.make({ mutationLog, ...reqPayload }))
197
269
 
@@ -1,100 +1,68 @@
1
- import type { HttpClient, Scope } from '@livestore/utils/effect'
2
- import { Deferred, Effect, Layer, Queue, SubscriptionRef } from '@livestore/utils/effect'
1
+ import type { HttpClient, Scope, WebChannel } from '@livestore/utils/effect'
2
+ import { Deferred, Effect, FiberSet, Layer, Queue, SubscriptionRef } from '@livestore/utils/effect'
3
3
 
4
- import type { BootStatus, MakeSqliteDb, SqliteError } from '../adapter-types.js'
4
+ import type { BootStatus, MakeSynchronousDatabase, SqliteError, SynchronousDatabase } from '../adapter-types.js'
5
5
  import { UnexpectedError } from '../adapter-types.js'
6
- import type * as Devtools from '../devtools/index.js'
7
6
  import type { LiveStoreSchema } from '../schema/mod.js'
8
7
  import { EventId, MutationEvent, mutationLogMetaTable, SYNC_STATUS_TABLE, syncStatusTable } from '../schema/mod.js'
9
8
  import { migrateTable } from '../schema-management/migrations.js'
10
- import type { InvalidPullError, IsOfflineError, SyncOptions } from '../sync/sync.js'
9
+ import type { InvalidPullError, IsOfflineError, SyncBackend } from '../sync/sync.js'
11
10
  import { sql } from '../util.js'
12
11
  import { execSql } from './connection.js'
12
+ import { makeLeaderSyncProcessor } from './leader-sync-processor.js'
13
13
  import { bootDevtools } from './leader-worker-devtools.js'
14
- import { makeLeaderSyncProcessor } from './LeaderSyncProcessor.js'
15
14
  import { makePullQueueSet } from './pull-queue-set.js'
16
15
  import { recreateDb } from './recreate-db.js'
17
- import type { ShutdownChannel } from './shutdown-channel.js'
18
- import type {
19
- DevtoolsOptions,
20
- InitialBlockingSyncContext,
21
- InitialSyncOptions,
22
- LeaderSqliteDb,
23
- ShutdownState,
24
- } from './types.js'
16
+ import type { DevtoolsOptions, InitialBlockingSyncContext, InitialSyncOptions, ShutdownState } from './types.js'
25
17
  import { LeaderThreadCtx } from './types.js'
26
18
 
27
19
  export const makeLeaderThreadLayer = ({
28
20
  schema,
29
21
  storeId,
30
- clientId,
31
- makeSqliteDb,
32
- syncOptions,
33
- dbReadModel,
34
- dbMutationLog,
22
+ originId,
23
+ makeSyncDb,
24
+ makeSyncBackend,
25
+ db,
26
+ dbLog,
35
27
  devtoolsOptions,
36
- shutdownChannel,
28
+ initialSyncOptions = { _tag: 'Skip' },
37
29
  }: {
38
30
  storeId: string
39
- clientId: string
31
+ originId: string
40
32
  schema: LiveStoreSchema
41
- makeSqliteDb: MakeSqliteDb
42
- syncOptions: SyncOptions | undefined
43
- dbReadModel: LeaderSqliteDb
44
- dbMutationLog: LeaderSqliteDb
33
+ makeSyncDb: MakeSynchronousDatabase
34
+ makeSyncBackend: Effect.Effect<SyncBackend, UnexpectedError, Scope.Scope> | undefined
35
+ db: SynchronousDatabase
36
+ dbLog: SynchronousDatabase
45
37
  devtoolsOptions: DevtoolsOptions
46
- shutdownChannel: ShutdownChannel
38
+ initialSyncOptions: InitialSyncOptions | undefined
47
39
  }): Layer.Layer<LeaderThreadCtx, UnexpectedError, Scope.Scope | HttpClient.HttpClient> =>
48
40
  Effect.gen(function* () {
49
41
  const bootStatusQueue = yield* Queue.unbounded<BootStatus>().pipe(Effect.acquireRelease(Queue.shutdown))
50
42
 
51
43
  // TODO do more validation here than just checking the count of tables
52
44
  // Either happens on initial boot or if schema changes
53
- const dbMissing =
54
- dbReadModel.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
45
+ const dbMissing = db.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
55
46
 
56
- const syncBackend = syncOptions === undefined ? undefined : yield* syncOptions.makeBackend({ storeId, clientId })
47
+ const syncBackend = makeSyncBackend === undefined ? undefined : yield* makeSyncBackend
57
48
 
58
- const initialBlockingSyncContext = yield* makeInitialBlockingSyncContext({
59
- initialSyncOptions: syncOptions?.initialSyncOptions ?? { _tag: 'Skip' },
60
- bootStatusQueue,
61
- })
62
-
63
- const syncProcessor = yield* makeLeaderSyncProcessor({
64
- schema,
65
- dbMissing,
66
- dbMutationLog,
67
- initialBlockingSyncContext,
68
- })
69
-
70
- const extraIncomingMessagesQueue = yield* Queue.unbounded<Devtools.MessageToAppLeader>().pipe(
71
- Effect.acquireRelease(Queue.shutdown),
72
- )
49
+ const initialBlockingSyncContext = yield* makeInitialBlockingSyncContext({ initialSyncOptions, bootStatusQueue })
73
50
 
74
- const devtoolsContext = devtoolsOptions.enabled
75
- ? {
76
- enabled: true as const,
77
- syncBackendPullLatch: yield* Effect.makeLatch(true),
78
- syncBackendPushLatch: yield* Effect.makeLatch(true),
79
- }
80
- : { enabled: false as const }
51
+ const syncProcessor = yield* makeLeaderSyncProcessor({ schema, dbMissing, dbLog, initialBlockingSyncContext })
81
52
 
82
53
  const ctx = {
83
54
  schema,
84
55
  bootStatusQueue,
85
56
  storeId,
86
- clientId,
87
- dbReadModel,
88
- dbMutationLog,
89
- makeSqliteDb,
57
+ originId,
58
+ db,
59
+ dbLog,
60
+ makeSyncDb,
90
61
  mutationEventSchema: MutationEvent.makeMutationEventSchema(schema),
91
62
  shutdownStateSubRef: yield* SubscriptionRef.make<ShutdownState>('running'),
92
- shutdownChannel,
93
63
  syncBackend,
94
64
  syncProcessor,
95
65
  connectedClientSessionPullQueues: yield* makePullQueueSet,
96
- extraIncomingMessagesQueue,
97
- devtools: devtoolsContext,
98
66
  } satisfies typeof LeaderThreadCtx.Service
99
67
 
100
68
  // @ts-expect-error For debugging purposes
@@ -177,17 +145,17 @@ const bootLeaderThread = ({
177
145
  LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
178
146
  > =>
179
147
  Effect.gen(function* () {
180
- const { dbMutationLog, bootStatusQueue, syncProcessor } = yield* LeaderThreadCtx
148
+ const { dbLog, bootStatusQueue, syncProcessor } = yield* LeaderThreadCtx
181
149
 
182
150
  yield* migrateTable({
183
- db: dbMutationLog,
151
+ db: dbLog,
184
152
  behaviour: 'create-if-not-exists',
185
153
  tableAst: mutationLogMetaTable.sqliteDef.ast,
186
154
  skipMetaTable: true,
187
155
  })
188
156
 
189
157
  yield* migrateTable({
190
- db: dbMutationLog,
158
+ db: dbLog,
191
159
  behaviour: 'create-if-not-exists',
192
160
  tableAst: syncStatusTable.sqliteDef.ast,
193
161
  skipMetaTable: true,
@@ -195,7 +163,7 @@ const bootLeaderThread = ({
195
163
 
196
164
  // Create sync status row if it doesn't exist
197
165
  yield* execSql(
198
- dbMutationLog,
166
+ dbLog,
199
167
  sql`INSERT INTO ${SYNC_STATUS_TABLE} (head)
200
168
  SELECT ${EventId.ROOT.global}
201
169
  WHERE NOT EXISTS (SELECT 1 FROM ${SYNC_STATUS_TABLE})`,
@@ -1,20 +1,17 @@
1
1
  import { Effect, Schema } from '@livestore/utils/effect'
2
2
 
3
- import type { SqliteDb } from '../adapter-types.js'
3
+ import type { SynchronousDatabase } from '../adapter-types.js'
4
4
  import * as EventId from '../schema/EventId.js'
5
- import type * as MutationEvent from '../schema/MutationEvent.js'
6
5
  import { MUTATION_LOG_META_TABLE, mutationLogMetaTable, SYNC_STATUS_TABLE } from '../schema/system-tables.js'
7
6
  import { prepareBindValues, sql } from '../util.js'
8
7
  import { LeaderThreadCtx } from './types.js'
9
8
 
10
- export const getMutationEventsSince = (
11
- since: EventId.EventId,
12
- ): Effect.Effect<ReadonlyArray<MutationEvent.AnyEncoded>, never, LeaderThreadCtx> =>
9
+ export const getMutationEventsSince = (since: EventId.EventId) =>
13
10
  Effect.gen(function* () {
14
- const { dbMutationLog } = yield* LeaderThreadCtx
11
+ const { dbLog } = yield* LeaderThreadCtx
15
12
 
16
13
  const query = mutationLogMetaTable.query.where('idGlobal', '>=', since.global).asSql()
17
- const pendingMutationEventsRaw = dbMutationLog.select(query.query, prepareBindValues(query.bindValues, query.query))
14
+ const pendingMutationEventsRaw = dbLog.select(query.query, prepareBindValues(query.bindValues, query.query))
18
15
  const pendingMutationEvents = Schema.decodeUnknownSync(mutationLogMetaTable.schema.pipe(Schema.Array))(
19
16
  pendingMutationEventsRaw,
20
17
  )
@@ -29,18 +26,17 @@ export const getMutationEventsSince = (
29
26
  .filter((_) => EventId.compare(_.id, since) > 0)
30
27
  })
31
28
 
32
- export const getLocalHeadFromDb = (dbMutationLog: SqliteDb): EventId.EventId => {
33
- const res = dbMutationLog.select<{ idGlobal: EventId.GlobalEventId; idLocal: EventId.LocalEventId }>(
29
+ export const getLocalHeadFromDb = (dbLog: SynchronousDatabase) => {
30
+ const res = dbLog.select<{ idGlobal: number; idLocal: number }>(
34
31
  sql`select idGlobal, idLocal from ${MUTATION_LOG_META_TABLE} order by idGlobal DESC, idLocal DESC limit 1`,
35
32
  )[0]
36
33
 
37
34
  return res ? { global: res.idGlobal, local: res.idLocal } : EventId.ROOT
38
35
  }
39
36
 
40
- export const getBackendHeadFromDb = (dbMutationLog: SqliteDb): EventId.GlobalEventId =>
41
- dbMutationLog.select<{ head: EventId.GlobalEventId }>(sql`select head from ${SYNC_STATUS_TABLE}`)[0]?.head ??
42
- EventId.ROOT.global
37
+ export const getBackendHeadFromDb = (dbLog: SynchronousDatabase) =>
38
+ dbLog.select<{ head: number }>(sql`select head from ${SYNC_STATUS_TABLE}`)[0]?.head ?? EventId.ROOT.global
43
39
 
44
40
  // TODO use prepared statements
45
- export const updateBackendHead = (dbMutationLog: SqliteDb, head: EventId.EventId) =>
46
- dbMutationLog.execute(sql`UPDATE ${SYNC_STATUS_TABLE} SET head = ${head.global}`)
41
+ export const updateBackendHead = (dbLog: SynchronousDatabase, head: EventId.EventId) =>
42
+ dbLog.execute(sql`UPDATE ${SYNC_STATUS_TABLE} SET head = ${head.global}`)
@@ -12,56 +12,54 @@ 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 { db, dbLog, makeSyncDb, schema, bootStatusQueue } = yield* LeaderThreadCtx
16
16
 
17
17
  const migrationOptions = schema.migrationOptions
18
18
 
19
19
  yield* Effect.addFinalizer(
20
20
  Effect.fn('recreateDb:finalizer')(function* (ex) {
21
- if (ex._tag === 'Failure') dbReadModel.destroy()
21
+ if (ex._tag === 'Failure') db.destroy()
22
22
  }),
23
23
  )
24
24
 
25
25
  // NOTE to speed up the operations below, we're creating a temporary in-memory database
26
26
  // and later we'll overwrite the persisted database with the new data
27
- // TODO bring back this optimization
28
- // const tmpDb = yield* makeSqliteDb({ _tag: 'in-memory' })
29
- const tmpDb = dbReadModel
30
- yield* configureConnection(tmpDb, { fkEnabled: true })
27
+ const tmpSyncDb = yield* makeSyncDb({ _tag: 'in-memory' })
28
+ yield* configureConnection(tmpSyncDb, { fkEnabled: true })
31
29
 
32
30
  const initDb = (hooks: Partial<MigrationHooks> | undefined) =>
33
31
  Effect.gen(function* () {
34
- yield* Effect.tryAll(() => hooks?.init?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
32
+ yield* Effect.tryAll(() => hooks?.init?.(tmpSyncDb)).pipe(UnexpectedError.mapToUnexpectedError)
35
33
 
36
34
  yield* migrateDb({
37
- db: tmpDb,
35
+ db: tmpSyncDb,
38
36
  schema,
39
37
  onProgress: ({ done, total }) =>
40
38
  Queue.offer(bootStatusQueue, { stage: 'migrating', progress: { done, total } }),
41
39
  })
42
40
 
43
- initializeSingletonTables(schema, tmpDb)
41
+ initializeSingletonTables(schema, tmpSyncDb)
44
42
 
45
- yield* Effect.tryAll(() => hooks?.pre?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
43
+ yield* Effect.tryAll(() => hooks?.pre?.(tmpSyncDb)).pipe(UnexpectedError.mapToUnexpectedError)
46
44
 
47
- return tmpDb
45
+ return tmpSyncDb
48
46
  })
49
47
 
50
48
  switch (migrationOptions.strategy) {
51
49
  case 'from-mutation-log': {
52
50
  const hooks = migrationOptions.hooks
53
- const tmpDb = yield* initDb(hooks)
51
+ const tmpSyncDb = yield* initDb(hooks)
54
52
 
55
53
  yield* rehydrateFromMutationLog({
56
- db: tmpDb,
57
- logDb: dbMutationLog,
54
+ db: tmpSyncDb,
55
+ logDb: dbLog,
58
56
  schema,
59
57
  migrationOptions,
60
58
  onProgress: ({ done, total }) =>
61
59
  Queue.offer(bootStatusQueue, { stage: 'rehydrating', progress: { done, total } }),
62
60
  })
63
61
 
64
- yield* Effect.tryAll(() => hooks?.post?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
62
+ yield* Effect.tryAll(() => hooks?.post?.(tmpSyncDb)).pipe(UnexpectedError.mapToUnexpectedError)
65
63
 
66
64
  break
67
65
  }
@@ -76,13 +74,13 @@ export const recreateDb: Effect.Effect<
76
74
  break
77
75
  }
78
76
  case 'manual': {
79
- const oldDbData = dbReadModel.export()
77
+ const oldDbData = db.export()
80
78
 
81
79
  const newDbData = yield* Effect.tryAll(() => migrationOptions.migrate(oldDbData)).pipe(
82
80
  UnexpectedError.mapToUnexpectedError,
83
81
  )
84
82
 
85
- tmpDb.import(newDbData)
83
+ tmpSyncDb.import(newDbData)
86
84
 
87
85
  // TODO validate schema
88
86
 
@@ -93,19 +91,17 @@ export const recreateDb: Effect.Effect<
93
91
  }
94
92
  }
95
93
 
96
- // TODO bring back
97
94
  // Import the temporary in-memory database into the persistent database
98
- // yield* Effect.sync(() => db.import(tmpDb)).pipe(
99
- // Effect.withSpan('@livestore/common:leader-thread:recreateDb:import'),
100
- // )
95
+ yield* Effect.sync(() => db.import(tmpSyncDb)).pipe(
96
+ Effect.withSpan('@livestore/common:leader-thread:recreateDb:import'),
97
+ )
101
98
 
102
99
  // TODO maybe bring back re-using this initial snapshot to avoid calling `.export()` again
103
100
  // We've disabled this for now as it made the code too complex, as we often run syncing right after
104
101
  // so the snapshot is no longer up to date
105
- // const snapshotFromTmpDb = tmpDb.export()
102
+ // const snapshotFromTmpDb = tmpSyncDb.export()
106
103
 
107
- // TODO bring back
108
- // tmpDb.close()
104
+ tmpSyncDb.close()
109
105
  }).pipe(
110
106
  Effect.scoped, // NOTE we're closing the scope here so finalizers are called when the effect is done
111
107
  Effect.withSpan('@livestore/common:leader-thread:recreateDb'),