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

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 (332) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +21 -21
  3. package/dist/adapter-types.d.ts +97 -53
  4. package/dist/adapter-types.d.ts.map +1 -1
  5. package/dist/adapter-types.js +17 -0
  6. package/dist/adapter-types.js.map +1 -1
  7. package/dist/bounded-collections.d.ts +1 -1
  8. package/dist/bounded-collections.d.ts.map +1 -1
  9. package/dist/debug-info.d.ts +1 -1
  10. package/dist/debug-info.d.ts.map +1 -1
  11. package/dist/derived-mutations.d.ts +5 -5
  12. package/dist/derived-mutations.d.ts.map +1 -1
  13. package/dist/derived-mutations.js +3 -3
  14. package/dist/derived-mutations.js.map +1 -1
  15. package/dist/derived-mutations.test.js.map +1 -1
  16. package/dist/devtools/devtools-messages-client-session.d.ts +389 -0
  17. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -0
  18. package/dist/devtools/devtools-messages-client-session.js +96 -0
  19. package/dist/devtools/devtools-messages-client-session.js.map +1 -0
  20. package/dist/devtools/devtools-messages-common.d.ts +61 -0
  21. package/dist/devtools/devtools-messages-common.d.ts.map +1 -0
  22. package/dist/devtools/devtools-messages-common.js +54 -0
  23. package/dist/devtools/devtools-messages-common.js.map +1 -0
  24. package/dist/devtools/devtools-messages-leader.d.ts +393 -0
  25. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -0
  26. package/dist/devtools/devtools-messages-leader.js +148 -0
  27. package/dist/devtools/devtools-messages-leader.js.map +1 -0
  28. package/dist/devtools/devtools-messages.d.ts +3 -592
  29. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  30. package/dist/devtools/devtools-messages.js +3 -171
  31. package/dist/devtools/devtools-messages.js.map +1 -1
  32. package/dist/devtools/devtools-sessioninfo.d.ts +28 -0
  33. package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -0
  34. package/dist/devtools/devtools-sessioninfo.js +34 -0
  35. package/dist/devtools/devtools-sessioninfo.js.map +1 -0
  36. package/dist/devtools/devtools-sessions-channel.d.ts +28 -0
  37. package/dist/devtools/devtools-sessions-channel.d.ts.map +1 -0
  38. package/dist/devtools/devtools-sessions-channel.js +34 -0
  39. package/dist/devtools/devtools-sessions-channel.js.map +1 -0
  40. package/dist/devtools/index.d.ts +35 -38
  41. package/dist/devtools/index.d.ts.map +1 -1
  42. package/dist/devtools/index.js +20 -45
  43. package/dist/devtools/index.js.map +1 -1
  44. package/dist/devtools/mod.d.ts +39 -0
  45. package/dist/devtools/mod.d.ts.map +1 -0
  46. package/dist/devtools/mod.js +27 -0
  47. package/dist/devtools/mod.js.map +1 -0
  48. package/dist/index.d.ts +2 -6
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +2 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/init-singleton-tables.d.ts +2 -2
  53. package/dist/init-singleton-tables.d.ts.map +1 -1
  54. package/dist/init-singleton-tables.js.map +1 -1
  55. package/dist/leader-thread/LeaderSyncProcessor.d.ts +39 -0
  56. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -0
  57. package/dist/leader-thread/LeaderSyncProcessor.js +527 -0
  58. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -0
  59. package/dist/leader-thread/apply-mutation.d.ts +5 -2
  60. package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
  61. package/dist/leader-thread/apply-mutation.js +55 -35
  62. package/dist/leader-thread/apply-mutation.js.map +1 -1
  63. package/dist/leader-thread/connection.d.ts +34 -6
  64. package/dist/leader-thread/connection.d.ts.map +1 -1
  65. package/dist/leader-thread/connection.js +22 -7
  66. package/dist/leader-thread/connection.js.map +1 -1
  67. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  68. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  69. package/dist/leader-thread/leader-worker-devtools.js +147 -124
  70. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  71. package/dist/leader-thread/make-leader-thread-layer.d.ts +12 -11
  72. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  73. package/dist/leader-thread/make-leader-thread-layer.js +55 -18
  74. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  75. package/dist/leader-thread/mutationlog.d.ts +6 -19
  76. package/dist/leader-thread/mutationlog.d.ts.map +1 -1
  77. package/dist/leader-thread/mutationlog.js +12 -9
  78. package/dist/leader-thread/mutationlog.js.map +1 -1
  79. package/dist/leader-thread/pull-queue-set.d.ts +3 -3
  80. package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
  81. package/dist/leader-thread/pull-queue-set.js +9 -0
  82. package/dist/leader-thread/pull-queue-set.js.map +1 -1
  83. package/dist/leader-thread/recreate-db.d.ts +4 -2
  84. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  85. package/dist/leader-thread/recreate-db.js +32 -21
  86. package/dist/leader-thread/recreate-db.js.map +1 -1
  87. package/dist/leader-thread/shutdown-channel.d.ts +2 -5
  88. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  89. package/dist/leader-thread/shutdown-channel.js +2 -4
  90. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  91. package/dist/leader-thread/types.d.ts +58 -26
  92. package/dist/leader-thread/types.d.ts.map +1 -1
  93. package/dist/leader-thread/types.js +1 -3
  94. package/dist/leader-thread/types.js.map +1 -1
  95. package/dist/mutation.d.ts +9 -2
  96. package/dist/mutation.d.ts.map +1 -1
  97. package/dist/mutation.js +5 -5
  98. package/dist/mutation.js.map +1 -1
  99. package/dist/otel.d.ts +2 -0
  100. package/dist/otel.d.ts.map +1 -1
  101. package/dist/otel.js +5 -0
  102. package/dist/otel.js.map +1 -1
  103. package/dist/query-builder/api.d.ts +3 -3
  104. package/dist/query-builder/api.d.ts.map +1 -1
  105. package/dist/query-builder/impl.d.ts +4 -4
  106. package/dist/query-builder/impl.d.ts.map +1 -1
  107. package/dist/query-builder/impl.js.map +1 -1
  108. package/dist/query-builder/impl.test.js +16 -1
  109. package/dist/query-builder/impl.test.js.map +1 -1
  110. package/dist/query-info.d.ts +3 -3
  111. package/dist/query-info.d.ts.map +1 -1
  112. package/dist/rehydrate-from-mutationlog.d.ts +5 -5
  113. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  114. package/dist/rehydrate-from-mutationlog.js +23 -27
  115. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  116. package/dist/schema/EventId.d.ts +27 -16
  117. package/dist/schema/EventId.d.ts.map +1 -1
  118. package/dist/schema/EventId.js +36 -11
  119. package/dist/schema/EventId.js.map +1 -1
  120. package/dist/schema/EventId.test.d.ts +2 -0
  121. package/dist/schema/EventId.test.d.ts.map +1 -0
  122. package/dist/schema/EventId.test.js +11 -0
  123. package/dist/schema/EventId.test.js.map +1 -0
  124. package/dist/schema/MutationEvent.d.ts +76 -82
  125. package/dist/schema/MutationEvent.d.ts.map +1 -1
  126. package/dist/schema/MutationEvent.js +53 -20
  127. package/dist/schema/MutationEvent.js.map +1 -1
  128. package/dist/schema/db-schema/ast/sqlite.d.ts +69 -0
  129. package/dist/schema/db-schema/ast/sqlite.d.ts.map +1 -0
  130. package/dist/schema/db-schema/ast/sqlite.js +71 -0
  131. package/dist/schema/db-schema/ast/sqlite.js.map +1 -0
  132. package/dist/schema/db-schema/ast/validate.d.ts +3 -0
  133. package/dist/schema/db-schema/ast/validate.d.ts.map +1 -0
  134. package/dist/schema/db-schema/ast/validate.js +12 -0
  135. package/dist/schema/db-schema/ast/validate.js.map +1 -0
  136. package/dist/schema/db-schema/dsl/field-defs.d.ts +90 -0
  137. package/dist/schema/db-schema/dsl/field-defs.d.ts.map +1 -0
  138. package/dist/schema/db-schema/dsl/field-defs.js +87 -0
  139. package/dist/schema/db-schema/dsl/field-defs.js.map +1 -0
  140. package/dist/schema/db-schema/dsl/field-defs.test.d.ts +2 -0
  141. package/dist/schema/db-schema/dsl/field-defs.test.d.ts.map +1 -0
  142. package/dist/schema/db-schema/dsl/field-defs.test.js +29 -0
  143. package/dist/schema/db-schema/dsl/field-defs.test.js.map +1 -0
  144. package/dist/schema/db-schema/dsl/mod.d.ts +88 -0
  145. package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -0
  146. package/dist/schema/db-schema/dsl/mod.js +35 -0
  147. package/dist/schema/db-schema/dsl/mod.js.map +1 -0
  148. package/dist/schema/db-schema/hash.d.ts +2 -0
  149. package/dist/schema/db-schema/hash.d.ts.map +1 -0
  150. package/dist/schema/db-schema/hash.js +14 -0
  151. package/dist/schema/db-schema/hash.js.map +1 -0
  152. package/dist/schema/db-schema/mod.d.ts +3 -0
  153. package/dist/schema/db-schema/mod.d.ts.map +1 -0
  154. package/dist/schema/db-schema/mod.js +3 -0
  155. package/dist/schema/db-schema/mod.js.map +1 -0
  156. package/dist/schema/mod.d.ts +1 -0
  157. package/dist/schema/mod.d.ts.map +1 -1
  158. package/dist/schema/mod.js +1 -0
  159. package/dist/schema/mod.js.map +1 -1
  160. package/dist/schema/mutations.d.ts +8 -9
  161. package/dist/schema/mutations.d.ts.map +1 -1
  162. package/dist/schema/mutations.js +2 -2
  163. package/dist/schema/mutations.js.map +1 -1
  164. package/dist/schema/schema-helpers.d.ts.map +1 -1
  165. package/dist/schema/schema-helpers.js +1 -1
  166. package/dist/schema/schema-helpers.js.map +1 -1
  167. package/dist/schema/schema.d.ts +5 -2
  168. package/dist/schema/schema.d.ts.map +1 -1
  169. package/dist/schema/schema.js +20 -9
  170. package/dist/schema/schema.js.map +1 -1
  171. package/dist/schema/system-tables.d.ts +65 -47
  172. package/dist/schema/system-tables.d.ts.map +1 -1
  173. package/dist/schema/system-tables.js +24 -13
  174. package/dist/schema/system-tables.js.map +1 -1
  175. package/dist/schema/table-def.d.ts +18 -24
  176. package/dist/schema/table-def.d.ts.map +1 -1
  177. package/dist/schema/table-def.js +3 -4
  178. package/dist/schema/table-def.js.map +1 -1
  179. package/dist/schema-management/common.d.ts +3 -3
  180. package/dist/schema-management/common.d.ts.map +1 -1
  181. package/dist/schema-management/common.js.map +1 -1
  182. package/dist/schema-management/migrations.d.ts +6 -6
  183. package/dist/schema-management/migrations.d.ts.map +1 -1
  184. package/dist/schema-management/migrations.js +13 -8
  185. package/dist/schema-management/migrations.js.map +1 -1
  186. package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -1
  187. package/dist/schema-management/validate-mutation-defs.js +2 -2
  188. package/dist/schema-management/validate-mutation-defs.js.map +1 -1
  189. package/dist/sql-queries/misc.d.ts.map +1 -1
  190. package/dist/sql-queries/sql-queries.d.ts +1 -1
  191. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  192. package/dist/sql-queries/sql-queries.js.map +1 -1
  193. package/dist/sql-queries/sql-query-builder.d.ts +1 -1
  194. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  195. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  196. package/dist/sql-queries/types.d.ts +2 -1
  197. package/dist/sql-queries/types.d.ts.map +1 -1
  198. package/dist/sql-queries/types.js.map +1 -1
  199. package/dist/sync/{client-session-sync-processor.d.ts → ClientSessionSyncProcessor.d.ts} +25 -14
  200. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -0
  201. package/dist/sync/ClientSessionSyncProcessor.js +199 -0
  202. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -0
  203. package/dist/sync/index.d.ts +1 -1
  204. package/dist/sync/index.d.ts.map +1 -1
  205. package/dist/sync/index.js +1 -1
  206. package/dist/sync/index.js.map +1 -1
  207. package/dist/sync/next/compact-events.d.ts.map +1 -1
  208. package/dist/sync/next/facts.d.ts.map +1 -1
  209. package/dist/sync/next/facts.js +1 -1
  210. package/dist/sync/next/facts.js.map +1 -1
  211. package/dist/sync/next/history-dag-common.d.ts +3 -4
  212. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  213. package/dist/sync/next/history-dag-common.js +3 -1
  214. package/dist/sync/next/history-dag-common.js.map +1 -1
  215. package/dist/sync/next/history-dag.d.ts.map +1 -1
  216. package/dist/sync/next/history-dag.js +1 -1
  217. package/dist/sync/next/history-dag.js.map +1 -1
  218. package/dist/sync/next/rebase-events.d.ts +6 -4
  219. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  220. package/dist/sync/next/rebase-events.js +6 -3
  221. package/dist/sync/next/rebase-events.js.map +1 -1
  222. package/dist/sync/next/test/compact-events.calculator.test.js +12 -12
  223. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  224. package/dist/sync/next/test/compact-events.test.js +43 -43
  225. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  226. package/dist/sync/next/test/mutation-fixtures.d.ts +4 -4
  227. package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -1
  228. package/dist/sync/next/test/mutation-fixtures.js +12 -16
  229. package/dist/sync/next/test/mutation-fixtures.js.map +1 -1
  230. package/dist/sync/sync.d.ts +31 -16
  231. package/dist/sync/sync.d.ts.map +1 -1
  232. package/dist/sync/sync.js +7 -3
  233. package/dist/sync/sync.js.map +1 -1
  234. package/dist/sync/syncstate.d.ts +177 -44
  235. package/dist/sync/syncstate.d.ts.map +1 -1
  236. package/dist/sync/syncstate.js +188 -56
  237. package/dist/sync/syncstate.js.map +1 -1
  238. package/dist/sync/syncstate.test.js +162 -92
  239. package/dist/sync/syncstate.test.js.map +1 -1
  240. package/dist/sync/validate-push-payload.d.ts +2 -2
  241. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  242. package/dist/sync/validate-push-payload.js +2 -2
  243. package/dist/sync/validate-push-payload.js.map +1 -1
  244. package/dist/util.d.ts +2 -2
  245. package/dist/util.d.ts.map +1 -1
  246. package/dist/version.d.ts +1 -1
  247. package/dist/version.d.ts.map +1 -1
  248. package/dist/version.js +1 -1
  249. package/dist/version.js.map +1 -1
  250. package/package.json +6 -6
  251. package/src/adapter-types.ts +80 -56
  252. package/src/derived-mutations.test.ts +1 -1
  253. package/src/derived-mutations.ts +13 -9
  254. package/src/devtools/devtools-messages-client-session.ts +141 -0
  255. package/src/devtools/devtools-messages-common.ts +106 -0
  256. package/src/devtools/devtools-messages-leader.ts +192 -0
  257. package/src/devtools/devtools-messages.ts +3 -243
  258. package/src/devtools/devtools-sessioninfo.ts +99 -0
  259. package/src/devtools/mod.ts +36 -0
  260. package/src/index.ts +2 -8
  261. package/src/init-singleton-tables.ts +2 -2
  262. package/src/leader-thread/LeaderSyncProcessor.ts +833 -0
  263. package/src/leader-thread/apply-mutation.ts +87 -43
  264. package/src/leader-thread/connection.ts +54 -9
  265. package/src/leader-thread/leader-worker-devtools.ts +199 -174
  266. package/src/leader-thread/make-leader-thread-layer.ts +89 -35
  267. package/src/leader-thread/mutationlog.ts +20 -14
  268. package/src/leader-thread/pull-queue-set.ts +10 -1
  269. package/src/leader-thread/recreate-db.ts +38 -25
  270. package/src/leader-thread/shutdown-channel.ts +2 -4
  271. package/src/leader-thread/types.ts +68 -34
  272. package/src/mutation.ts +17 -7
  273. package/src/otel.ts +8 -0
  274. package/src/query-builder/api.ts +4 -4
  275. package/src/query-builder/impl.test.ts +22 -1
  276. package/src/query-builder/impl.ts +2 -2
  277. package/src/query-info.ts +3 -3
  278. package/src/rehydrate-from-mutationlog.ts +28 -34
  279. package/src/schema/EventId.test.ts +12 -0
  280. package/src/schema/EventId.ts +49 -15
  281. package/src/schema/MutationEvent.ts +78 -31
  282. package/src/schema/db-schema/ast/sqlite.ts +142 -0
  283. package/src/schema/db-schema/ast/validate.ts +13 -0
  284. package/src/schema/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +206 -0
  285. package/src/schema/db-schema/dsl/field-defs.test.ts +35 -0
  286. package/src/schema/db-schema/dsl/field-defs.ts +242 -0
  287. package/src/schema/db-schema/dsl/mod.ts +195 -0
  288. package/src/schema/db-schema/hash.ts +14 -0
  289. package/src/schema/db-schema/mod.ts +2 -0
  290. package/src/schema/mod.ts +1 -0
  291. package/src/schema/mutations.ts +10 -20
  292. package/src/schema/schema-helpers.ts +1 -1
  293. package/src/schema/schema.ts +22 -10
  294. package/src/schema/system-tables.ts +24 -13
  295. package/src/schema/table-def.ts +17 -17
  296. package/src/schema-management/common.ts +3 -3
  297. package/src/schema-management/migrations.ts +19 -15
  298. package/src/schema-management/validate-mutation-defs.ts +2 -2
  299. package/src/sql-queries/sql-queries.ts +1 -1
  300. package/src/sql-queries/sql-query-builder.ts +1 -2
  301. package/src/sql-queries/types.ts +3 -1
  302. package/src/sync/ClientSessionSyncProcessor.ts +313 -0
  303. package/src/sync/index.ts +1 -1
  304. package/src/sync/next/facts.ts +1 -1
  305. package/src/sync/next/history-dag-common.ts +5 -1
  306. package/src/sync/next/history-dag.ts +1 -1
  307. package/src/sync/next/rebase-events.ts +13 -7
  308. package/src/sync/next/test/compact-events.calculator.test.ts +12 -12
  309. package/src/sync/next/test/compact-events.test.ts +43 -43
  310. package/src/sync/next/test/mutation-fixtures.ts +16 -19
  311. package/src/sync/sync.ts +26 -10
  312. package/src/sync/syncstate.test.ts +178 -98
  313. package/src/sync/syncstate.ts +170 -83
  314. package/src/sync/validate-push-payload.ts +7 -4
  315. package/src/version.ts +1 -1
  316. package/tmp/pack.tgz +0 -0
  317. package/tsconfig.json +1 -1
  318. package/dist/devtools/devtools-bridge.d.ts +0 -12
  319. package/dist/devtools/devtools-bridge.d.ts.map +0 -1
  320. package/dist/devtools/devtools-bridge.js +0 -2
  321. package/dist/devtools/devtools-bridge.js.map +0 -1
  322. package/dist/leader-thread/leader-sync-processor.d.ts +0 -47
  323. package/dist/leader-thread/leader-sync-processor.d.ts.map +0 -1
  324. package/dist/leader-thread/leader-sync-processor.js +0 -422
  325. package/dist/leader-thread/leader-sync-processor.js.map +0 -1
  326. package/dist/sync/client-session-sync-processor.d.ts.map +0 -1
  327. package/dist/sync/client-session-sync-processor.js +0 -131
  328. package/dist/sync/client-session-sync-processor.js.map +0 -1
  329. package/src/devtools/devtools-bridge.ts +0 -13
  330. package/src/devtools/index.ts +0 -48
  331. package/src/leader-thread/leader-sync-processor.ts +0 -666
  332. package/src/sync/client-session-sync-processor.ts +0 -207
