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

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 (280) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +21 -21
  3. package/dist/adapter-types.d.ts +41 -18
  4. package/dist/adapter-types.d.ts.map +1 -1
  5. package/dist/adapter-types.js +12 -0
  6. package/dist/adapter-types.js.map +1 -1
  7. package/dist/db-schema/ast/sqlite.d.ts +69 -0
  8. package/dist/db-schema/ast/sqlite.d.ts.map +1 -0
  9. package/dist/db-schema/ast/sqlite.js +71 -0
  10. package/dist/db-schema/ast/sqlite.js.map +1 -0
  11. package/dist/db-schema/ast/validate.d.ts +3 -0
  12. package/dist/db-schema/ast/validate.d.ts.map +1 -0
  13. package/dist/db-schema/ast/validate.js +12 -0
  14. package/dist/db-schema/ast/validate.js.map +1 -0
  15. package/dist/db-schema/dsl/field-defs.d.ts +90 -0
  16. package/dist/db-schema/dsl/field-defs.d.ts.map +1 -0
  17. package/dist/db-schema/dsl/field-defs.js +87 -0
  18. package/dist/db-schema/dsl/field-defs.js.map +1 -0
  19. package/dist/db-schema/dsl/field-defs.test.d.ts +2 -0
  20. package/dist/db-schema/dsl/field-defs.test.d.ts.map +1 -0
  21. package/dist/db-schema/dsl/field-defs.test.js +29 -0
  22. package/dist/db-schema/dsl/field-defs.test.js.map +1 -0
  23. package/dist/db-schema/dsl/index.d.ts +88 -0
  24. package/dist/db-schema/dsl/index.d.ts.map +1 -0
  25. package/dist/db-schema/dsl/index.js +35 -0
  26. package/dist/db-schema/dsl/index.js.map +1 -0
  27. package/dist/db-schema/dsl/mod.d.ts +90 -0
  28. package/dist/db-schema/dsl/mod.d.ts.map +1 -0
  29. package/dist/db-schema/dsl/mod.js +35 -0
  30. package/dist/db-schema/dsl/mod.js.map +1 -0
  31. package/dist/db-schema/dsl/sqlite/field-defs.d.ts +90 -0
  32. package/dist/db-schema/dsl/sqlite/field-defs.d.ts.map +1 -0
  33. package/dist/db-schema/dsl/sqlite/field-defs.js +86 -0
  34. package/dist/db-schema/dsl/sqlite/field-defs.js.map +1 -0
  35. package/dist/db-schema/dsl/sqlite/field-defs.test.d.ts +2 -0
  36. package/dist/db-schema/dsl/sqlite/field-defs.test.d.ts.map +1 -0
  37. package/dist/db-schema/dsl/sqlite/field-defs.test.js +29 -0
  38. package/dist/db-schema/dsl/sqlite/field-defs.test.js.map +1 -0
  39. package/dist/db-schema/dsl/sqlite/index.d.ts +88 -0
  40. package/dist/db-schema/dsl/sqlite/index.d.ts.map +1 -0
  41. package/dist/db-schema/dsl/sqlite/index.js +35 -0
  42. package/dist/db-schema/dsl/sqlite/index.js.map +1 -0
  43. package/dist/db-schema/hash.d.ts +2 -0
  44. package/dist/db-schema/hash.d.ts.map +1 -0
  45. package/dist/db-schema/hash.js +14 -0
  46. package/dist/db-schema/hash.js.map +1 -0
  47. package/dist/db-schema/index.d.ts +4 -0
  48. package/dist/db-schema/index.d.ts.map +1 -0
  49. package/dist/db-schema/index.js +6 -0
  50. package/dist/db-schema/index.js.map +1 -0
  51. package/dist/db-schema/mod.d.ts +3 -0
  52. package/dist/db-schema/mod.d.ts.map +1 -0
  53. package/dist/db-schema/mod.js +3 -0
  54. package/dist/db-schema/mod.js.map +1 -0
  55. package/dist/derived-mutations.d.ts +1 -1
  56. package/dist/derived-mutations.d.ts.map +1 -1
  57. package/dist/derived-mutations.js +3 -3
  58. package/dist/derived-mutations.js.map +1 -1
  59. package/dist/devtools/devtools-bridge.d.ts +10 -7
  60. package/dist/devtools/devtools-bridge.d.ts.map +1 -1
  61. package/dist/devtools/devtools-messages-client-session.d.ts +101 -28
  62. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  63. package/dist/devtools/devtools-messages-client-session.js +19 -3
  64. package/dist/devtools/devtools-messages-client-session.js.map +1 -1
  65. package/dist/devtools/devtools-messages-common.d.ts +24 -32
  66. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  67. package/dist/devtools/devtools-messages-common.js +19 -10
  68. package/dist/devtools/devtools-messages-common.js.map +1 -1
  69. package/dist/devtools/devtools-messages-leader.d.ts +220 -56
  70. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  71. package/dist/devtools/devtools-messages-leader.js +57 -9
  72. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  73. package/dist/devtools/devtools-messages.d.ts +2 -2
  74. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  75. package/dist/devtools/devtools-messages.js +2 -2
  76. package/dist/devtools/devtools-messages.js.map +1 -1
  77. package/dist/index.d.ts +1 -1
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +1 -1
  80. package/dist/index.js.map +1 -1
  81. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  82. package/dist/leader-thread/LeaderSyncProcessor.js +50 -35
  83. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  84. package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
  85. package/dist/leader-thread/apply-mutation.js +8 -6
  86. package/dist/leader-thread/apply-mutation.js.map +1 -1
  87. package/dist/leader-thread/connection.d.ts +31 -3
  88. package/dist/leader-thread/connection.d.ts.map +1 -1
  89. package/dist/leader-thread/connection.js +18 -3
  90. package/dist/leader-thread/connection.js.map +1 -1
  91. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  92. package/dist/leader-thread/leader-worker-devtools.js +56 -31
  93. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  94. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  95. package/dist/leader-thread/make-leader-thread-layer.js +23 -6
  96. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  97. package/dist/leader-thread/mutationlog.d.ts +1 -1
  98. package/dist/leader-thread/mutationlog.d.ts.map +1 -1
  99. package/dist/leader-thread/mutationlog.js +7 -5
  100. package/dist/leader-thread/mutationlog.js.map +1 -1
  101. package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
  102. package/dist/leader-thread/recreate-db.d.ts +4 -2
  103. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  104. package/dist/leader-thread/recreate-db.js +13 -8
  105. package/dist/leader-thread/recreate-db.js.map +1 -1
  106. package/dist/leader-thread/types.d.ts +22 -15
  107. package/dist/leader-thread/types.d.ts.map +1 -1
  108. package/dist/leader-thread/types.js +1 -3
  109. package/dist/leader-thread/types.js.map +1 -1
  110. package/dist/query-builder/api.d.ts +3 -3
  111. package/dist/query-builder/api.d.ts.map +1 -1
  112. package/dist/query-builder/impl.js.map +1 -1
  113. package/dist/query-builder/impl.test.js +16 -1
  114. package/dist/query-builder/impl.test.js.map +1 -1
  115. package/dist/query-info.d.ts +3 -3
  116. package/dist/query-info.d.ts.map +1 -1
  117. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  118. package/dist/rehydrate-from-mutationlog.js +8 -6
  119. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  120. package/dist/schema/EventId.d.ts +10 -9
  121. package/dist/schema/EventId.d.ts.map +1 -1
  122. package/dist/schema/EventId.js +14 -11
  123. package/dist/schema/EventId.js.map +1 -1
  124. package/dist/schema/EventId.test.js +3 -3
  125. package/dist/schema/EventId.test.js.map +1 -1
  126. package/dist/schema/MutationEvent.d.ts +37 -12
  127. package/dist/schema/MutationEvent.d.ts.map +1 -1
  128. package/dist/schema/MutationEvent.js +20 -4
  129. package/dist/schema/MutationEvent.js.map +1 -1
  130. package/dist/schema/db-schema/ast/sqlite.d.ts +69 -0
  131. package/dist/schema/db-schema/ast/sqlite.d.ts.map +1 -0
  132. package/dist/schema/db-schema/ast/sqlite.js +71 -0
  133. package/dist/schema/db-schema/ast/sqlite.js.map +1 -0
  134. package/dist/schema/db-schema/ast/validate.d.ts +3 -0
  135. package/dist/schema/db-schema/ast/validate.d.ts.map +1 -0
  136. package/dist/schema/db-schema/ast/validate.js +12 -0
  137. package/dist/schema/db-schema/ast/validate.js.map +1 -0
  138. package/dist/schema/db-schema/dsl/field-defs.d.ts +90 -0
  139. package/dist/schema/db-schema/dsl/field-defs.d.ts.map +1 -0
  140. package/dist/schema/db-schema/dsl/field-defs.js +87 -0
  141. package/dist/schema/db-schema/dsl/field-defs.js.map +1 -0
  142. package/dist/schema/db-schema/dsl/field-defs.test.d.ts +2 -0
  143. package/dist/schema/db-schema/dsl/field-defs.test.d.ts.map +1 -0
  144. package/dist/schema/db-schema/dsl/field-defs.test.js +29 -0
  145. package/dist/schema/db-schema/dsl/field-defs.test.js.map +1 -0
  146. package/dist/schema/db-schema/dsl/mod.d.ts +88 -0
  147. package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -0
  148. package/dist/schema/db-schema/dsl/mod.js +35 -0
  149. package/dist/schema/db-schema/dsl/mod.js.map +1 -0
  150. package/dist/schema/db-schema/hash.d.ts +2 -0
  151. package/dist/schema/db-schema/hash.d.ts.map +1 -0
  152. package/dist/schema/db-schema/hash.js +14 -0
  153. package/dist/schema/db-schema/hash.js.map +1 -0
  154. package/dist/schema/db-schema/mod.d.ts +3 -0
  155. package/dist/schema/db-schema/mod.d.ts.map +1 -0
  156. package/dist/schema/db-schema/mod.js +3 -0
  157. package/dist/schema/db-schema/mod.js.map +1 -0
  158. package/dist/schema/mod.d.ts +1 -0
  159. package/dist/schema/mod.d.ts.map +1 -1
  160. package/dist/schema/mod.js +1 -0
  161. package/dist/schema/mod.js.map +1 -1
  162. package/dist/schema/mutations.d.ts +4 -8
  163. package/dist/schema/mutations.d.ts.map +1 -1
  164. package/dist/schema/mutations.js +2 -2
  165. package/dist/schema/mutations.js.map +1 -1
  166. package/dist/schema/schema-helpers.js +1 -1
  167. package/dist/schema/schema-helpers.js.map +1 -1
  168. package/dist/schema/schema.d.ts +1 -1
  169. package/dist/schema/schema.d.ts.map +1 -1
  170. package/dist/schema/schema.js +1 -1
  171. package/dist/schema/schema.js.map +1 -1
  172. package/dist/schema/system-tables.d.ts +47 -29
  173. package/dist/schema/system-tables.d.ts.map +1 -1
  174. package/dist/schema/system-tables.js +10 -7
  175. package/dist/schema/system-tables.js.map +1 -1
  176. package/dist/schema/table-def.d.ts +18 -14
  177. package/dist/schema/table-def.d.ts.map +1 -1
  178. package/dist/schema/table-def.js +3 -4
  179. package/dist/schema/table-def.js.map +1 -1
  180. package/dist/schema-management/migrations.d.ts +3 -3
  181. package/dist/schema-management/migrations.d.ts.map +1 -1
  182. package/dist/schema-management/migrations.js +7 -2
  183. package/dist/schema-management/migrations.js.map +1 -1
  184. package/dist/sql-queries/sql-queries.d.ts +1 -1
  185. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  186. package/dist/sql-queries/sql-queries.js.map +1 -1
  187. package/dist/sql-queries/sql-query-builder.d.ts +1 -1
  188. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  189. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  190. package/dist/sql-queries/types.d.ts +2 -1
  191. package/dist/sql-queries/types.d.ts.map +1 -1
  192. package/dist/sql-queries/types.js.map +1 -1
  193. package/dist/sync/ClientSessionSyncProcessor.d.ts +3 -5
  194. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  195. package/dist/sync/ClientSessionSyncProcessor.js +38 -12
  196. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  197. package/dist/sync/next/facts.js +1 -1
  198. package/dist/sync/next/facts.js.map +1 -1
  199. package/dist/sync/next/history-dag-common.d.ts +2 -0
  200. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  201. package/dist/sync/next/history-dag-common.js +3 -1
  202. package/dist/sync/next/history-dag-common.js.map +1 -1
  203. package/dist/sync/next/history-dag.d.ts.map +1 -1
  204. package/dist/sync/next/history-dag.js +1 -1
  205. package/dist/sync/next/history-dag.js.map +1 -1
  206. package/dist/sync/next/rebase-events.d.ts +3 -1
  207. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  208. package/dist/sync/next/rebase-events.js +5 -3
  209. package/dist/sync/next/rebase-events.js.map +1 -1
  210. package/dist/sync/next/test/compact-events.calculator.test.js +12 -12
  211. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  212. package/dist/sync/next/test/compact-events.test.js +43 -43
  213. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  214. package/dist/sync/next/test/mutation-fixtures.d.ts +11 -11
  215. package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -1
  216. package/dist/sync/next/test/mutation-fixtures.js +12 -10
  217. package/dist/sync/next/test/mutation-fixtures.js.map +1 -1
  218. package/dist/sync/sync.d.ts +2 -2
  219. package/dist/sync/syncstate.d.ts +9 -9
  220. package/dist/sync/syncstate.js +6 -6
  221. package/dist/sync/syncstate.js.map +1 -1
  222. package/dist/sync/syncstate.test.js +18 -16
  223. package/dist/sync/syncstate.test.js.map +1 -1
  224. package/dist/version.d.ts +1 -1
  225. package/dist/version.js +1 -1
  226. package/package.json +2 -3
  227. package/src/adapter-types.ts +35 -17
  228. package/src/derived-mutations.ts +4 -4
  229. package/src/devtools/devtools-bridge.ts +10 -7
  230. package/src/devtools/devtools-messages-client-session.ts +26 -10
  231. package/src/devtools/devtools-messages-common.ts +37 -8
  232. package/src/devtools/devtools-messages-leader.ts +78 -16
  233. package/src/devtools/devtools-messages.ts +2 -2
  234. package/src/index.ts +1 -1
  235. package/src/leader-thread/LeaderSyncProcessor.ts +59 -38
  236. package/src/leader-thread/apply-mutation.ts +15 -5
  237. package/src/leader-thread/connection.ts +48 -3
  238. package/src/leader-thread/leader-worker-devtools.ts +85 -35
  239. package/src/leader-thread/make-leader-thread-layer.ts +29 -9
  240. package/src/leader-thread/mutationlog.ts +8 -6
  241. package/src/leader-thread/recreate-db.ts +19 -10
  242. package/src/leader-thread/types.ts +22 -15
  243. package/src/query-builder/api.ts +4 -4
  244. package/src/query-builder/impl.test.ts +22 -1
  245. package/src/query-builder/impl.ts +2 -2
  246. package/src/query-info.ts +3 -3
  247. package/src/rehydrate-from-mutationlog.ts +8 -6
  248. package/src/schema/EventId.test.ts +3 -3
  249. package/src/schema/EventId.ts +20 -16
  250. package/src/schema/MutationEvent.ts +31 -6
  251. package/src/schema/db-schema/ast/sqlite.ts +142 -0
  252. package/src/schema/db-schema/ast/validate.ts +13 -0
  253. package/src/schema/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +206 -0
  254. package/src/schema/db-schema/dsl/field-defs.test.ts +35 -0
  255. package/src/schema/db-schema/dsl/field-defs.ts +242 -0
  256. package/src/schema/db-schema/dsl/mod.ts +195 -0
  257. package/src/schema/db-schema/hash.ts +14 -0
  258. package/src/schema/db-schema/mod.ts +2 -0
  259. package/src/schema/mod.ts +1 -0
  260. package/src/schema/mutations.ts +6 -19
  261. package/src/schema/schema-helpers.ts +1 -1
  262. package/src/schema/schema.ts +2 -2
  263. package/src/schema/system-tables.ts +10 -7
  264. package/src/schema/table-def.ts +17 -16
  265. package/src/schema-management/migrations.ts +10 -6
  266. package/src/sql-queries/sql-queries.ts +1 -1
  267. package/src/sql-queries/sql-query-builder.ts +1 -2
  268. package/src/sql-queries/types.ts +3 -1
  269. package/src/sync/ClientSessionSyncProcessor.ts +44 -14
  270. package/src/sync/next/facts.ts +1 -1
  271. package/src/sync/next/history-dag-common.ts +5 -1
  272. package/src/sync/next/history-dag.ts +1 -1
  273. package/src/sync/next/rebase-events.ts +8 -2
  274. package/src/sync/next/test/compact-events.calculator.test.ts +12 -12
  275. package/src/sync/next/test/compact-events.test.ts +43 -43
  276. package/src/sync/next/test/mutation-fixtures.ts +16 -12
  277. package/src/sync/syncstate.test.ts +19 -17
  278. package/src/sync/syncstate.ts +6 -6
  279. package/src/version.ts +1 -1
  280. package/tsconfig.json +1 -1