package/dist/index.js CHANGED
@@ -8,11 +8,11 @@ export * from './rehydrate-from-mutationlog.js';
8
8
  export * from './query-info.js';
9
9
  export * from './derived-mutations.js';
10
10
  export * from './sync/index.js';
11
- export * as Devtools from './devtools/index.js';
11
+ export * as Devtools from './devtools/mod.js';
12
12
  export * from './debug-info.js';
13
13
  export * from './bounded-collections.js';
14
14
  export * from './version.js';
15
15
  export * from './query-builder/mod.js';
16
- export * from './sync/syncstate.js';
16
+ export * as SyncState from './sync/syncstate.js';
17
17
  export * from './otel.js';
18
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,cAAc,mCAAmC,CAAA;AACjD,cAAc,eAAe,CAAA;AAC7B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAA;AAC/C,cAAc,iBAAiB,CAAA;AAC/B,cAAc,0BAA0B,CAAA;AACxC,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,cAAc,mCAAmC,CAAA;AACjD,cAAc,eAAe,CAAA;AAC7B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,cAAc,iBAAiB,CAAA;AAC/B,cAAc,0BAA0B,CAAA;AACxC,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,OAAO,KAAK,SAAS,MAAM,qBAAqB,CAAA;AAChD,cAAc,WAAW,CAAA"}
@@ -1,4 +1,4 @@
1
- import type { SynchronousDatabase } from './adapter-types.js';
1
+ import type { SqliteDb } from './adapter-types.js';
2
2
  import type { LiveStoreSchema } from './schema/mod.js';