@@ -1,4 +1,4 @@
1
- import { isNotUndefined, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
1
+ import { isNotUndefined, LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
2
2
  import type { HttpClient, Scope, Tracer } from '@livestore/utils/effect'
3
3
  import {
4
4
  BucketQueue,
@@ -32,7 +32,7 @@ import * as SyncState from '../sync/syncstate.js'
32
32
  import { sql } from '../util.js'
33
33
  import { makeApplyMutation } from './apply-mutation.js'
34
34
  import { execSql } from './connection.js'
35
- import { getBackendHeadFromDb, getLocalHeadFromDb, getMutationEventsSince, updateBackendHead } from './mutationlog.js'
35
+ import { getBackendHeadFromDb, getClientHeadFromDb, getMutationEventsSince, updateBackendHead } from './mutationlog.js'
36
36
  import type { InitialBlockingSyncContext, InitialSyncInfo, LeaderSyncProcessor } from './types.js'
37
37
  import { LeaderThreadCtx } from './types.js'
38
38
 
@@ -83,7 +83,7 @@ export const makeLeaderSyncProcessor = ({
83
83
 
84
84
  const isLocalEvent = (mutationEventEncoded: MutationEvent.EncodedWithMeta) => {
85
85
  const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
86
- return mutationDef.options.localOnly
86
+ return mutationDef.options.clientOnly
87
87
  }
88
88
 
89
89
  // This context depends on data from `boot`, we should find a better implementation to avoid this ref indirection.
@@ -93,8 +93,7 @@ export const makeLeaderSyncProcessor = ({
93
93
  | {
94
94
  otelSpan: otel.Span | undefined
95
95
  span: Tracer.Span
96
- devtoolsPullLatch: Effect.Latch | undefined
97
- devtoolsPushLatch: Effect.Latch | undefined
96
+ devtoolsLatch: Effect.Latch | undefined
98
97
  },
99
98
  }
100
99
 
@@ -107,10 +106,6 @@ export const makeLeaderSyncProcessor = ({
107
106
  // TODO validate batch
108
107
  if (newEvents.length === 0) return
109
108
 
110
- if (ctxRef.current?.devtoolsPushLatch !== undefined) {
111
- yield* ctxRef.current.devtoolsPushLatch.await
112
- }
113
-
114
109
  const waitForProcessing = options?.waitForProcessing ?? false
115
110
 
116
111
  if (waitForProcessing) {
@@ -137,18 +132,24 @@ export const makeLeaderSyncProcessor = ({
137
132
  }),
138
133
  )
139
134
 
140
- const pushPartial: LeaderSyncProcessor['pushPartial'] = (mutationEventEncoded_) =>
135
+ const pushPartial: LeaderSyncProcessor['pushPartial'] = ({
136
+ mutationEvent: partialMutationEvent,
137
+ clientId,
138
+ sessionId,
139
+ }) =>
141
140
  Effect.gen(function* () {
142
141
  const syncState = yield* syncStateSref
143
142
  if (syncState === undefined) return shouldNeverHappen('Not initialized')
144
143
 
145
144
  const mutationDef =
146
- schema.mutations.get(mutationEventEncoded_.mutation) ??
147
- shouldNeverHappen(`Unknown mutation: ${mutationEventEncoded_.mutation}`)
145
+ schema.mutations.get(partialMutationEvent.mutation) ??
146
+ shouldNeverHappen(`Unknown mutation: ${partialMutationEvent.mutation}`)
148
147
 
149
148
  const mutationEventEncoded = new MutationEvent.EncodedWithMeta({
150
- ...mutationEventEncoded_,
151
- ...EventId.nextPair(syncState.localHead, mutationDef.options.localOnly),
149
+ ...partialMutationEvent,
150
+ clientId,
151
+ sessionId,
152
+ ...EventId.nextPair(syncState.localHead, mutationDef.options.clientOnly),
152
153
  })
153
154
 
154
155
  yield* push([mutationEventEncoded])
@@ -164,12 +165,11 @@ export const makeLeaderSyncProcessor = ({
164
165
  ctxRef.current = {
165
166
  otelSpan,
166
167
  span,
167
- devtoolsPullLatch: devtools.enabled ? devtools.syncBackendPullLatch : undefined,
168
- devtoolsPushLatch: devtools.enabled ? devtools.syncBackendPushLatch : undefined,
168
+ devtoolsLatch: devtools.enabled ? devtools.syncBackendLatch : undefined,
169
169
  }
170
170
 
171
171
  const initialBackendHead = dbMissing ? EventId.ROOT.global : getBackendHeadFromDb(dbMutationLog)
172
- const initialLocalHead = dbMissing ? EventId.ROOT : getLocalHeadFromDb(dbMutationLog)
172
+ const initialLocalHead = dbMissing ? EventId.ROOT : getClientHeadFromDb(dbMutationLog)
173
173
 
174
174
  if (initialBackendHead > initialLocalHead.global) {
175
175
  return shouldNeverHappen(
@@ -179,14 +179,14 @@ export const makeLeaderSyncProcessor = ({
179
179
 
180
180
  const pendingMutationEvents = yield* getMutationEventsSince({
181
181
  global: initialBackendHead,
182
- local: EventId.localDefault,
182
+ client: EventId.clientDefault,
183
183
  }).pipe(Effect.map(ReadonlyArray.map((_) => new MutationEvent.EncodedWithMeta(_))))
184
184
 
185
185
  const initialSyncState = new SyncState.SyncState({
186
186
  pending: pendingMutationEvents,
187
187
  // On the leader we don't need a rollback tail beyond `pending` items
188
188
  rollbackTail: [],
189
- upstreamHead: { global: initialBackendHead, local: EventId.localDefault },
189
+ upstreamHead: { global: initialBackendHead, client: EventId.clientDefault },
190
190
  localHead: initialLocalHead,
191
191
  })
192
192
 
@@ -196,10 +196,10 @@ export const makeLeaderSyncProcessor = ({
196
196
  // Rehydrate sync queue
197
197
  if (pendingMutationEvents.length > 0) {
198
198
  const filteredBatch = pendingMutationEvents
199
- // Don't sync localOnly mutations
199
+ // Don't sync clientOnly mutations
200
200
  .filter((mutationEventEncoded) => {
201
201
  const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
202
- return mutationDef.options.localOnly === false
202
+ return mutationDef.options.clientOnly === false
203
203
  })
204
204
 
205
205
  yield* BucketQueue.offerAll(syncBackendQueue, filteredBatch)
@@ -220,7 +220,12 @@ export const makeLeaderSyncProcessor = ({
220
220
 
221
221
  yield* FiberHandle.run(
222
222
  backendPushingFiberHandle,
223
- backgroundBackendPushing({ dbReady, syncBackendQueue, otelSpan }).pipe(Effect.tapCauseLogPretty),
223
+ backgroundBackendPushing({
224
+ dbReady,
225
+ syncBackendQueue,
226
+ otelSpan,
227
+ devtoolsLatch: ctxRef.current?.devtoolsLatch,
228
+ }).pipe(Effect.tapCauseLogPretty),
224
229
  )
225
230
 
226
231
  yield* backgroundBackendPulling({
@@ -239,7 +244,12 @@ export const makeLeaderSyncProcessor = ({
239
244
  // Restart pushing fiber
240
245
  yield* FiberHandle.run(
241
246
  backendPushingFiberHandle,
242
- backgroundBackendPushing({ dbReady, syncBackendQueue, otelSpan }).pipe(Effect.tapCauseLogPretty),
247
+ backgroundBackendPushing({
248
+ dbReady,
249
+ syncBackendQueue,
250
+ otelSpan,
251
+ devtoolsLatch: ctxRef.current?.devtoolsLatch,
252
+ }).pipe(Effect.tapCauseLogPretty),
243
253
  )
244
254
  }),
245
255
  syncStateSref,
@@ -247,8 +257,10 @@ export const makeLeaderSyncProcessor = ({
247
257
  pullLatch,
248
258
  otelSpan,
249
259
  initialBlockingSyncContext,
250
- devtoolsPullLatch: ctxRef.current?.devtoolsPullLatch,
260
+ devtoolsLatch: ctxRef.current?.devtoolsLatch,
251
261
  }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
262
+
263
+ return { initialLeaderHead: initialLocalHead }
252
264
  }).pipe(Effect.withSpanScoped('@livestore/common:leader-thread:syncing'))
253
265
 
254
266
  return {
@@ -357,10 +369,10 @@ const backgroundApplyLocalPushes = ({
357
369
  updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
358
370
  })
359
371
 
360
- // Don't sync localOnly mutations
372
+ // Don't sync clientOnly mutations
361
373
  const filteredBatch = updateResult.newEvents.filter((mutationEventEncoded) => {
362
374
  const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
363
- return mutationDef.options.localOnly === false
375
+ return mutationDef.options.clientOnly === false
364
376
  })
365
377
 
366
378
  yield* BucketQueue.offerAll(syncBackendQueue, filteredBatch)
@@ -431,7 +443,7 @@ const backgroundBackendPulling = ({
431
443
  syncStateSref,
432
444
  localPushesLatch,
433
445
  pullLatch,
434
- devtoolsPullLatch,
446
+ devtoolsLatch,
435
447
  initialBlockingSyncContext,
436
448
  }: {
437
449
  dbReady: Deferred.Deferred<void>
@@ -444,7 +456,7 @@ const backgroundBackendPulling = ({
444
456
  syncStateSref: SubscriptionRef.SubscriptionRef<SyncState.SyncState | undefined>
445
457
  localPushesLatch: Effect.Latch
446
458
  pullLatch: Effect.Latch
447
- devtoolsPullLatch: Effect.Latch | undefined
459
+ devtoolsLatch: Effect.Latch | undefined
448
460
  initialBlockingSyncContext: InitialBlockingSyncContext
449
461
  }) =>
450
462
  Effect.gen(function* () {
@@ -466,8 +478,8 @@ const backgroundBackendPulling = ({
466
478
  Effect.gen(function* () {
467
479
  if (newEvents.length === 0) return
468
480
 
469
- if (devtoolsPullLatch !== undefined) {
470
- yield* devtoolsPullLatch.await
481
+ if (devtoolsLatch !== undefined) {
482
+ yield* devtoolsLatch.await
471
483
  }
472
484
 
473
485
  // Prevent more local pushes from being processed until this pull is finished
@@ -507,7 +519,7 @@ const backgroundBackendPulling = ({
507
519
 
508
520
  const filteredRebasedPending = updateResult.newSyncState.pending.filter((mutationEvent) => {
509
521
  const mutationDef = schema.mutations.get(mutationEvent.mutation)!
510
- return mutationDef.options.localOnly === false
522
+ return mutationDef.options.clientOnly === false
511
523
  })
512
524
  yield* restartBackendPushing(filteredRebasedPending)
513
525
 
@@ -592,9 +604,9 @@ const rollback = ({
592
604
  Effect.gen(function* () {
593
605
  const rollbackEvents = db
594
606
  .select<SessionChangesetMetaRow>(
595
- sql`SELECT * FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idLocal) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.local})`).join(', ')})`,
607
+ sql`SELECT * FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`).join(', ')})`,
596
608
  )
597
- .map((_) => ({ id: { global: _.idGlobal, local: _.idLocal }, changeset: _.changeset, debug: _.debug }))
609
+ .map((_) => ({ id: { global: _.idGlobal, client: _.idClient }, changeset: _.changeset, debug: _.debug }))
598
610
  .toSorted((a, b) => EventId.compare(a.id, b.id))
599
611
 
600
612
  // Apply changesets in reverse order
@@ -607,12 +619,12 @@ const rollback = ({
607
619
 
608
620
  // Delete the changeset rows
609
621
  db.execute(
610
- sql`DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idLocal) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.local})`).join(', ')})`,
622
+ sql`DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`).join(', ')})`,
611
623
  )
612
624
 
613
625
  // Delete the mutation log rows
614
626
  dbMutationLog.execute(
615
- sql`DELETE FROM ${MUTATION_LOG_META_TABLE} WHERE (idGlobal, idLocal) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.local})`).join(', ')})`,
627
+ sql`DELETE FROM ${MUTATION_LOG_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`).join(', ')})`,
616
628
  )
617
629
  }).pipe(
618
630
  Effect.withSpan('@livestore/common:leader-thread:syncing:rollback', {
@@ -632,12 +644,12 @@ const getCursorInfo = (remoteHead: EventId.GlobalEventId) =>
632
644
 
633
645
  const syncMetadataOption = yield* Effect.sync(() =>
634
646
  dbMutationLog.select<{ syncMetadataJson: string }>(
635
- sql`SELECT syncMetadataJson FROM ${MUTATION_LOG_META_TABLE} WHERE idGlobal = ${remoteHead} ORDER BY idLocal ASC LIMIT 1`,
647
+ sql`SELECT syncMetadataJson FROM ${MUTATION_LOG_META_TABLE} WHERE idGlobal = ${remoteHead} ORDER BY idClient ASC LIMIT 1`,
636
648
  ),
637
649
  ).pipe(Effect.andThen(Schema.decode(MutationlogQuerySchema)), Effect.map(Option.flatten), Effect.orDie)
638
650
 
639
651
  return Option.some({
640
- cursor: { global: remoteHead, local: EventId.localDefault },
652
+ cursor: { global: remoteHead, client: EventId.clientDefault },
641
653
  metadata: syncMetadataOption,
642
654
  }) satisfies InitialSyncInfo
643
655
  }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:getCursorInfo', { attributes: { remoteHead } }))
@@ -646,10 +658,12 @@ const backgroundBackendPushing = ({
646
658
  dbReady,
647
659
  syncBackendQueue,
648
660
  otelSpan,
661
+ devtoolsLatch,
649
662
  }: {
650
663
  dbReady: Deferred.Deferred<void>
651
664
  syncBackendQueue: BucketQueue.BucketQueue<MutationEvent.EncodedWithMeta>
652
665
  otelSpan: otel.Span | undefined
666
+ devtoolsLatch: Effect.Latch | undefined
653
667
  }) =>
654
668
  Effect.gen(function* () {
655
669
  const { syncBackend, dbMutationLog } = yield* LeaderThreadCtx
@@ -665,6 +679,10 @@ const backgroundBackendPushing = ({
665
679
 
666
680
  yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true)
667
681
 
682
+ if (devtoolsLatch !== undefined) {
683
+ yield* devtoolsLatch.await
684
+ }
685
+
668
686
  otelSpan?.addEvent('backend-push', {
669
687
  batchSize: queueItems.length,
670
688
  batch: TRACE_VERBOSE ? JSON.stringify(queueItems) : undefined,
@@ -674,6 +692,9 @@ const backgroundBackendPushing = ({
674
692
  const pushResult = yield* syncBackend.push(queueItems.map((_) => _.toGlobal())).pipe(Effect.either)
675
693
 
676
694
  if (pushResult._tag === 'Left') {
695
+ if (LS_DEV) {
696
+ yield* Effect.logDebug('backend-push-error', { error: pushResult.left.toString() })
697
+ }
677
698
  otelSpan?.addEvent('backend-push-error', { error: pushResult.left.toString() })
678
699
  // wait for interrupt caused by background pulling which will then restart pushing
679
700
  return yield* Effect.never
@@ -689,7 +710,7 @@ const backgroundBackendPushing = ({
689
710
  ...updateRows({
690
711
  tableName: MUTATION_LOG_META_TABLE,
691
712
  columns: mutationLogMetaTable.sqliteDef.columns,
692
- where: { idGlobal: mutationEventEncoded.id.global, idLocal: mutationEventEncoded.id.local },
713
+ where: { idGlobal: mutationEventEncoded.id.global, idClient: mutationEventEncoded.id.client },
693
714
  updateValues: { syncMetadataJson: metadata[i]! },
694
715
  }),
695
716
  )
@@ -79,7 +79,7 @@ export const makeApplyMutation: Effect.Effect<ApplyMutation, never, Scope.Scope
79
79
  columns: sessionChangesetMetaTable.sqliteDef.columns,
80
80
  values: {
81
81
  idGlobal: mutationEventEncoded.id.global,
82
- idLocal: mutationEventEncoded.id.local,
82
+ idClient: mutationEventEncoded.id.client,
83
83
  // NOTE the changeset will be empty (i.e. null) for no-op mutations
84
84
  changeset: changeset ?? null,
85
85
  debug: execArgsArr,
@@ -92,7 +92,13 @@ export const makeApplyMutation: Effect.Effect<ApplyMutation, never, Scope.Scope
92
92
  // write to mutation_log
93
93
  const excludeFromMutationLog = shouldExcludeMutationFromLog(mutationName, mutationEventEncoded)
94
94
  if (skipMutationLog === false && excludeFromMutationLog === false) {
95
- yield* insertIntoMutationLog(mutationEventEncoded, dbMutationLog, mutationDefSchemaHashMap)
95
+ yield* insertIntoMutationLog(
96
+ mutationEventEncoded,
97
+ dbMutationLog,
98
+ mutationDefSchemaHashMap,
99
+ mutationEventEncoded.clientId,
100
+ mutationEventEncoded.sessionId,
101
+ )
96
102
  } else {
97
103
  // console.debug('[@livestore/common:leader-thread] skipping mutation log write', mutation, statementSql, bindValues)
98
104
  }
@@ -101,7 +107,7 @@ export const makeApplyMutation: Effect.Effect<ApplyMutation, never, Scope.Scope
101
107
  attributes: {
102
108
  mutationName: mutationEventEncoded.mutation,
103
109
  mutationId: mutationEventEncoded.id,
104
- 'span.label': `(${mutationEventEncoded.id.global},${mutationEventEncoded.id.local}) ${mutationEventEncoded.mutation}`,
110
+ 'span.label': `(${mutationEventEncoded.id.global},${mutationEventEncoded.id.client}) ${mutationEventEncoded.mutation}`,
105
111
  },
106
112
  }),
107
113
  // Effect.logDuration('@livestore/common:leader-thread:applyMutation'),
@@ -113,6 +119,8 @@ const insertIntoMutationLog = (
113
119
  mutationEventEncoded: MutationEvent.AnyEncoded,
114
120
  dbMutationLog: SqliteDb,
115
121
  mutationDefSchemaHashMap: Map<string, number>,
122
+ clientId: string,
123
+ sessionId: string | undefined,
116
124
  ) =>
117
125
  Effect.gen(function* () {
118
126
  const mutationName = mutationEventEncoded.mutation
@@ -127,11 +135,13 @@ const insertIntoMutationLog = (
127
135
  columns: mutationLogMetaTable.sqliteDef.columns,
128
136
  values: {
129
137
  idGlobal: mutationEventEncoded.id.global,
130
- idLocal: mutationEventEncoded.id.local,
138
+ idClient: mutationEventEncoded.id.client,
131
139
  parentIdGlobal: mutationEventEncoded.parentId.global,
132
- parentIdLocal: mutationEventEncoded.parentId.local,
140
+ parentIdClient: mutationEventEncoded.parentId.client,
133
141
  mutation: mutationEventEncoded.mutation,
134
142
  argsJson: mutationEventEncoded.args ?? {},
143
+ clientId,
144
+ sessionId: sessionId ?? null,
135
145
  schemaHash: mutationDefSchemaHash,
136
146
  syncMetadataJson: Option.none(),
137
147
  },
@@ -12,13 +12,58 @@ namespace WaSqlite {
12
12
  export type SQLiteError = any
13
13
  }
14
14
 
15
- export const configureConnection = (sqliteDb: SqliteDb, { fkEnabled }: { fkEnabled: boolean }) =>
15
+ type ConnectionOptions = {
16
+ /**
17
+ * The database connection locking mode.
18
+ *
19
+ * @remarks
20
+ *
21
+ * This **option is ignored** when used on an **in-memory database** as they can only operate in exclusive locking mode.
22
+ * In-memory databases can’t share state between connections (unless using a
23
+ * {@link https://www.sqlite.org/sharedcache.html#shared_cache_and_in_memory_databases|shared cache}),
24
+ * making concurrent access impossible. This is functionally equivalent to exclusive locking.
25
+ *
26
+ * @defaultValue
27
+ * The default is `"NORMAL"` unless it was unless overridden at compile-time using `SQLITE_DEFAULT_LOCKING_MODE`.
28
+ *
29
+ * @see {@link https://www.sqlite.org/pragma.html#pragma_locking_mode|`locking_mode` pragma}
30
+ */
31
+ lockingMode?: 'NORMAL' | 'EXCLUSIVE'
32
+
33
+ /**
34
+ * Whether to enforce foreign key constraints.
35
+ *
36
+ * @privateRemarks
37
+ *
38
+ * We require a value for this option to minimize future problems, as the default value might change in future
39
+ * versions of SQLite.
40
+ *
41
+ * @see {@link https://www.sqlite.org/pragma.html#pragma_foreign_keys|`foreign_keys` pragma}
42
+ */
43
+ foreignKeys: boolean
44
+ }
45
+
46
+ export const configureConnection = (sqliteDb: SqliteDb, { foreignKeys, lockingMode }: ConnectionOptions) =>
16
47
  execSql(
17
48
  sqliteDb,
49
+ // We use the WAL journal mode is significantly faster in most scenarios than the traditional rollback journal mode.
50
+ // It specifically significantly improves write performance. However, when using the WAL journal mode, transactions
51
+ // that involve changes against multiple ATTACHed databases are atomic for each database but are not atomic
52
+ // across all databases as a set. Additionally, it is not possible to change the page size after entering WAL mode,
53
+ // whether on an empty database or by using VACUUM or the backup API. To change the page size, we must switch to the
54
+ // rollback journal mode.
55
+ //
56
+ // When connected to an in-memory database, the WAL journal mode option is ignored because an in-memory database can
57
+ // only be in either the MEMORY or OFF options. By default, an in-memory database is in the MEMORY option, which
58
+ // means that it stores the rollback journal in volatile RAM. This saves disk I/O but at the expense of safety and
59
+ // integrity. If the thread using SQLite crashes in the middle of a transaction, then the database file will very
60
+ // likely go corrupt.
18
61
  sql`
62
+ -- disable WAL until we have it working properly
63
+ -- PRAGMA journal_mode=WAL;
19
64
  PRAGMA page_size=8192;
20
- PRAGMA journal_mode=MEMORY;
21
- ${fkEnabled ? sql`PRAGMA foreign_keys='ON';` : sql`PRAGMA foreign_keys='OFF';`}
65
+ PRAGMA foreign_keys=${foreignKeys ? 'ON' : 'OFF'};
66
+ ${lockingMode === undefined ? '' : sql`PRAGMA locking_mode=${lockingMode};`}
22
67
  `,
23
68
  {},
24
69
  )
@@ -5,7 +5,7 @@ import { MUTATION_LOG_META_TABLE, SCHEMA_META_TABLE, SCHEMA_MUTATIONS_META_TABLE
5
5
  import type { DevtoolsOptions, PersistenceInfoPair } from './types.js'
6
6
  import { LeaderThreadCtx } from './types.js'
7
7
 
8
- type SendMessageToDevtools = (message: Devtools.MessageFromAppLeader) => Effect.Effect<void>
8
+ type SendMessageToDevtools = (message: Devtools.Leader.MessageFromApp) => Effect.Effect<void>
9
9
 
10
10
  // TODO bind scope to the webchannel lifetime
11
11
  export const bootDevtools = (options: DevtoolsOptions) =>
@@ -38,18 +38,7 @@ export const bootDevtools = (options: DevtoolsOptions) =>
38
38
  const pullQueue = yield* connectedClientSessionPullQueues.makeQueue(localHead)
39
39
 
40
40
  yield* Stream.fromQueue(pullQueue).pipe(
41
- Stream.tap((msg) =>
42
- Effect.gen(function* () {
43
- if (msg.payload._tag === 'upstream-advance') {
44
- for (const mutationEventEncoded of msg.payload.newEvents) {
45
- // TODO refactor with push semantics
46
- yield* sendMessage(Devtools.MutationBroadcast.make({ mutationEventEncoded, liveStoreVersion }))
47
- }
48
- } else {
49
- yield* Effect.logWarning('TODO implement rebases in devtools')
50
- }
51
- }),
52
- ),
41
+ Stream.tap((msg) => sendMessage(Devtools.Leader.SyncPull.make({ payload: msg.payload, liveStoreVersion }))),
53
42
  Stream.runDrain,
54
43
  Effect.forkScoped,
55
44
  )
@@ -66,7 +55,7 @@ const listenToDevtools = ({
66
55
  sendMessage,
67
56
  persistenceInfo,
68
57
  }: {
69
- incomingMessages: Stream.Stream<Devtools.MessageToAppLeader>
58
+ incomingMessages: Stream.Stream<Devtools.Leader.MessageToApp>
70
59
  sendMessage: SendMessageToDevtools
71
60
  persistenceInfo?: PersistenceInfoPair
72
61
  }) =>
@@ -79,6 +68,8 @@ const listenToDevtools = ({
79
68
  shutdownStateSubRef,
80
69
  shutdownChannel,
81
70
  syncProcessor,
71
+ clientId,
72
+ devtools,
82
73
  } = yield* LeaderThreadCtx
83
74
 
84
75
  type RequestId = string
@@ -89,22 +80,22 @@ const listenToDevtools = ({
89
80
  Effect.gen(function* () {
90
81
  // yield* Effect.logDebug('[@livestore/common:leader-thread:devtools] incomingMessage', decodedEvent)
91
82
 
92
- if (decodedEvent._tag === 'LSD.Disconnect') {
83
+ if (decodedEvent._tag === 'LSD.Leader.Disconnect') {
93
84
  return
94
85
  }
95
86
 
96
87
  const { requestId } = decodedEvent
97
- const reqPayload = { requestId, liveStoreVersion }
88
+ const reqPayload = { requestId, liveStoreVersion, clientId }
98
89
 
99
90
  switch (decodedEvent._tag) {
100
- case 'LSD.Ping': {
101
- yield* sendMessage(Devtools.Pong.make({ ...reqPayload }))
91
+ case 'LSD.Leader.Ping': {
92
+ yield* sendMessage(Devtools.Leader.Pong.make({ ...reqPayload }))
102
93
  return
103
94
  }
104
95
  case 'LSD.Leader.SnapshotReq': {
105
96
  const snapshot = dbReadModel.export()
106
97
 
107
- yield* sendMessage(Devtools.SnapshotRes.make({ snapshot, ...reqPayload }))
98
+ yield* sendMessage(Devtools.Leader.SnapshotRes.make({ snapshot, ...reqPayload }))
108
99
 
109
100
  return
110
101
  }
@@ -125,7 +116,9 @@ const listenToDevtools = ({
125
116
  tmpDb.close()
126
117
  } catch (e) {
127
118
  yield* Effect.logError(`Error importing database file`, e)
128
- yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-file' }))
119
+ yield* sendMessage(
120
+ Devtools.Leader.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-file' }),
121
+ )
129
122
 
130
123
  return
131
124
  }
@@ -143,17 +136,19 @@ const listenToDevtools = ({
143
136
 
144
137
  dbMutationLog.destroy()
145
138
  } else {
146
- yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-database' }))
139
+ yield* sendMessage(
140
+ Devtools.Leader.LoadDatabaseFileRes.make({ ...reqPayload, status: 'unsupported-database' }),
141
+ )
147
142
  return
148
143
  }
149
144
 
150
- yield* sendMessage(Devtools.LoadDatabaseFileRes.make({ ...reqPayload, status: 'ok' }))
145
+ yield* sendMessage(Devtools.Leader.LoadDatabaseFileRes.make({ ...reqPayload, status: 'ok' }))
151
146
 
152
147
  yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-import' })) ?? Effect.void
153
148
 
154
149
  return
155
150
  }
156
- case 'LSD.Leader.ResetAllDataReq': {
151
+ case 'LSD.Leader.ResetAllData.Request': {
157
152
  const { mode } = decodedEvent
158
153
 
159
154
  yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
@@ -164,7 +159,7 @@ const listenToDevtools = ({
164
159
  dbMutationLog.destroy()
165
160
  }
166
161
 
167
- yield* sendMessage(Devtools.ResetAllDataRes.make({ ...reqPayload }))
162
+ yield* sendMessage(Devtools.Leader.ResetAllData.Response.make({ ...reqPayload }))
168
163
 
169
164
  yield* shutdownChannel.send(IntentionalShutdownCause.make({ reason: 'devtools-reset' })) ?? Effect.void
170
165
 
@@ -181,7 +176,7 @@ const listenToDevtools = ({
181
176
  const mutationLogFileSize = dbMutationLog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
182
177
 
183
178
  yield* sendMessage(
184
- Devtools.DatabaseFileInfoRes.make({
179
+ Devtools.Leader.DatabaseFileInfoRes.make({
185
180
  readModel: { fileSize: dbFileSize, persistenceInfo: persistenceInfo.readModel },
186
181
  mutationLog: { fileSize: mutationLogFileSize, persistenceInfo: persistenceInfo.mutationLog },
187
182
  ...reqPayload,
@@ -193,14 +188,18 @@ const listenToDevtools = ({
193
188
  case 'LSD.Leader.MutationLogReq': {
194
189
  const mutationLog = dbMutationLog.export()
195
190
 
196
- yield* sendMessage(Devtools.MutationLogRes.make({ mutationLog, ...reqPayload }))
191
+ yield* sendMessage(Devtools.Leader.MutationLogRes.make({ mutationLog, ...reqPayload }))
197
192
 
198
193
  return
199
194
  }
200
195
  case 'LSD.Leader.RunMutationReq': {
201
- yield* syncProcessor.pushPartial(decodedEvent.mutationEventEncoded)
196
+ yield* syncProcessor.pushPartial({
197
+ mutationEvent: decodedEvent.mutationEventEncoded,
198
+ clientId: `devtools-${clientId}`,
199
+ sessionId: undefined,
200
+ })
202
201
 
203
- yield* sendMessage(Devtools.RunMutationRes.make({ ...reqPayload }))
202
+ yield* sendMessage(Devtools.Leader.RunMutationRes.make({ ...reqPayload }))
204
203
 
205
204
  return
206
205
  }
@@ -213,7 +212,7 @@ const listenToDevtools = ({
213
212
  Stream.map((_) => _.batch),
214
213
  Stream.flattenIterables,
215
214
  Stream.tap(({ mutationEventEncoded, metadata }) =>
216
- sendMessage(Devtools.SyncHistoryRes.make({ mutationEventEncoded, metadata, ...reqPayload })),
215
+ sendMessage(Devtools.Leader.SyncHistoryRes.make({ mutationEventEncoded, metadata, ...reqPayload })),
217
216
  ),
218
217
  Stream.runDrain,
219
218
  Effect.acquireRelease(() => Effect.log('syncHistorySubscribe done')),
@@ -234,12 +233,12 @@ const listenToDevtools = ({
234
233
  return
235
234
  }
236
235
  case 'LSD.Leader.SyncingInfoReq': {
237
- const syncingInfo = Devtools.SyncingInfo.make({
236
+ const syncingInfo = Devtools.Leader.SyncingInfo.make({
238
237
  enabled: syncBackend !== undefined,
239
238
  metadata: {},
240
239
  })
241
240
 
242
- yield* sendMessage(Devtools.SyncingInfoRes.make({ syncingInfo, ...reqPayload }))
241
+ yield* sendMessage(Devtools.Leader.SyncingInfoRes.make({ syncingInfo, ...reqPayload }))
243
242
 
244
243
  return
245
244
  }
@@ -252,11 +251,14 @@ const listenToDevtools = ({
252
251
  // This is probably the same "flaky databrowser loading" bug as we're seeing in the playwright tests
253
252
  yield* Effect.sleep(1000)
254
253
 
255
- yield* syncBackend.isConnected.changes.pipe(
256
- Stream.tap((isConnected) =>
254
+ yield* Stream.zipLatest(
255
+ syncBackend.isConnected.changes,
256
+ devtools.enabled ? devtools.syncBackendLatchState.changes : Stream.make({ latchClosed: false }),
257
+ ).pipe(
258
+ Stream.tap(([isConnected, { latchClosed }]) =>
257
259
  sendMessage(
258
- Devtools.NetworkStatusRes.make({
259
- networkStatus: { isConnected, timestampMs: Date.now() },
260
+ Devtools.Leader.NetworkStatusRes.make({
261
+ networkStatus: { isConnected, timestampMs: Date.now(), latchClosed },
260
262
  ...reqPayload,
261
263
  }),
262
264
  ),
@@ -277,6 +279,54 @@ const listenToDevtools = ({
277
279
 
278
280
  return
279
281
  }
282
+ case 'LSD.Leader.SyncHeadSubscribe': {
283
+ const { requestId } = decodedEvent
284
+
285
+ yield* syncProcessor.syncState.changes.pipe(
286
+ Stream.tap((syncState) =>
287
+ sendMessage(
288
+ Devtools.Leader.SyncHeadRes.make({
289
+ local: syncState.localHead,
290
+ upstream: syncState.upstreamHead,
291
+ ...reqPayload,
292
+ }),
293
+ ),
294
+ ),
295
+ Stream.runDrain,
296
+ Effect.interruptible,
297
+ Effect.tapCauseLogPretty,
298
+ FiberMap.run(subscriptionFiberMap, requestId),
299
+ )
300
+
301
+ return
302
+ }
303
+ case 'LSD.Leader.SyncHeadUnsubscribe': {
304
+ const { requestId } = decodedEvent
305
+
306
+ yield* FiberMap.remove(subscriptionFiberMap, requestId)
307
+
308
+ return
309
+ }
310
+ case 'LSD.Leader.SetSyncLatch.Request': {
311
+ const { closeLatch } = decodedEvent
312
+
313
+ if (devtools.enabled === false) return
314
+
315
+ if (closeLatch === true) {
316
+ yield* devtools.syncBackendLatch.close
317
+ } else {
318
+ yield* devtools.syncBackendLatch.open
319
+ }
320
+
321
+ yield* SubscriptionRef.set(devtools.syncBackendLatchState, { latchClosed: closeLatch })
322
+
323
+ yield* sendMessage(Devtools.Leader.SetSyncLatch.Response.make({ ...reqPayload }))
324
+
325
+ return
326
+ }
327
+ default: {
328
+ yield* Effect.logWarning(`TODO implement devtools message`, decodedEvent)
329
+ }
280
330
  }
281
331
  }).pipe(Effect.withSpan(`@livestore/common:leader-thread:onDevtoolsMessage:${decodedEvent._tag}`)),
282
332
  ),