3
- export declare const initializeSingletonTables: (schema: LiveStoreSchema, db: SynchronousDatabase) => void;
3
+ export declare const initializeSingletonTables: (schema: LiveStoreSchema, db: SqliteDb) => void;
4
4
  //# sourceMappingURL=init-singleton-tables.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init-singleton-tables.d.ts","sourceRoot":"","sources":["../src/init-singleton-tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAItD,eAAO,MAAM,yBAAyB,WAAY,eAAe,MAAM,mBAAmB,SAkBzF,CAAA"}
1
+ {"version":3,"file":"init-singleton-tables.d.ts","sourceRoot":"","sources":["../src/init-singleton-tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAItD,eAAO,MAAM,yBAAyB,GAAI,QAAQ,eAAe,EAAE,IAAI,QAAQ,SAkB9E,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"init-singleton-tables.js","sourceRoot":"","sources":["../src/init-singleton-tables.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAElD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,MAAuB,EAAE,EAAuB,EAAE,EAAE;IAC5F,KAAK,MAAM,CAAC,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACzC,IAAI,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;YAElE,MAAM,kBAAkB,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAA;YAChE,MAAM,YAAY,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAE5E,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAA;YACzC,MAAM,WAAW,GAAG,GAAG,CAAA,eAAe,SAAS,KAAK,kBAAkB,CAAC,IAAI,CACzE,IAAI,CACL,YAAY,YAAY,mCAAmC,SAAS,0BAA0B,CAAA;YAE/F,MAAM,UAAU,GAAG,iBAAiB,CAAC,EAAE,GAAG,aAAa,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,WAAW,CAAC,CAAA;YAExF,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QACrC,CAAC;IACH,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"init-singleton-tables.js","sourceRoot":"","sources":["../src/init-singleton-tables.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAElD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,MAAuB,EAAE,EAAY,EAAE,EAAE;IACjF,KAAK,MAAM,CAAC,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACzC,IAAI,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;YAElE,MAAM,kBAAkB,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAA;YAChE,MAAM,YAAY,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAE5E,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAA;YACzC,MAAM,WAAW,GAAG,GAAG,CAAA,eAAe,SAAS,KAAK,kBAAkB,CAAC,IAAI,CACzE,IAAI,CACL,YAAY,YAAY,mCAAmC,SAAS,0BAA0B,CAAA;YAE/F,MAAM,UAAU,GAAG,iBAAiB,CAAC,EAAE,GAAG,aAAa,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,WAAW,CAAC,CAAA;YAExF,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QACrC,CAAC;IACH,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,39 @@
1
+ import type { Scope } from '@livestore/utils/effect';
2
+ import { Effect } from '@livestore/utils/effect';
3
+ import type { SqliteDb } from '../adapter-types.js';
4
+ import { UnexpectedError } from '../adapter-types.js';
5
+ import type { LiveStoreSchema } from '../schema/mod.js';
6
+ import type { InitialBlockingSyncContext, LeaderSyncProcessor } from './types.js';
7
+ export declare const BACKEND_PUSH_BATCH_SIZE = 50;
8
+ /**
9
+ * The LeaderSyncProcessor manages synchronization of mutations between
10
+ * the local state and the sync backend, ensuring efficient and orderly processing.
11
+ *
12
+ * In the LeaderSyncProcessor, pulling always has precedence over pushing.
13
+ *
14
+ * Responsibilities:
15
+ * - Queueing incoming local mutations in a localPushMailbox.
16
+ * - Broadcasting mutations to client sessions via pull queues.
17
+ * - Pushing mutations to the sync backend.
18
+ *
19
+ * Notes:
20
+ *
21
+ * local push processing:
22
+ * - localPushMailbox:
23
+ * - Maintains events in ascending order.
24
+ * - Uses `Deferred` objects to resolve/reject events based on application success.
25
+ * - Processes events from the mailbox, applying mutations in batches.
26
+ * - Controlled by a `Latch` to manage execution flow.
27
+ * - The latch closes on pull receipt and re-opens post-pull completion.
28
+ * - Processes up to `maxBatchSize` events per cycle.
29
+ *
30
+ */
31
+ export declare const makeLeaderSyncProcessor: ({ schema, dbMissing, dbMutationLog, clientId, initialBlockingSyncContext, }: {
32
+ schema: LiveStoreSchema;
33
+ /** Only used to know whether we can safely query dbMutationLog during setup execution */
34
+ dbMissing: boolean;
35
+ dbMutationLog: SqliteDb;
36
+ clientId: string;
37
+ initialBlockingSyncContext: InitialBlockingSyncContext;
38
+ }) => Effect.Effect<LeaderSyncProcessor, UnexpectedError, Scope.Scope>;
39
+ //# sourceMappingURL=LeaderSyncProcessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LeaderSyncProcessor.d.ts","sourceRoot":"","sources":["../../src/leader-thread/LeaderSyncProcessor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,KAAK,EAAU,MAAM,yBAAyB,CAAA;AACxE,OAAO,EAGL,MAAM,EAUP,MAAM,yBAAyB,CAAA;AAGhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAA2B,MAAM,kBAAkB,CAAA;AAgBhF,OAAO,KAAK,EAAE,0BAA0B,EAAmB,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAGlG,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAQzC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,uBAAuB,GAAI,6EAMrC;IACD,MAAM,EAAE,eAAe,CAAA;IACvB,yFAAyF;IACzF,SAAS,EAAE,OAAO,CAAA;IAClB,aAAa,EAAE,QAAQ,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,0BAA0B,EAAE,0BAA0B,CAAA;CACvD,KAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,eAAe,EAAE,KAAK,CAAC,KAAK,CAqO/D,CAAA"}
@@ -0,0 +1,527 @@
1
+ import { casesHandled, isNotUndefined, LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils';
2
+ import { BucketQueue, Deferred, Effect, Exit, FiberHandle, Option, OtelTracer, ReadonlyArray, Schema, Stream, Subscribable, SubscriptionRef, } from '@livestore/utils/effect';
3
+ import { UnexpectedError } from '../adapter-types.js';
4
+ import { EventId, getMutationDef, MUTATION_LOG_META_TABLE, MutationEvent, mutationLogMetaTable, SESSION_CHANGESET_META_TABLE, } from '../schema/mod.js';
5
+ import { updateRows } from '../sql-queries/index.js';
6
+ import { LeaderAheadError } from '../sync/sync.js';
7
+ import * as SyncState from '../sync/syncstate.js';
8
+ import { sql } from '../util.js';
9
+ import { makeApplyMutation } from './apply-mutation.js';
10
+ import { execSql } from './connection.js';
11
+ import { getBackendHeadFromDb, getClientHeadFromDb, getMutationEventsSince, updateBackendHead } from './mutationlog.js';
12
+ import { LeaderThreadCtx } from './types.js';
13
+ export const BACKEND_PUSH_BATCH_SIZE = 50;
14
+ /**
15
+ * The LeaderSyncProcessor manages synchronization of mutations between
16
+ * the local state and the sync backend, ensuring efficient and orderly processing.
17
+ *
18
+ * In the LeaderSyncProcessor, pulling always has precedence over pushing.
19
+ *
20
+ * Responsibilities:
21
+ * - Queueing incoming local mutations in a localPushMailbox.
22
+ * - Broadcasting mutations to client sessions via pull queues.
23
+ * - Pushing mutations to the sync backend.
24
+ *
25
+ * Notes:
26
+ *
27
+ * local push processing:
28
+ * - localPushMailbox:
29
+ * - Maintains events in ascending order.
30
+ * - Uses `Deferred` objects to resolve/reject events based on application success.
31
+ * - Processes events from the mailbox, applying mutations in batches.
32
+ * - Controlled by a `Latch` to manage execution flow.
33
+ * - The latch closes on pull receipt and re-opens post-pull completion.
34
+ * - Processes up to `maxBatchSize` events per cycle.
35
+ *
36
+ */
37
+ export const makeLeaderSyncProcessor = ({ schema, dbMissing, dbMutationLog, clientId, initialBlockingSyncContext, }) => Effect.gen(function* () {
38
+ const syncBackendQueue = yield* BucketQueue.make();
39
+ const syncStateSref = yield* SubscriptionRef.make(undefined);
40
+ const isLocalEvent = (mutationEventEncoded) => {
41
+ const mutationDef = getMutationDef(schema, mutationEventEncoded.mutation);
42
+ return mutationDef.options.clientOnly;
43
+ };
44
+ /**
45
+ * Tracks generations of queued local push events.
46
+ * If a batch is rejected, all subsequent push queue items with the same generation are also rejected,
47
+ * even if they would be valid on their own.
48
+ */
49
+ const currentLocalPushGenerationRef = { current: 0 };
50
+ // This context depends on data from `boot`, we should find a better implementation to avoid this ref indirection.
51
+ const ctxRef = {
52
+ current: undefined,
53
+ };
54
+ const localPushesQueue = yield* BucketQueue.make();
55
+ const localPushesLatch = yield* Effect.makeLatch(true);
56
+ const pullLatch = yield* Effect.makeLatch(true);
57
+ const push = (newEvents, options) => Effect.gen(function* () {
58
+ // TODO validate batch
59
+ if (newEvents.length === 0)
60
+ return;
61
+ // if (options.generation < currentLocalPushGenerationRef.current) {
62
+ // debugger
63
+ // // We can safely drop this batch as it's from a previous push generation
64
+ // return
65
+ // }
66
+ if (clientId === 'client-b') {
67
+ // console.log(
68
+ // 'push from client session',
69
+ // newEvents.map((item) => item.toJSON()),
70
+ // )
71
+ }
72
+ const waitForProcessing = options?.waitForProcessing ?? false;
73
+ const generation = currentLocalPushGenerationRef.current;
74
+ if (waitForProcessing) {
75
+ const deferreds = yield* Effect.forEach(newEvents, () => Deferred.make());
76
+ const items = newEvents.map((mutationEventEncoded, i) => [mutationEventEncoded, deferreds[i], generation]);
77
+ yield* BucketQueue.offerAll(localPushesQueue, items);
78
+ yield* Effect.all(deferreds);
79
+ }
80
+ else {
81
+ const items = newEvents.map((mutationEventEncoded) => [mutationEventEncoded, undefined, generation]);
82
+ yield* BucketQueue.offerAll(localPushesQueue, items);
83
+ }
84
+ }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:local-push', {
85
+ attributes: {
86
+ batchSize: newEvents.length,
87
+ batch: TRACE_VERBOSE ? newEvents : undefined,
88
+ },
89
+ links: ctxRef.current?.span ? [{ _tag: 'SpanLink', span: ctxRef.current.span, attributes: {} }] : undefined,
90
+ }));
91
+ const pushPartial = ({ mutationEvent: partialMutationEvent, clientId, sessionId, }) => Effect.gen(function* () {
92
+ const syncState = yield* syncStateSref;
93
+ if (syncState === undefined)
94
+ return shouldNeverHappen('Not initialized');
95
+ const mutationDef = getMutationDef(schema, partialMutationEvent.mutation);
96
+ const mutationEventEncoded = new MutationEvent.EncodedWithMeta({
97
+ ...partialMutationEvent,
98
+ clientId,
99
+ sessionId,
100
+ ...EventId.nextPair(syncState.localHead, mutationDef.options.clientOnly),
101
+ });
102
+ yield* push([mutationEventEncoded]);
103
+ }).pipe(Effect.catchTag('LeaderAheadError', Effect.orDie));
104
+ // Starts various background loops
105
+ const boot = ({ dbReady }) => Effect.gen(function* () {
106
+ const span = yield* Effect.currentSpan.pipe(Effect.orDie);
107
+ const otelSpan = yield* OtelTracer.currentOtelSpan.pipe(Effect.catchAll(() => Effect.succeed(undefined)));
108
+ const { devtools, shutdownChannel } = yield* LeaderThreadCtx;
109
+ ctxRef.current = {
110
+ otelSpan,
111
+ span,
112
+ devtoolsLatch: devtools.enabled ? devtools.syncBackendLatch : undefined,
113
+ };
114
+ const initialBackendHead = dbMissing ? EventId.ROOT.global : getBackendHeadFromDb(dbMutationLog);
115
+ const initialLocalHead = dbMissing ? EventId.ROOT : getClientHeadFromDb(dbMutationLog);
116
+ if (initialBackendHead > initialLocalHead.global) {
117
+ return shouldNeverHappen(`During boot the backend head (${initialBackendHead}) should never be greater than the local head (${initialLocalHead.global})`);
118
+ }
119
+ const pendingMutationEvents = yield* getMutationEventsSince({
120
+ global: initialBackendHead,
121
+ client: EventId.clientDefault,
122
+ }).pipe(Effect.map(ReadonlyArray.map((_) => new MutationEvent.EncodedWithMeta(_))));
123
+ const initialSyncState = new SyncState.SyncState({
124
+ pending: pendingMutationEvents,
125
+ // On the leader we don't need a rollback tail beyond `pending` items
126
+ rollbackTail: [],
127
+ upstreamHead: { global: initialBackendHead, client: EventId.clientDefault },
128
+ localHead: initialLocalHead,
129
+ });
130
+ /** State transitions need to happen atomically, so we use a Ref to track the state */
131
+ yield* SubscriptionRef.set(syncStateSref, initialSyncState);
132
+ // Rehydrate sync queue
133
+ if (pendingMutationEvents.length > 0) {
134
+ const filteredBatch = pendingMutationEvents
135
+ // Don't sync clientOnly mutations
136
+ .filter((mutationEventEncoded) => {
137
+ const mutationDef = getMutationDef(schema, mutationEventEncoded.mutation);
138
+ return mutationDef.options.clientOnly === false;
139
+ });
140
+ yield* BucketQueue.offerAll(syncBackendQueue, filteredBatch);
141
+ }
142
+ const shutdownOnError = (cause) => Effect.gen(function* () {
143
+ yield* shutdownChannel.send(UnexpectedError.make({ cause }));
144
+ yield* Effect.die(cause);
145
+ });
146
+ yield* backgroundApplyLocalPushes({
147
+ localPushesLatch,
148
+ localPushesQueue,
149
+ pullLatch,
150
+ syncStateSref,
151
+ syncBackendQueue,
152
+ schema,
153
+ isLocalEvent,
154
+ otelSpan,
155
+ currentLocalPushGenerationRef,
156
+ }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError), Effect.forkScoped);
157
+ const backendPushingFiberHandle = yield* FiberHandle.make();
158
+ yield* FiberHandle.run(backendPushingFiberHandle, backgroundBackendPushing({
159
+ dbReady,
160
+ syncBackendQueue,
161
+ otelSpan,
162
+ devtoolsLatch: ctxRef.current?.devtoolsLatch,
163
+ }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError)));
164
+ yield* backgroundBackendPulling({
165
+ dbReady,
166
+ initialBackendHead,
167
+ isLocalEvent,
168
+ restartBackendPushing: (filteredRebasedPending) => Effect.gen(function* () {
169
+ // Stop current pushing fiber
170
+ yield* FiberHandle.clear(backendPushingFiberHandle);
171
+ // Reset the sync queue
172
+ yield* BucketQueue.clear(syncBackendQueue);
173
+ yield* BucketQueue.offerAll(syncBackendQueue, filteredRebasedPending);
174
+ // Restart pushing fiber
175
+ yield* FiberHandle.run(backendPushingFiberHandle, backgroundBackendPushing({
176
+ dbReady,
177
+ syncBackendQueue,
178
+ otelSpan,
179
+ devtoolsLatch: ctxRef.current?.devtoolsLatch,
180
+ }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError)));
181
+ }),
182
+ syncStateSref,
183
+ localPushesLatch,
184
+ pullLatch,
185
+ otelSpan,
186
+ initialBlockingSyncContext,
187
+ devtoolsLatch: ctxRef.current?.devtoolsLatch,
188
+ }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError), Effect.forkScoped);
189
+ return { initialLeaderHead: initialLocalHead };
190
+ }).pipe(Effect.withSpanScoped('@livestore/common:leader-thread:syncing'));
191
+ return {
192
+ push,
193
+ pushPartial,
194
+ boot,
195
+ syncState: Subscribable.make({
196
+ get: Effect.gen(function* () {
197
+ const syncState = yield* syncStateSref;
198
+ if (syncState === undefined)
199
+ return shouldNeverHappen('Not initialized');
200
+ return syncState;
201
+ }),
202
+ changes: syncStateSref.changes.pipe(Stream.filter(isNotUndefined)),
203
+ }),
204
+ };
205
+ });
206
+ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLatch, syncStateSref, syncBackendQueue, schema, isLocalEvent, otelSpan, currentLocalPushGenerationRef, }) => Effect.gen(function* () {
207
+ const { connectedClientSessionPullQueues, clientId } = yield* LeaderThreadCtx;
208
+ const applyMutationItems = yield* makeApplyMutationItems;
209
+ while (true) {
210
+ // TODO make batch size configurable
211
+ const batchItems = yield* BucketQueue.takeBetween(localPushesQueue, 1, 10);
212
+ // Wait for the backend pulling to finish
213
+ yield* localPushesLatch.await;
214
+ // Prevent backend pull processing until this local push is finished
215
+ yield* pullLatch.close;
216
+ // Since the generation might have changed since enqueuing, we need to filter out items with older generation
217
+ // It's important that we filter after we got localPushesLatch, otherwise we might filter with the old generation
218
+ const filteredBatchItems = batchItems
219
+ .filter(([_1, _2, generation]) => generation === currentLocalPushGenerationRef.current)
220
+ .map(([mutationEventEncoded, deferred]) => [mutationEventEncoded, deferred]);
221
+ if (filteredBatchItems.length === 0) {
222
+ // console.log('dropping old-gen batch', currentLocalPushGenerationRef.current)
223
+ // Allow the backend pulling to start
224
+ yield* pullLatch.open;
225
+ continue;
226
+ }
227
+ const [newEvents, deferreds] = ReadonlyArray.unzip(filteredBatchItems);
228
+ const syncState = yield* syncStateSref;
229
+ if (syncState === undefined)
230
+ return shouldNeverHappen('Not initialized');
231
+ const updateResult = SyncState.updateSyncState({
232
+ syncState,
233
+ payload: { _tag: 'local-push', newEvents },
234
+ isLocalEvent,
235
+ isEqualEvent: MutationEvent.isEqualEncoded,
236
+ });
237
+ switch (updateResult._tag) {
238
+ case 'unexpected-error': {
239
+ otelSpan?.addEvent('local-push:unexpected-error', {
240
+ batchSize: newEvents.length,
241
+ newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
242
+ });
243
+ return yield* Effect.fail(updateResult.cause);
244
+ }
245
+ case 'rebase': {
246
+ return shouldNeverHappen('The leader thread should never have to rebase due to a local push');
247
+ }
248
+ case 'reject': {
249
+ otelSpan?.addEvent('local-push:reject', {
250
+ batchSize: newEvents.length,
251
+ updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
252
+ });
253
+ /*
254
+
255
+ TODO: how to test this?
256
+ */
257
+ currentLocalPushGenerationRef.current++;
258
+ const nextGeneration = currentLocalPushGenerationRef.current;
259
+ const providedId = newEvents.at(0).id;
260
+ // All subsequent pushes with same generation should be rejected as well
261
+ // We're also handling the case where the localPushQueue already contains events
262
+ // from the next generation which we preserve in the queue
263
+ const remainingEventsMatchingGeneration = yield* BucketQueue.takeSplitWhere(localPushesQueue, (item) => item[2] >= nextGeneration);
264
+ if ((yield* BucketQueue.size(localPushesQueue)) > 0) {
265
+ console.log('localPushesQueue is not empty', yield* BucketQueue.size(localPushesQueue));
266
+ debugger;
267
+ }
268
+ const allDeferredsToReject = [
269
+ ...deferreds,
270
+ ...remainingEventsMatchingGeneration.map(([_, deferred]) => deferred),
271
+ ].filter(isNotUndefined);
272
+ yield* Effect.forEach(allDeferredsToReject, (deferred) => Deferred.fail(deferred, LeaderAheadError.make({
273
+ minimumExpectedId: updateResult.expectedMinimumId,
274
+ providedId,
275
+ // nextGeneration,
276
+ })));
277
+ // Allow the backend pulling to start
278
+ yield* pullLatch.open;
279
+ // In this case we're skipping state update and down/upstream processing
280
+ // We've cleared the local push queue and are now waiting for new local pushes / backend pulls
281
+ continue;
282
+ }
283
+ case 'advance': {
284
+ break;
285
+ }
286
+ default: {
287
+ casesHandled(updateResult);
288
+ }
289
+ }
290
+ yield* SubscriptionRef.set(syncStateSref, updateResult.newSyncState);
291
+ if (clientId === 'client-b') {
292
+ // yield* Effect.log('offer upstream-advance due to local-push')
293
+ // debugger
294
+ }
295
+ yield* connectedClientSessionPullQueues.offer({
296
+ payload: { _tag: 'upstream-advance', newEvents: updateResult.newEvents },
297
+ remaining: 0,
298
+ });
299
+ otelSpan?.addEvent('local-push', {
300
+ batchSize: newEvents.length,
301
+ updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
302
+ });
303
+ // Don't sync clientOnly mutations
304
+ const filteredBatch = updateResult.newEvents.filter((mutationEventEncoded) => {
305
+ const mutationDef = getMutationDef(schema, mutationEventEncoded.mutation);
306
+ return mutationDef.options.clientOnly === false;
307
+ });
308
+ yield* BucketQueue.offerAll(syncBackendQueue, filteredBatch);
309
+ yield* applyMutationItems({ batchItems: newEvents, deferreds });
310
+ // Allow the backend pulling to start
311
+ yield* pullLatch.open;
312
+ }
313
+ });
314
+ // TODO how to handle errors gracefully
315
+ const makeApplyMutationItems = Effect.gen(function* () {
316
+ const leaderThreadCtx = yield* LeaderThreadCtx;
317
+ const { dbReadModel: db, dbMutationLog } = leaderThreadCtx;
318
+ const applyMutation = yield* makeApplyMutation;
319
+ return ({ batchItems, deferreds }) => Effect.gen(function* () {
320
+ db.execute('BEGIN TRANSACTION', undefined); // Start the transaction
321
+ dbMutationLog.execute('BEGIN TRANSACTION', undefined); // Start the transaction
322
+ yield* Effect.addFinalizer((exit) => Effect.gen(function* () {
323
+ if (Exit.isSuccess(exit))
324
+ return;
325
+ // Rollback in case of an error
326
+ db.execute('ROLLBACK', undefined);
327
+ dbMutationLog.execute('ROLLBACK', undefined);
328
+ }));
329
+ for (let i = 0; i < batchItems.length; i++) {
330
+ yield* applyMutation(batchItems[i]);
331
+ if (deferreds?.[i] !== undefined) {
332
+ yield* Deferred.succeed(deferreds[i], void 0);
333
+ }
334
+ }
335
+ db.execute('COMMIT', undefined); // Commit the transaction
336
+ dbMutationLog.execute('COMMIT', undefined); // Commit the transaction
337
+ }).pipe(Effect.uninterruptible, Effect.scoped, Effect.withSpan('@livestore/common:leader-thread:syncing:applyMutationItems', {
338
+ attributes: { count: batchItems.length },
339
+ }), Effect.tapCauseLogPretty, UnexpectedError.mapToUnexpectedError);
340
+ });
341
+ const backgroundBackendPulling = ({ dbReady, initialBackendHead, isLocalEvent, restartBackendPushing, otelSpan, syncStateSref, localPushesLatch, pullLatch, devtoolsLatch, initialBlockingSyncContext, }) => Effect.gen(function* () {
342
+ const { syncBackend, dbReadModel: db, dbMutationLog, connectedClientSessionPullQueues, schema, clientId, } = yield* LeaderThreadCtx;
343
+ if (syncBackend === undefined)
344
+ return;
345
+ const cursorInfo = yield* getCursorInfo(initialBackendHead);
346
+ const applyMutationItems = yield* makeApplyMutationItems;
347
+ const onNewPullChunk = (newEvents, remaining) => Effect.gen(function* () {
348
+ if (newEvents.length === 0)
349
+ return;
350
+ if (devtoolsLatch !== undefined) {
351
+ yield* devtoolsLatch.await;
352
+ }
353
+ // Prevent more local pushes from being processed until this pull is finished
354
+ yield* localPushesLatch.close;
355
+ // Wait for pending local pushes to finish
356
+ yield* pullLatch.await;
357
+ const syncState = yield* syncStateSref;
358
+ if (syncState === undefined)
359
+ return shouldNeverHappen('Not initialized');
360
+ const trimRollbackUntil = newEvents.at(-1).id;
361
+ const updateResult = SyncState.updateSyncState({
362
+ syncState,
363
+ payload: { _tag: 'upstream-advance', newEvents, trimRollbackUntil },
364
+ isLocalEvent,
365
+ isEqualEvent: MutationEvent.isEqualEncoded,
366
+ ignoreLocalEvents: true,
367
+ });
368
+ if (updateResult._tag === 'reject') {
369
+ return shouldNeverHappen('The leader thread should never reject upstream advances');
370
+ }
371
+ else if (updateResult._tag === 'unexpected-error') {
372
+ otelSpan?.addEvent('backend-pull:unexpected-error', {
373
+ newEventsCount: newEvents.length,
374
+ newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
375
+ });
376
+ return yield* Effect.fail(updateResult.cause);
377
+ }
378
+ const newBackendHead = newEvents.at(-1).id;
379
+ updateBackendHead(dbMutationLog, newBackendHead);
380
+ if (updateResult._tag === 'rebase') {
381
+ otelSpan?.addEvent('backend-pull:rebase', {
382
+ newEventsCount: newEvents.length,
383
+ newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
384
+ rollbackCount: updateResult.eventsToRollback.length,
385
+ updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
386
+ });
387
+ const filteredRebasedPending = updateResult.newSyncState.pending.filter((mutationEvent) => {
388
+ const mutationDef = getMutationDef(schema, mutationEvent.mutation);
389
+ return mutationDef.options.clientOnly === false;
390
+ });
391
+ yield* restartBackendPushing(filteredRebasedPending);
392
+ if (updateResult.eventsToRollback.length > 0) {
393
+ yield* rollback({ db, dbMutationLog, eventIdsToRollback: updateResult.eventsToRollback.map((_) => _.id) });
394
+ }
395
+ yield* connectedClientSessionPullQueues.offer({
396
+ payload: {
397
+ _tag: 'upstream-rebase',
398
+ newEvents: updateResult.newEvents,
399
+ rollbackUntil: updateResult.eventsToRollback.at(0).id,
400
+ trimRollbackUntil,
401
+ },
402
+ remaining,
403
+ });
404
+ }
405
+ else {
406
+ otelSpan?.addEvent('backend-pull:advance', {
407
+ newEventsCount: newEvents.length,
408
+ updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
409
+ });
410
+ if (clientId === 'client-b') {
411
+ // yield* Effect.log('offer upstream-advance due to pull')
412
+ }
413
+ yield* connectedClientSessionPullQueues.offer({
414
+ payload: { _tag: 'upstream-advance', newEvents: updateResult.newEvents, trimRollbackUntil },
415
+ remaining,
416
+ });
417
+ }
418
+ trimChangesetRows(db, newBackendHead);
419
+ yield* applyMutationItems({ batchItems: updateResult.newEvents, deferreds: undefined });
420
+ yield* SubscriptionRef.set(syncStateSref, updateResult.newSyncState);
421
+ if (remaining === 0) {
422
+ // Allow local pushes to be processed again
423
+ yield* localPushesLatch.open;
424
+ }
425
+ });
426
+ yield* syncBackend.pull(cursorInfo).pipe(
427
+ // TODO only take from queue while connected
428
+ Stream.tap(({ batch, remaining }) => Effect.gen(function* () {
429
+ // yield* Effect.spanEvent('batch', {
430
+ // attributes: {
431
+ // batchSize: batch.length,
432
+ // batch: TRACE_VERBOSE ? batch : undefined,
433
+ // },
434
+ // })
435
+ // Wait for the db to be initially created
436
+ yield* dbReady;
437
+ // NOTE we only want to take process mutations when the sync backend is connected
438
+ // (e.g. needed for simulating being offline)
439
+ // TODO remove when there's a better way to handle this in stream above
440
+ yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true);
441
+ yield* onNewPullChunk(batch.map((_) => MutationEvent.EncodedWithMeta.fromGlobal(_.mutationEventEncoded)), remaining);
442
+ yield* initialBlockingSyncContext.update({ processed: batch.length, remaining });
443
+ })), Stream.runDrain, Effect.interruptible);
444
+ }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:backend-pulling'));
445
+ const rollback = ({ db, dbMutationLog, eventIdsToRollback, }) => Effect.gen(function* () {
446
+ const rollbackEvents = db
447
+ .select(sql `SELECT * FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`).join(', ')})`)
448
+ .map((_) => ({ id: { global: _.idGlobal, client: _.idClient }, changeset: _.changeset, debug: _.debug }))
449
+ .toSorted((a, b) => EventId.compare(a.id, b.id));
450
+ // Apply changesets in reverse order
451
+ for (let i = rollbackEvents.length - 1; i >= 0; i--) {
452
+ const { changeset } = rollbackEvents[i];
453
+ if (changeset !== null) {
454
+ db.makeChangeset(changeset).invert().apply();
455
+ }
456
+ }
457
+ const eventIdPairChunks = ReadonlyArray.chunksOf(100)(eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`));
458
+ // Delete the changeset rows
459
+ for (const eventIdPairChunk of eventIdPairChunks) {
460
+ db.execute(sql `DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`);
461
+ }
462
+ // Delete the mutation log rows
463
+ for (const eventIdPairChunk of eventIdPairChunks) {
464
+ dbMutationLog.execute(sql `DELETE FROM ${MUTATION_LOG_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`);
465
+ }
466
+ }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:rollback', {
467
+ attributes: { count: eventIdsToRollback.length },
468
+ }));
469
+ const getCursorInfo = (remoteHead) => Effect.gen(function* () {
470
+ const { dbMutationLog } = yield* LeaderThreadCtx;
471
+ if (remoteHead === EventId.ROOT.global)
472
+ return Option.none();
473
+ const MutationlogQuerySchema = Schema.Struct({
474
+ syncMetadataJson: Schema.parseJson(Schema.Option(Schema.JsonValue)),
475
+ }).pipe(Schema.pluck('syncMetadataJson'), Schema.Array, Schema.head);
476
+ const syncMetadataOption = yield* Effect.sync(() => dbMutationLog.select(sql `SELECT syncMetadataJson FROM ${MUTATION_LOG_META_TABLE} WHERE idGlobal = ${remoteHead} ORDER BY idClient ASC LIMIT 1`)).pipe(Effect.andThen(Schema.decode(MutationlogQuerySchema)), Effect.map(Option.flatten), Effect.orDie);
477
+ return Option.some({
478
+ cursor: { global: remoteHead, client: EventId.clientDefault },
479
+ metadata: syncMetadataOption,
480
+ });
481
+ }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:getCursorInfo', { attributes: { remoteHead } }));
482
+ const backgroundBackendPushing = ({ dbReady, syncBackendQueue, otelSpan, devtoolsLatch, }) => Effect.gen(function* () {
483
+ const { syncBackend, dbMutationLog } = yield* LeaderThreadCtx;
484
+ if (syncBackend === undefined)
485
+ return;
486
+ yield* dbReady;
487
+ while (true) {
488
+ yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true);
489
+ // TODO make batch size configurable
490
+ const queueItems = yield* BucketQueue.takeBetween(syncBackendQueue, 1, BACKEND_PUSH_BATCH_SIZE);
491
+ yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true);
492
+ if (devtoolsLatch !== undefined) {
493
+ yield* devtoolsLatch.await;
494
+ }
495
+ otelSpan?.addEvent('backend-push', {
496
+ batchSize: queueItems.length,
497
+ batch: TRACE_VERBOSE ? JSON.stringify(queueItems) : undefined,
498
+ });
499
+ // TODO handle push errors (should only happen during concurrent pull+push)
500
+ const pushResult = yield* syncBackend.push(queueItems.map((_) => _.toGlobal())).pipe(Effect.either);
501
+ if (pushResult._tag === 'Left') {
502
+ if (LS_DEV) {
503
+ yield* Effect.logDebug('handled backend-push-error', { error: pushResult.left.toString() });
504
+ }
505
+ otelSpan?.addEvent('backend-push-error', { error: pushResult.left.toString() });
506
+ // wait for interrupt caused by background pulling which will then restart pushing
507
+ return yield* Effect.never;
508
+ }
509
+ const { metadata } = pushResult.right;
510
+ // TODO try to do this in a single query
511
+ for (let i = 0; i < queueItems.length; i++) {
512
+ const mutationEventEncoded = queueItems[i];
513
+ yield* execSql(dbMutationLog, ...updateRows({
514
+ tableName: MUTATION_LOG_META_TABLE,
515
+ columns: mutationLogMetaTable.sqliteDef.columns,
516
+ where: { idGlobal: mutationEventEncoded.id.global, idClient: mutationEventEncoded.id.client },
517
+ updateValues: { syncMetadataJson: metadata[i] },
518
+ }));
519
+ }
520
+ }
521
+ }).pipe(Effect.interruptible, Effect.withSpan('@livestore/common:leader-thread:syncing:backend-pushing'));
522
+ const trimChangesetRows = (db, newHead) => {
523
+ // Since we're using the session changeset rows to query for the current head,
524
+ // we're keeping at least one row for the current head, and thus are using `<` instead of `<=`
525
+ db.execute(sql `DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE idGlobal < ${newHead.global}`);
526
+ };
527
+ //# sourceMappingURL=LeaderSyncProcessor.js.map