@livestore/common 0.3.0-dev.3 → 0.3.0-dev.30

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 (426) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +83 -221
  3. package/dist/__tests__/fixture.d.ts.map +1 -1
  4. package/dist/__tests__/fixture.js +33 -11
  5. package/dist/__tests__/fixture.js.map +1 -1
  6. package/dist/adapter-types.d.ts +128 -68
  7. package/dist/adapter-types.d.ts.map +1 -1
  8. package/dist/adapter-types.js +36 -7
  9. package/dist/adapter-types.js.map +1 -1
  10. package/dist/bounded-collections.d.ts.map +1 -1
  11. package/dist/debug-info.d.ts +1 -1
  12. package/dist/debug-info.d.ts.map +1 -1
  13. package/dist/debug-info.js +1 -0
  14. package/dist/debug-info.js.map +1 -1
  15. package/dist/devtools/devtools-messages-client-session.d.ts +389 -0
  16. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -0
  17. package/dist/devtools/devtools-messages-client-session.js +96 -0
  18. package/dist/devtools/devtools-messages-client-session.js.map +1 -0
  19. package/dist/devtools/devtools-messages-common.d.ts +68 -0
  20. package/dist/devtools/devtools-messages-common.d.ts.map +1 -0
  21. package/dist/devtools/devtools-messages-common.js +60 -0
  22. package/dist/devtools/devtools-messages-common.js.map +1 -0
  23. package/dist/devtools/devtools-messages-leader.d.ts +394 -0
  24. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -0
  25. package/dist/devtools/devtools-messages-leader.js +147 -0
  26. package/dist/devtools/devtools-messages-leader.js.map +1 -0
  27. package/dist/devtools/devtools-messages.d.ts +3 -592
  28. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  29. package/dist/devtools/devtools-messages.js +3 -171
  30. package/dist/devtools/devtools-messages.js.map +1 -1
  31. package/dist/devtools/devtools-sessioninfo.d.ts +28 -0
  32. package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -0
  33. package/dist/devtools/devtools-sessioninfo.js +34 -0
  34. package/dist/devtools/devtools-sessioninfo.js.map +1 -0
  35. package/dist/devtools/mod.d.ts +39 -0
  36. package/dist/devtools/mod.d.ts.map +1 -0
  37. package/dist/devtools/mod.js +27 -0
  38. package/dist/devtools/mod.js.map +1 -0
  39. package/dist/index.d.ts +4 -11
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +4 -7
  42. package/dist/index.js.map +1 -1
  43. package/dist/leader-thread/LeaderSyncProcessor.d.ts +62 -0
  44. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -0
  45. package/dist/leader-thread/LeaderSyncProcessor.js +589 -0
  46. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -0
  47. package/dist/leader-thread/apply-event.d.ts +16 -0
  48. package/dist/leader-thread/apply-event.d.ts.map +1 -0
  49. package/dist/leader-thread/apply-event.js +103 -0
  50. package/dist/leader-thread/apply-event.js.map +1 -0
  51. package/dist/leader-thread/connection.d.ts +34 -6
  52. package/dist/leader-thread/connection.d.ts.map +1 -1
  53. package/dist/leader-thread/connection.js +22 -7
  54. package/dist/leader-thread/connection.js.map +1 -1
  55. package/dist/leader-thread/eventlog.d.ts +27 -0
  56. package/dist/leader-thread/eventlog.d.ts.map +1 -0
  57. package/dist/leader-thread/eventlog.js +123 -0
  58. package/dist/leader-thread/eventlog.js.map +1 -0
  59. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  60. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  61. package/dist/leader-thread/leader-worker-devtools.js +154 -132
  62. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  63. package/dist/leader-thread/make-leader-thread-layer.d.ts +26 -12
  64. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  65. package/dist/leader-thread/make-leader-thread-layer.js +82 -47
  66. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  67. package/dist/leader-thread/mod.d.ts +1 -1
  68. package/dist/leader-thread/mod.d.ts.map +1 -1
  69. package/dist/leader-thread/mod.js +1 -1
  70. package/dist/leader-thread/mod.js.map +1 -1
  71. package/dist/leader-thread/recreate-db.d.ts +4 -2
  72. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  73. package/dist/leader-thread/recreate-db.js +35 -25
  74. package/dist/leader-thread/recreate-db.js.map +1 -1
  75. package/dist/leader-thread/shutdown-channel.d.ts +2 -5
  76. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  77. package/dist/leader-thread/shutdown-channel.js +2 -4
  78. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  79. package/dist/leader-thread/types.d.ts +86 -39
  80. package/dist/leader-thread/types.d.ts.map +1 -1
  81. package/dist/leader-thread/types.js +1 -3
  82. package/dist/leader-thread/types.js.map +1 -1
  83. package/dist/materializer-helper.d.ts +23 -0
  84. package/dist/materializer-helper.d.ts.map +1 -0
  85. package/dist/materializer-helper.js +70 -0
  86. package/dist/materializer-helper.js.map +1 -0
  87. package/dist/otel.d.ts +2 -0
  88. package/dist/otel.d.ts.map +1 -1
  89. package/dist/otel.js +5 -0
  90. package/dist/otel.js.map +1 -1
  91. package/dist/query-builder/api.d.ts +158 -55
  92. package/dist/query-builder/api.d.ts.map +1 -1
  93. package/dist/query-builder/api.js +3 -5
  94. package/dist/query-builder/api.js.map +1 -1
  95. package/dist/query-builder/astToSql.d.ts +7 -0
  96. package/dist/query-builder/astToSql.d.ts.map +1 -0
  97. package/dist/query-builder/astToSql.js +190 -0
  98. package/dist/query-builder/astToSql.js.map +1 -0
  99. package/dist/query-builder/impl.d.ts +3 -8
  100. package/dist/query-builder/impl.d.ts.map +1 -1
  101. package/dist/query-builder/impl.js +166 -124
  102. package/dist/query-builder/impl.js.map +1 -1
  103. package/dist/query-builder/impl.test.d.ts +86 -1
  104. package/dist/query-builder/impl.test.d.ts.map +1 -1
  105. package/dist/query-builder/impl.test.js +411 -69
  106. package/dist/query-builder/impl.test.js.map +1 -1
  107. package/dist/query-builder/mod.d.ts +7 -0
  108. package/dist/query-builder/mod.d.ts.map +1 -1
  109. package/dist/query-builder/mod.js +7 -0
  110. package/dist/query-builder/mod.js.map +1 -1
  111. package/dist/rehydrate-from-eventlog.d.ts +14 -0
  112. package/dist/rehydrate-from-eventlog.d.ts.map +1 -0
  113. package/dist/rehydrate-from-eventlog.js +65 -0
  114. package/dist/rehydrate-from-eventlog.js.map +1 -0
  115. package/dist/schema/EventDef.d.ts +136 -0
  116. package/dist/schema/EventDef.d.ts.map +1 -0
  117. package/dist/schema/EventDef.js +58 -0
  118. package/dist/schema/EventDef.js.map +1 -0
  119. package/dist/schema/EventId.d.ts +35 -15
  120. package/dist/schema/EventId.d.ts.map +1 -1
  121. package/dist/schema/EventId.js +57 -11
  122. package/dist/schema/EventId.js.map +1 -1
  123. package/dist/schema/EventId.test.d.ts +2 -0
  124. package/dist/schema/EventId.test.d.ts.map +1 -0
  125. package/dist/schema/EventId.test.js +11 -0
  126. package/dist/schema/EventId.test.js.map +1 -0
  127. package/dist/schema/LiveStoreEvent.d.ts +255 -0
  128. package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
  129. package/dist/schema/LiveStoreEvent.js +118 -0
  130. package/dist/schema/LiveStoreEvent.js.map +1 -0
  131. package/dist/schema/client-document-def.d.ts +223 -0
  132. package/dist/schema/client-document-def.d.ts.map +1 -0
  133. package/dist/schema/client-document-def.js +170 -0
  134. package/dist/schema/client-document-def.js.map +1 -0
  135. package/dist/schema/client-document-def.test.d.ts +2 -0
  136. package/dist/schema/client-document-def.test.d.ts.map +1 -0
  137. package/dist/schema/client-document-def.test.js +201 -0
  138. package/dist/schema/client-document-def.test.js.map +1 -0
  139. package/dist/schema/db-schema/ast/sqlite.d.ts +69 -0
  140. package/dist/schema/db-schema/ast/sqlite.d.ts.map +1 -0
  141. package/dist/schema/db-schema/ast/sqlite.js +71 -0
  142. package/dist/schema/db-schema/ast/sqlite.js.map +1 -0
  143. package/dist/schema/db-schema/ast/validate.d.ts +3 -0
  144. package/dist/schema/db-schema/ast/validate.d.ts.map +1 -0
  145. package/dist/schema/db-schema/ast/validate.js +12 -0
  146. package/dist/schema/db-schema/ast/validate.js.map +1 -0
  147. package/dist/schema/db-schema/dsl/field-defs.d.ts +90 -0
  148. package/dist/schema/db-schema/dsl/field-defs.d.ts.map +1 -0
  149. package/dist/schema/db-schema/dsl/field-defs.js +87 -0
  150. package/dist/schema/db-schema/dsl/field-defs.js.map +1 -0
  151. package/dist/schema/db-schema/dsl/field-defs.test.d.ts +2 -0
  152. package/dist/schema/db-schema/dsl/field-defs.test.d.ts.map +1 -0
  153. package/dist/schema/db-schema/dsl/field-defs.test.js +29 -0
  154. package/dist/schema/db-schema/dsl/field-defs.test.js.map +1 -0
  155. package/dist/schema/db-schema/dsl/mod.d.ts +90 -0
  156. package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -0
  157. package/dist/schema/db-schema/dsl/mod.js +41 -0
  158. package/dist/schema/db-schema/dsl/mod.js.map +1 -0
  159. package/dist/schema/db-schema/hash.d.ts +2 -0
  160. package/dist/schema/db-schema/hash.d.ts.map +1 -0
  161. package/dist/schema/db-schema/hash.js +14 -0
  162. package/dist/schema/db-schema/hash.js.map +1 -0
  163. package/dist/schema/db-schema/mod.d.ts +3 -0
  164. package/dist/schema/db-schema/mod.d.ts.map +1 -0
  165. package/dist/schema/db-schema/mod.js +3 -0
  166. package/dist/schema/db-schema/mod.js.map +1 -0
  167. package/dist/schema/events.d.ts +2 -0
  168. package/dist/schema/events.d.ts.map +1 -0
  169. package/dist/schema/events.js +2 -0
  170. package/dist/schema/events.js.map +1 -0
  171. package/dist/schema/mod.d.ts +5 -3
  172. package/dist/schema/mod.d.ts.map +1 -1
  173. package/dist/schema/mod.js +5 -3
  174. package/dist/schema/mod.js.map +1 -1
  175. package/dist/schema/schema-helpers.d.ts.map +1 -1
  176. package/dist/schema/schema-helpers.js +1 -1
  177. package/dist/schema/schema-helpers.js.map +1 -1
  178. package/dist/schema/schema.d.ts +30 -23
  179. package/dist/schema/schema.d.ts.map +1 -1
  180. package/dist/schema/schema.js +48 -35
  181. package/dist/schema/schema.js.map +1 -1
  182. package/dist/schema/sqlite-state.d.ts +12 -0
  183. package/dist/schema/sqlite-state.d.ts.map +1 -0
  184. package/dist/schema/sqlite-state.js +36 -0
  185. package/dist/schema/sqlite-state.js.map +1 -0
  186. package/dist/schema/system-tables.d.ts +179 -125
  187. package/dist/schema/system-tables.d.ts.map +1 -1
  188. package/dist/schema/system-tables.js +76 -41
  189. package/dist/schema/system-tables.js.map +1 -1
  190. package/dist/schema/table-def.d.ts +37 -109
  191. package/dist/schema/table-def.d.ts.map +1 -1
  192. package/dist/schema/table-def.js +23 -66
  193. package/dist/schema/table-def.js.map +1 -1
  194. package/dist/schema/view.d.ts +3 -0
  195. package/dist/schema/view.d.ts.map +1 -0
  196. package/dist/schema/view.js +3 -0
  197. package/dist/schema/view.js.map +1 -0
  198. package/dist/schema-management/common.d.ts +7 -7
  199. package/dist/schema-management/common.d.ts.map +1 -1
  200. package/dist/schema-management/common.js.map +1 -1
  201. package/dist/schema-management/migrations.d.ts +6 -6
  202. package/dist/schema-management/migrations.d.ts.map +1 -1
  203. package/dist/schema-management/migrations.js +19 -14
  204. package/dist/schema-management/migrations.js.map +1 -1
  205. package/dist/schema-management/validate-mutation-defs.d.ts +3 -3
  206. package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -1
  207. package/dist/schema-management/validate-mutation-defs.js +17 -17
  208. package/dist/schema-management/validate-mutation-defs.js.map +1 -1
  209. package/dist/sql-queries/misc.d.ts.map +1 -1
  210. package/dist/sql-queries/sql-queries.d.ts +1 -1
  211. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  212. package/dist/sql-queries/sql-queries.js.map +1 -1
  213. package/dist/sql-queries/sql-query-builder.d.ts +1 -1
  214. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  215. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  216. package/dist/sql-queries/types.d.ts +2 -1
  217. package/dist/sql-queries/types.d.ts.map +1 -1
  218. package/dist/sql-queries/types.js.map +1 -1
  219. package/dist/sync/ClientSessionSyncProcessor.d.ts +66 -0
  220. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -0
  221. package/dist/sync/ClientSessionSyncProcessor.js +209 -0
  222. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -0
  223. package/dist/sync/index.d.ts +1 -1
  224. package/dist/sync/index.d.ts.map +1 -1
  225. package/dist/sync/index.js +1 -1
  226. package/dist/sync/index.js.map +1 -1
  227. package/dist/sync/next/compact-events.d.ts.map +1 -1
  228. package/dist/sync/next/facts.d.ts +19 -19
  229. package/dist/sync/next/facts.d.ts.map +1 -1
  230. package/dist/sync/next/facts.js +3 -3
  231. package/dist/sync/next/facts.js.map +1 -1
  232. package/dist/sync/next/history-dag-common.d.ts +6 -7
  233. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  234. package/dist/sync/next/history-dag-common.js +4 -2
  235. package/dist/sync/next/history-dag-common.js.map +1 -1
  236. package/dist/sync/next/history-dag.d.ts.map +1 -1
  237. package/dist/sync/next/history-dag.js +2 -2
  238. package/dist/sync/next/history-dag.js.map +1 -1
  239. package/dist/sync/next/rebase-events.d.ts +10 -8
  240. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  241. package/dist/sync/next/rebase-events.js +11 -8
  242. package/dist/sync/next/rebase-events.js.map +1 -1
  243. package/dist/sync/next/test/compact-events.calculator.test.js +38 -33
  244. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  245. package/dist/sync/next/test/compact-events.test.js +76 -76
  246. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  247. package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +29 -29
  248. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
  249. package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +67 -36
  250. package/dist/sync/next/test/event-fixtures.js.map +1 -0
  251. package/dist/sync/next/test/mod.d.ts +1 -1
  252. package/dist/sync/next/test/mod.d.ts.map +1 -1
  253. package/dist/sync/next/test/mod.js +1 -1
  254. package/dist/sync/next/test/mod.js.map +1 -1
  255. package/dist/sync/sync.d.ts +55 -20
  256. package/dist/sync/sync.d.ts.map +1 -1
  257. package/dist/sync/sync.js +7 -3
  258. package/dist/sync/sync.js.map +1 -1
  259. package/dist/sync/syncstate.d.ts +213 -82
  260. package/dist/sync/syncstate.d.ts.map +1 -1
  261. package/dist/sync/syncstate.js +319 -120
  262. package/dist/sync/syncstate.js.map +1 -1
  263. package/dist/sync/syncstate.test.js +295 -275
  264. package/dist/sync/syncstate.test.js.map +1 -1
  265. package/dist/sync/validate-push-payload.d.ts +2 -2
  266. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  267. package/dist/sync/validate-push-payload.js +2 -2
  268. package/dist/sync/validate-push-payload.js.map +1 -1
  269. package/dist/util.d.ts +2 -2
  270. package/dist/util.d.ts.map +1 -1
  271. package/dist/version.d.ts +1 -1
  272. package/dist/version.d.ts.map +1 -1
  273. package/dist/version.js +1 -1
  274. package/dist/version.js.map +1 -1
  275. package/package.json +6 -6
  276. package/src/__tests__/fixture.ts +36 -15
  277. package/src/adapter-types.ts +111 -74
  278. package/src/debug-info.ts +1 -0
  279. package/src/devtools/devtools-messages-client-session.ts +141 -0
  280. package/src/devtools/devtools-messages-common.ts +115 -0
  281. package/src/devtools/devtools-messages-leader.ts +191 -0
  282. package/src/devtools/devtools-messages.ts +3 -243
  283. package/src/devtools/devtools-sessioninfo.ts +99 -0
  284. package/src/devtools/mod.ts +36 -0
  285. package/src/index.ts +4 -13
  286. package/src/leader-thread/LeaderSyncProcessor.ts +935 -0
  287. package/src/leader-thread/apply-event.ts +173 -0
  288. package/src/leader-thread/connection.ts +54 -9
  289. package/src/leader-thread/eventlog.ts +199 -0
  290. package/src/leader-thread/leader-worker-devtools.ts +212 -189
  291. package/src/leader-thread/make-leader-thread-layer.ts +143 -77
  292. package/src/leader-thread/mod.ts +1 -1
  293. package/src/leader-thread/recreate-db.ts +41 -30
  294. package/src/leader-thread/shutdown-channel.ts +2 -4
  295. package/src/leader-thread/types.ts +95 -51
  296. package/src/materializer-helper.ts +110 -0
  297. package/src/otel.ts +8 -0
  298. package/src/query-builder/api.ts +236 -85
  299. package/src/query-builder/astToSql.ts +232 -0
  300. package/src/query-builder/impl.test.ts +447 -78
  301. package/src/query-builder/impl.ts +209 -144
  302. package/src/query-builder/mod.ts +7 -0
  303. package/src/rehydrate-from-eventlog.ts +114 -0
  304. package/src/schema/EventDef.ts +216 -0
  305. package/src/schema/EventId.test.ts +12 -0
  306. package/src/schema/EventId.ts +75 -15
  307. package/src/schema/LiveStoreEvent.ts +239 -0
  308. package/src/schema/client-document-def.test.ts +239 -0
  309. package/src/schema/client-document-def.ts +444 -0
  310. package/src/schema/db-schema/ast/sqlite.ts +142 -0
  311. package/src/schema/db-schema/ast/validate.ts +13 -0
  312. package/src/schema/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +206 -0
  313. package/src/schema/db-schema/dsl/field-defs.test.ts +35 -0
  314. package/src/schema/db-schema/dsl/field-defs.ts +242 -0
  315. package/src/schema/db-schema/dsl/mod.ts +222 -0
  316. package/src/schema/db-schema/hash.ts +14 -0
  317. package/src/schema/db-schema/mod.ts +2 -0
  318. package/src/schema/events.ts +1 -0
  319. package/src/schema/mod.ts +5 -3
  320. package/src/schema/schema-helpers.ts +1 -1
  321. package/src/schema/schema.ts +84 -62
  322. package/src/schema/sqlite-state.ts +62 -0
  323. package/src/schema/system-tables.ts +68 -50
  324. package/src/schema/table-def.ts +68 -214
  325. package/src/schema/view.ts +2 -0
  326. package/src/schema-management/common.ts +7 -7
  327. package/src/schema-management/migrations.ts +27 -24
  328. package/src/schema-management/validate-mutation-defs.ts +22 -24
  329. package/src/sql-queries/sql-queries.ts +1 -1
  330. package/src/sql-queries/sql-query-builder.ts +1 -2
  331. package/src/sql-queries/types.ts +3 -1
  332. package/src/sync/ClientSessionSyncProcessor.ts +332 -0
  333. package/src/sync/index.ts +1 -1
  334. package/src/sync/next/facts.ts +32 -33
  335. package/src/sync/next/history-dag-common.ts +9 -5
  336. package/src/sync/next/history-dag.ts +2 -2
  337. package/src/sync/next/rebase-events.ts +22 -16
  338. package/src/sync/next/test/compact-events.calculator.test.ts +45 -45
  339. package/src/sync/next/test/compact-events.test.ts +78 -78
  340. package/src/sync/next/test/event-fixtures.ts +219 -0
  341. package/src/sync/next/test/mod.ts +1 -1
  342. package/src/sync/sync.ts +51 -19
  343. package/src/sync/syncstate.test.ts +335 -308
  344. package/src/sync/syncstate.ts +394 -212
  345. package/src/sync/validate-push-payload.ts +7 -4
  346. package/src/version.ts +1 -1
  347. package/tmp/pack.tgz +0 -0
  348. package/tsconfig.json +2 -1
  349. package/dist/derived-mutations.d.ts +0 -109
  350. package/dist/derived-mutations.d.ts.map +0 -1
  351. package/dist/derived-mutations.js +0 -54
  352. package/dist/derived-mutations.js.map +0 -1
  353. package/dist/derived-mutations.test.d.ts +0 -2
  354. package/dist/derived-mutations.test.d.ts.map +0 -1
  355. package/dist/derived-mutations.test.js +0 -93
  356. package/dist/derived-mutations.test.js.map +0 -1
  357. package/dist/devtools/devtools-bridge.d.ts +0 -12
  358. package/dist/devtools/devtools-bridge.d.ts.map +0 -1
  359. package/dist/devtools/devtools-bridge.js +0 -2
  360. package/dist/devtools/devtools-bridge.js.map +0 -1
  361. package/dist/devtools/index.d.ts +0 -42
  362. package/dist/devtools/index.d.ts.map +0 -1
  363. package/dist/devtools/index.js +0 -48
  364. package/dist/devtools/index.js.map +0 -1
  365. package/dist/init-singleton-tables.d.ts +0 -4
  366. package/dist/init-singleton-tables.d.ts.map +0 -1
  367. package/dist/init-singleton-tables.js +0 -16
  368. package/dist/init-singleton-tables.js.map +0 -1
  369. package/dist/leader-thread/apply-mutation.d.ts +0 -8
  370. package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
  371. package/dist/leader-thread/apply-mutation.js +0 -95
  372. package/dist/leader-thread/apply-mutation.js.map +0 -1
  373. package/dist/leader-thread/leader-sync-processor.d.ts +0 -47
  374. package/dist/leader-thread/leader-sync-processor.d.ts.map +0 -1
  375. package/dist/leader-thread/leader-sync-processor.js +0 -422
  376. package/dist/leader-thread/leader-sync-processor.js.map +0 -1
  377. package/dist/leader-thread/mutationlog.d.ts +0 -23
  378. package/dist/leader-thread/mutationlog.d.ts.map +0 -1
  379. package/dist/leader-thread/mutationlog.js +0 -27
  380. package/dist/leader-thread/mutationlog.js.map +0 -1
  381. package/dist/leader-thread/pull-queue-set.d.ts +0 -7
  382. package/dist/leader-thread/pull-queue-set.d.ts.map +0 -1
  383. package/dist/leader-thread/pull-queue-set.js +0 -39
  384. package/dist/leader-thread/pull-queue-set.js.map +0 -1
  385. package/dist/mutation.d.ts +0 -13
  386. package/dist/mutation.d.ts.map +0 -1
  387. package/dist/mutation.js +0 -57
  388. package/dist/mutation.js.map +0 -1
  389. package/dist/query-info.d.ts +0 -38
  390. package/dist/query-info.d.ts.map +0 -1
  391. package/dist/query-info.js +0 -7
  392. package/dist/query-info.js.map +0 -1
  393. package/dist/rehydrate-from-mutationlog.d.ts +0 -14
  394. package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
  395. package/dist/rehydrate-from-mutationlog.js +0 -72
  396. package/dist/rehydrate-from-mutationlog.js.map +0 -1
  397. package/dist/schema/MutationEvent.d.ts +0 -191
  398. package/dist/schema/MutationEvent.d.ts.map +0 -1
  399. package/dist/schema/MutationEvent.js +0 -56
  400. package/dist/schema/MutationEvent.js.map +0 -1
  401. package/dist/schema/mutations.d.ts +0 -107
  402. package/dist/schema/mutations.d.ts.map +0 -1
  403. package/dist/schema/mutations.js +0 -42
  404. package/dist/schema/mutations.js.map +0 -1
  405. package/dist/sync/client-session-sync-processor.d.ts +0 -45
  406. package/dist/sync/client-session-sync-processor.d.ts.map +0 -1
  407. package/dist/sync/client-session-sync-processor.js +0 -131
  408. package/dist/sync/client-session-sync-processor.js.map +0 -1
  409. package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
  410. package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
  411. package/src/derived-mutations.test.ts +0 -101
  412. package/src/derived-mutations.ts +0 -166
  413. package/src/devtools/devtools-bridge.ts +0 -13
  414. package/src/devtools/index.ts +0 -48
  415. package/src/init-singleton-tables.ts +0 -24
  416. package/src/leader-thread/apply-mutation.ts +0 -143
  417. package/src/leader-thread/leader-sync-processor.ts +0 -666
  418. package/src/leader-thread/mutationlog.ts +0 -42
  419. package/src/leader-thread/pull-queue-set.ts +0 -58
  420. package/src/mutation.ts +0 -81
  421. package/src/query-info.ts +0 -78
  422. package/src/rehydrate-from-mutationlog.ts +0 -127
  423. package/src/schema/MutationEvent.ts +0 -161
  424. package/src/schema/mutations.ts +0 -192
  425. package/src/sync/client-session-sync-processor.ts +0 -207
  426. package/src/sync/next/test/mutation-fixtures.ts +0 -231
@@ -1,666 +0,0 @@
1
- import { shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
2
- import type { HttpClient, Scope } from '@livestore/utils/effect'
3
- import {
4
- BucketQueue,
5
- Deferred,
6
- Effect,
7
- Exit,
8
- Fiber,
9
- FiberHandle,
10
- Option,
11
- OtelTracer,
12
- Ref,
13
- Schema,
14
- Stream,
15
- SubscriptionRef,
16
- } from '@livestore/utils/effect'
17
- import type * as otel from '@opentelemetry/api'
18
-
19
- import type { SynchronousDatabase } from '../adapter-types.js'
20
- import { UnexpectedError } from '../adapter-types.js'
21
- import type { LiveStoreSchema, SessionChangesetMetaRow } from '../schema/mod.js'
22
- import {
23
- EventId,
24
- MUTATION_LOG_META_TABLE,
25
- MutationEvent,
26
- mutationLogMetaTable,
27
- SESSION_CHANGESET_META_TABLE,
28
- } from '../schema/mod.js'
29
- import { updateRows } from '../sql-queries/index.js'
30
- import { InvalidPushError } from '../sync/sync.js'
31
- import * as SyncState from '../sync/syncstate.js'
32
- import { sql } from '../util.js'
33
- import { makeApplyMutation } from './apply-mutation.js'
34
- import { execSql } from './connection.js'
35
- import { getBackendHeadFromDb, getLocalHeadFromDb, getMutationEventsSince, updateBackendHead } from './mutationlog.js'
36
- import type { InitialBlockingSyncContext, InitialSyncInfo, SyncProcessor } from './types.js'
37
- import { LeaderThreadCtx } from './types.js'
38
-
39
- type ProcessorStateInit = {
40
- _tag: 'init'
41
- }
42
-
43
- type ProcessorStateInSync = {
44
- _tag: 'in-sync'
45
- syncState: SyncState.SyncState
46
- }
47
-
48
- type ProcessorStateApplyingSyncStateAdvance = {
49
- _tag: 'applying-syncstate-advance'
50
- origin: 'pull' | 'push'
51
- syncState: SyncState.SyncState
52
- // TODO re-introduce this
53
- // proccesHead: EventId
54
- fiber: Fiber.RuntimeFiber<void, UnexpectedError>
55
- }
56
-
57
- type ProcessorState = ProcessorStateInit | ProcessorStateInSync | ProcessorStateApplyingSyncStateAdvance
58
-
59
- /**
60
- * The general idea of the sync processor is to "follow the sync state"
61
- * and apply/rollback mutations as needed to the read model and mutation log.
62
- * The leader sync processor is also responsible for
63
- * - broadcasting mutations to client sessions via the pull queues.
64
- * - pushing mutations to the sync backend
65
- *
66
- * In the leader sync processor, pulling always has precedence over pushing.
67
- *
68
- * External events:
69
- * - Mutation pushed from client session
70
- * - Mutation pushed from devtools (via pushPartial)
71
- * - Mutation pulled from sync backend
72
- *
73
- * The machine can be in the following states:
74
- * - in-sync: fully synced with remote, now idling
75
- * - applying-syncstate-advance (with pointer to current progress in case of rebase interrupt)
76
- *
77
- * Transitions:
78
- * - in-sync -> applying-syncstate-advance
79
- * - applying-syncstate-advance -> in-sync
80
- * - applying-syncstate-advance -> applying-syncstate-advance (need to interrupt previous operation)
81
- *
82
- * Queuing vs interrupting behaviour:
83
- * - Operations caused by pull can never be interrupted
84
- * - Incoming pull can interrupt current push
85
- * - Incoming pull needs to wait to previous pull to finish
86
- * - Incoming push needs to wait to previous push to finish
87
- *
88
- * Backend pushing:
89
- * - continously push to backend
90
- * - only interrupted and restarted on rebase
91
- */
92
- export const makeLeaderSyncProcessor = ({
93
- schema,
94
- dbMissing,
95
- dbLog,
96
- initialBlockingSyncContext,
97
- }: {
98
- schema: LiveStoreSchema
99
- /** Only used to know whether we can safely query dbLog during setup execution */
100
- dbMissing: boolean
101
- dbLog: SynchronousDatabase
102
- initialBlockingSyncContext: InitialBlockingSyncContext
103
- }): Effect.Effect<SyncProcessor, UnexpectedError, Scope.Scope> =>
104
- Effect.gen(function* () {
105
- const syncBackendQueue = yield* BucketQueue.make<MutationEvent.AnyEncoded>()
106
-
107
- const stateRef = yield* Ref.make<ProcessorState>({ _tag: 'init' })
108
-
109
- const semaphore = yield* Effect.makeSemaphore(1)
110
-
111
- const isLocalEvent = (mutationEventEncoded: MutationEvent.EncodedWithMeta) => {
112
- const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
113
- return mutationDef.options.localOnly
114
- }
115
-
116
- const spanRef = { current: undefined as otel.Span | undefined }
117
- const applyMutationItemsRef = { current: undefined as ApplyMutationItems | undefined }
118
-
119
- // TODO get rid of counters once Effect semaphore ordering is fixed
120
- let counterRef = 0
121
- let expectedCounter = 0
122
-
123
- /*
124
- TODO: refactor
125
- - Pushes go directly into a Mailbox
126
- - Have a worker fiber that takes from the mailbox (wouldn't need a semaphore)
127
- */
128
-
129
- const waitForSyncState = (counter: number): Effect.Effect<ProcessorStateInSync> =>
130
- Effect.gen(function* () {
131
- // console.log('waitForSyncState: waiting for semaphore', counter)
132
- yield* semaphore.take(1)
133
- // NOTE this is a workaround to ensure the semaphore take-order is respected
134
- // TODO this needs to be fixed upstream in Effect
135
- if (counter !== expectedCounter) {
136
- console.log(
137
- `waitForSyncState: counter mismatch (expected: ${expectedCounter}, got: ${counter}), releasing semaphore`,
138
- )
139
- yield* semaphore.release(1)
140
- yield* Effect.yieldNow()
141
- // Retrying...
142
- return yield* waitForSyncState(counter)
143
- }
144
- // console.log('waitForSyncState: took semaphore', counter)
145
- const state = yield* Ref.get(stateRef)
146
- if (state._tag !== 'in-sync') {
147
- return shouldNeverHappen('Expected to be in-sync but got ' + state._tag)
148
- }
149
- expectedCounter = counter + 1
150
- return state
151
- }).pipe(Effect.withSpan(`@livestore/common:leader-thread:syncing:waitForSyncState(${counter})`))
152
-
153
- const push = (newEvents: ReadonlyArray<MutationEvent.EncodedWithMeta>) =>
154
- Effect.gen(function* () {
155
- const counter = counterRef
156
- counterRef++
157
- // TODO validate batch
158
- if (newEvents.length === 0) return
159
-
160
- const { connectedClientSessionPullQueues } = yield* LeaderThreadCtx
161
-
162
- // TODO if there are multiple pending pushes, we should batch them together
163
- const state = yield* waitForSyncState(counter)
164
-
165
- const updateResult = SyncState.updateSyncState({
166
- syncState: state.syncState,
167
- payload: { _tag: 'local-push', newEvents },
168
- isLocalEvent,
169
- isEqualEvent: MutationEvent.isEqualEncoded,
170
- })
171
-
172
- if (updateResult._tag === 'rebase') {
173
- return shouldNeverHappen('The leader thread should never have to rebase due to a local push')
174
- } else if (updateResult._tag === 'reject') {
175
- return yield* Effect.fail(
176
- InvalidPushError.make({
177
- reason: {
178
- _tag: 'LeaderAhead',
179
- minimumExpectedId: updateResult.expectedMinimumId,
180
- providedId: newEvents.at(0)!.id,
181
- },
182
- }),
183
- )
184
- }
185
-
186
- const fiber = yield* applyMutationItemsRef.current!({ batchItems: updateResult.newEvents }).pipe(Effect.fork)
187
-
188
- yield* Ref.set(stateRef, {
189
- _tag: 'applying-syncstate-advance',
190
- origin: 'push',
191
- syncState: updateResult.newSyncState,
192
- fiber,
193
- })
194
-
195
- // console.log('setRef:applying-syncstate-advance after push', counter)
196
-
197
- yield* connectedClientSessionPullQueues.offer({
198
- payload: { _tag: 'upstream-advance', newEvents: updateResult.newEvents },
199
- remaining: 0,
200
- })
201
-
202
- spanRef.current?.addEvent('local-push', {
203
- batchSize: newEvents.length,
204
- updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
205
- })
206
-
207
- // Don't sync localOnly mutations
208
- const filteredBatch = updateResult.newEvents.filter((mutationEventEncoded) => {
209
- const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
210
- return mutationDef.options.localOnly === false
211
- })
212
-
213
- yield* BucketQueue.offerAll(syncBackendQueue, filteredBatch)
214
-
215
- yield* fiber // Waiting for the mutation to be applied
216
- }).pipe(
217
- Effect.withSpan('@livestore/common:leader-thread:syncing:local-push', {
218
- attributes: {
219
- batchSize: newEvents.length,
220
- batch: TRACE_VERBOSE ? newEvents : undefined,
221
- },
222
- links: spanRef.current
223
- ? [{ _tag: 'SpanLink', span: OtelTracer.makeExternalSpan(spanRef.current.spanContext()), attributes: {} }]
224
- : undefined,
225
- }),
226
- )
227
-
228
- const pushPartial: SyncProcessor['pushPartial'] = (mutationEventEncoded_) =>
229
- Effect.gen(function* () {
230
- const state = yield* Ref.get(stateRef)
231
- if (state._tag === 'init') return shouldNeverHappen('Not initialized')
232
-
233
- const mutationDef =
234
- schema.mutations.get(mutationEventEncoded_.mutation) ??
235
- shouldNeverHappen(`Unknown mutation: ${mutationEventEncoded_.mutation}`)
236
-
237
- const mutationEventEncoded = new MutationEvent.EncodedWithMeta({
238
- ...mutationEventEncoded_,
239
- ...EventId.nextPair(state.syncState.localHead, mutationDef.options.localOnly),
240
- })
241
-
242
- yield* push([mutationEventEncoded])
243
- }).pipe(Effect.catchTag('InvalidPushError', Effect.orDie))
244
-
245
- // Starts various background loops
246
- const boot: SyncProcessor['boot'] = ({ dbReady }) =>
247
- Effect.gen(function* () {
248
- const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.catchAll(() => Effect.succeed(undefined)))
249
- spanRef.current = span
250
-
251
- const initialBackendHead = dbMissing ? EventId.ROOT.global : getBackendHeadFromDb(dbLog)
252
- const initialLocalHead = dbMissing ? EventId.ROOT : getLocalHeadFromDb(dbLog)
253
-
254
- if (initialBackendHead > initialLocalHead.global) {
255
- return shouldNeverHappen(
256
- `During boot the backend head (${initialBackendHead}) should never be greater than the local head (${initialLocalHead.global})`,
257
- )
258
- }
259
-
260
- const pendingMutationEvents = yield* getMutationEventsSince({ global: initialBackendHead, local: 0 })
261
-
262
- const initialSyncState = {
263
- pending: pendingMutationEvents.map((_) => new MutationEvent.EncodedWithMeta(_)),
264
- // On the leader we don't need a rollback tail beyond `pending` items
265
- rollbackTail: [],
266
- upstreamHead: { global: initialBackendHead, local: 0 },
267
- localHead: initialLocalHead,
268
- } as SyncState.SyncState
269
-
270
- /** State transitions need to happen atomically, so we use a Ref to track the state */
271
- yield* Ref.set(stateRef, { _tag: 'in-sync', syncState: initialSyncState })
272
-
273
- applyMutationItemsRef.current = yield* makeApplyMutationItems({ stateRef, semaphore })
274
-
275
- // Rehydrate sync queue
276
- if (pendingMutationEvents.length > 0) {
277
- const filteredBatch = pendingMutationEvents
278
- // Don't sync localOnly mutations
279
- .filter((mutationEventEncoded) => {
280
- const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
281
- return mutationDef.options.localOnly === false
282
- })
283
-
284
- yield* BucketQueue.offerAll(syncBackendQueue, filteredBatch)
285
- }
286
-
287
- const backendPushingFiberHandle = yield* FiberHandle.make()
288
-
289
- yield* FiberHandle.run(
290
- backendPushingFiberHandle,
291
- backgroundBackendPushing({ dbReady, syncBackendQueue, span }).pipe(Effect.tapCauseLogPretty),
292
- )
293
-
294
- yield* backgroundBackendPulling({
295
- dbReady,
296
- initialBackendHead,
297
- isLocalEvent,
298
- restartBackendPushing: (filteredRebasedPending) =>
299
- Effect.gen(function* () {
300
- // Stop current pushing fiber
301
- yield* FiberHandle.clear(backendPushingFiberHandle)
302
-
303
- // Reset the sync queue
304
- yield* BucketQueue.clear(syncBackendQueue)
305
- yield* BucketQueue.offerAll(syncBackendQueue, filteredRebasedPending)
306
-
307
- // Restart pushing fiber
308
- yield* FiberHandle.run(
309
- backendPushingFiberHandle,
310
- backgroundBackendPushing({ dbReady, syncBackendQueue, span }).pipe(Effect.tapCauseLogPretty),
311
- )
312
- }),
313
- applyMutationItemsRef,
314
- stateRef,
315
- semaphore,
316
- span,
317
- initialBlockingSyncContext,
318
- }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
319
- }).pipe(Effect.withSpanScoped('@livestore/common:leader-thread:syncing'))
320
-
321
- return {
322
- push,
323
- pushPartial,
324
- boot,
325
- syncState: Effect.gen(function* () {
326
- const state = yield* Ref.get(stateRef)
327
- if (state._tag === 'init') return shouldNeverHappen('Not initialized')
328
- return state.syncState
329
- }),
330
- } satisfies SyncProcessor
331
- })
332
-
333
- type ApplyMutationItems = (_: {
334
- batchItems: ReadonlyArray<MutationEvent.EncodedWithMeta>
335
- }) => Effect.Effect<void, UnexpectedError>
336
-
337
- // TODO how to handle errors gracefully
338
- const makeApplyMutationItems = ({
339
- stateRef,
340
- semaphore,
341
- }: {
342
- stateRef: Ref.Ref<ProcessorState>
343
- semaphore: Effect.Semaphore
344
- }): Effect.Effect<ApplyMutationItems, UnexpectedError, LeaderThreadCtx | Scope.Scope> =>
345
- Effect.gen(function* () {
346
- const leaderThreadCtx = yield* LeaderThreadCtx
347
- const { db, dbLog } = leaderThreadCtx
348
-
349
- const applyMutation = yield* makeApplyMutation
350
-
351
- return ({ batchItems }) =>
352
- Effect.gen(function* () {
353
- const state = yield* Ref.get(stateRef)
354
- if (state._tag !== 'applying-syncstate-advance') {
355
- // console.log('applyMutationItems: counter', counter)
356
- return shouldNeverHappen(`Expected to be applying-syncstate-advance but got ${state._tag}`)
357
- }
358
-
359
- db.execute('BEGIN TRANSACTION', undefined) // Start the transaction
360
- dbLog.execute('BEGIN TRANSACTION', undefined) // Start the transaction
361
-
362
- yield* Effect.addFinalizer((exit) =>
363
- Effect.gen(function* () {
364
- if (Exit.isSuccess(exit)) return
365
-
366
- // Rollback in case of an error
367
- db.execute('ROLLBACK', undefined)
368
- dbLog.execute('ROLLBACK', undefined)
369
- }),
370
- )
371
-
372
- for (let i = 0; i < batchItems.length; i++) {
373
- const { meta, ...mutationEventEncoded } = batchItems[i]!
374
-
375
- yield* applyMutation(mutationEventEncoded)
376
-
377
- if (meta?.deferred) {
378
- yield* Deferred.succeed(meta.deferred, void 0)
379
- }
380
-
381
- // TODO re-introduce this
382
- // if (i < batchItems.length - 1) {
383
- // yield* Ref.set(stateRef, { ...state, proccesHead: batchItems[i + 1]!.id })
384
- // }
385
- }
386
-
387
- db.execute('COMMIT', undefined) // Commit the transaction
388
- dbLog.execute('COMMIT', undefined) // Commit the transaction
389
-
390
- yield* Ref.set(stateRef, { _tag: 'in-sync', syncState: state.syncState })
391
- // console.log('setRef:sync after applyMutationItems', counter)
392
- yield* semaphore.release(1)
393
- }).pipe(
394
- Effect.scoped,
395
- Effect.withSpan('@livestore/common:leader-thread:syncing:applyMutationItems'),
396
- Effect.tapCauseLogPretty,
397
- UnexpectedError.mapToUnexpectedError,
398
- )
399
- })
400
-
401
- const backgroundBackendPulling = ({
402
- dbReady,
403
- initialBackendHead,
404
- isLocalEvent,
405
- restartBackendPushing,
406
- span,
407
- stateRef,
408
- applyMutationItemsRef,
409
- semaphore,
410
- initialBlockingSyncContext,
411
- }: {
412
- dbReady: Deferred.Deferred<void>
413
- initialBackendHead: number
414
- isLocalEvent: (mutationEventEncoded: MutationEvent.EncodedWithMeta) => boolean
415
- restartBackendPushing: (
416
- filteredRebasedPending: ReadonlyArray<MutationEvent.EncodedWithMeta>,
417
- ) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx | HttpClient.HttpClient>
418
- span: otel.Span | undefined
419
- stateRef: Ref.Ref<ProcessorState>
420
- applyMutationItemsRef: { current: ApplyMutationItems | undefined }
421
- semaphore: Effect.Semaphore
422
- initialBlockingSyncContext: InitialBlockingSyncContext
423
- }) =>
424
- Effect.gen(function* () {
425
- const { syncBackend, db, dbLog, connectedClientSessionPullQueues, schema } = yield* LeaderThreadCtx
426
-
427
- if (syncBackend === undefined) return
428
-
429
- const cursorInfo = yield* getCursorInfo(initialBackendHead)
430
-
431
- const onNewPullChunk = (newEvents: MutationEvent.EncodedWithMeta[], remaining: number) =>
432
- Effect.gen(function* () {
433
- if (newEvents.length === 0) return
434
-
435
- const state = yield* Ref.get(stateRef)
436
- if (state._tag === 'init') return shouldNeverHappen('Not initialized')
437
-
438
- // const counter = state.counter + 1
439
-
440
- if (state._tag === 'applying-syncstate-advance') {
441
- if (state.origin === 'push') {
442
- yield* Fiber.interrupt(state.fiber)
443
- // In theory we should force-take the semaphore here, but as it's still taken,
444
- // it's already in the right state we want it to be in
445
- } else {
446
- // Wait for previous advance to finish
447
- yield* semaphore.take(1)
448
- }
449
- }
450
-
451
- const trimRollbackUntil = newEvents.at(-1)!.id
452
-
453
- const updateResult = SyncState.updateSyncState({
454
- syncState: state.syncState,
455
- payload: { _tag: 'upstream-advance', newEvents, trimRollbackUntil },
456
- isLocalEvent,
457
- isEqualEvent: MutationEvent.isEqualEncoded,
458
- ignoreLocalEvents: true,
459
- })
460
-
461
- if (updateResult._tag === 'reject') {
462
- return shouldNeverHappen('The leader thread should never reject upstream advances')
463
- }
464
-
465
- const newBackendHead = newEvents.at(-1)!.id
466
-
467
- updateBackendHead(dbLog, newBackendHead)
468
-
469
- if (updateResult._tag === 'rebase') {
470
- span?.addEvent('backend-pull:rebase', {
471
- newEventsCount: newEvents.length,
472
- newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
473
- rollbackCount: updateResult.eventsToRollback.length,
474
- updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
475
- })
476
-
477
- const filteredRebasedPending = updateResult.newSyncState.pending.filter((mutationEvent) => {
478
- const mutationDef = schema.mutations.get(mutationEvent.mutation)!
479
- return mutationDef.options.localOnly === false
480
- })
481
- yield* restartBackendPushing(filteredRebasedPending)
482
-
483
- if (updateResult.eventsToRollback.length > 0) {
484
- yield* rollback({ db, dbLog, eventIdsToRollback: updateResult.eventsToRollback.map((_) => _.id) })
485
- }
486
-
487
- yield* connectedClientSessionPullQueues.offer({
488
- payload: {
489
- _tag: 'upstream-rebase',
490
- newEvents: updateResult.newEvents,
491
- rollbackUntil: updateResult.eventsToRollback.at(0)!.id,
492
- trimRollbackUntil,
493
- },
494
- remaining,
495
- })
496
- } else {
497
- span?.addEvent('backend-pull:advance', {
498
- newEventsCount: newEvents.length,
499
- updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
500
- })
501
-
502
- yield* connectedClientSessionPullQueues.offer({
503
- payload: { _tag: 'upstream-advance', newEvents: updateResult.newEvents, trimRollbackUntil },
504
- remaining,
505
- })
506
- }
507
-
508
- const fiber = yield* applyMutationItemsRef.current!({
509
- batchItems: updateResult.newEvents,
510
- }).pipe(Effect.fork)
511
-
512
- yield* Ref.set(stateRef, {
513
- _tag: 'applying-syncstate-advance',
514
- origin: 'pull',
515
- syncState: updateResult.newSyncState,
516
- fiber,
517
- })
518
- // console.log('setRef:applying-syncstate-advance after backgroundBackendPulling', -1)
519
- })
520
-
521
- yield* syncBackend.pull(cursorInfo).pipe(
522
- // TODO only take from queue while connected
523
- Stream.tap(({ batch, remaining }) =>
524
- Effect.gen(function* () {
525
- // yield* Effect.spanEvent('batch', {
526
- // attributes: {
527
- // batchSize: batch.length,
528
- // batch: TRACE_VERBOSE ? batch : undefined,
529
- // },
530
- // })
531
-
532
- // Wait for the db to be initially created
533
- yield* dbReady
534
-
535
- // NOTE we only want to take process mutations when the sync backend is connected
536
- // (e.g. needed for simulating being offline)
537
- // TODO remove when there's a better way to handle this in stream above
538
- yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true)
539
-
540
- yield* onNewPullChunk(
541
- batch.map((_) => new MutationEvent.EncodedWithMeta(_.mutationEventEncoded)),
542
- remaining,
543
- )
544
-
545
- yield* initialBlockingSyncContext.update({ processed: batch.length, remaining })
546
- }),
547
- ),
548
- Stream.runDrain,
549
- Effect.interruptible,
550
- )
551
- }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:backend-pulling'))
552
-
553
- const rollback = ({
554
- db,
555
- dbLog,
556
- eventIdsToRollback,
557
- }: {
558
- db: SynchronousDatabase
559
- dbLog: SynchronousDatabase
560
- eventIdsToRollback: EventId.EventId[]
561
- }) =>
562
- Effect.gen(function* () {
563
- const rollbackEvents = db
564
- .select<SessionChangesetMetaRow>(
565
- sql`SELECT * FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idLocal) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.local})`).join(', ')})`,
566
- )
567
- .map((_) => ({ id: { global: _.idGlobal, local: _.idLocal }, changeset: _.changeset, debug: _.debug }))
568
- .toSorted((a, b) => EventId.compare(a.id, b.id))
569
-
570
- // Apply changesets in reverse order
571
- for (let i = rollbackEvents.length - 1; i >= 0; i--) {
572
- const { changeset } = rollbackEvents[i]!
573
- db.makeChangeset(changeset).invert().apply()
574
- }
575
-
576
- // Delete the changeset rows
577
- db.execute(
578
- sql`DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idLocal) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.local})`).join(', ')})`,
579
- )
580
-
581
- // Delete the mutation log rows
582
- dbLog.execute(
583
- sql`DELETE FROM ${MUTATION_LOG_META_TABLE} WHERE (idGlobal, idLocal) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.local})`).join(', ')})`,
584
- )
585
- }).pipe(
586
- Effect.withSpan('@livestore/common:leader-thread:syncing:rollback', {
587
- attributes: { count: eventIdsToRollback.length },
588
- }),
589
- )
590
-
591
- const getCursorInfo = (remoteHead: number) =>
592
- Effect.gen(function* () {
593
- const { dbLog } = yield* LeaderThreadCtx
594
-
595
- if (remoteHead === EventId.ROOT.global) return Option.none()
596
-
597
- const MutationlogQuerySchema = Schema.Struct({
598
- syncMetadataJson: Schema.parseJson(Schema.Option(Schema.JsonValue)),
599
- }).pipe(Schema.pluck('syncMetadataJson'), Schema.Array, Schema.head)
600
-
601
- const syncMetadataOption = yield* Effect.sync(() =>
602
- dbLog.select<{ syncMetadataJson: string }>(
603
- sql`SELECT syncMetadataJson FROM ${MUTATION_LOG_META_TABLE} WHERE idGlobal = ${remoteHead} ORDER BY idLocal ASC LIMIT 1`,
604
- ),
605
- ).pipe(Effect.andThen(Schema.decode(MutationlogQuerySchema)), Effect.map(Option.flatten), Effect.orDie)
606
-
607
- return Option.some({
608
- cursor: { global: remoteHead, local: 0 },
609
- metadata: syncMetadataOption,
610
- }) satisfies InitialSyncInfo
611
- }).pipe(Effect.withSpan('@livestore/common:leader-thread:syncing:getCursorInfo', { attributes: { remoteHead } }))
612
-
613
- const backgroundBackendPushing = ({
614
- dbReady,
615
- syncBackendQueue,
616
- span,
617
- }: {
618
- dbReady: Deferred.Deferred<void>
619
- syncBackendQueue: BucketQueue.BucketQueue<MutationEvent.AnyEncoded>
620
- span: otel.Span | undefined
621
- }) =>
622
- Effect.gen(function* () {
623
- const { syncBackend, dbLog } = yield* LeaderThreadCtx
624
- if (syncBackend === undefined) return
625
-
626
- yield* dbReady
627
-
628
- while (true) {
629
- yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true)
630
-
631
- // TODO make batch size configurable
632
- const queueItems = yield* BucketQueue.takeBetween(syncBackendQueue, 1, 50)
633
-
634
- yield* SubscriptionRef.waitUntil(syncBackend.isConnected, (isConnected) => isConnected === true)
635
-
636
- span?.addEvent('backend-push', {
637
- batchSize: queueItems.length,
638
- batch: TRACE_VERBOSE ? JSON.stringify(queueItems) : undefined,
639
- })
640
-
641
- // TODO handle push errors (should only happen during concurrent pull+push)
642
- const pushResult = yield* syncBackend.push(queueItems).pipe(Effect.either)
643
-
644
- if (pushResult._tag === 'Left') {
645
- span?.addEvent('backend-push-error', { error: pushResult.left.toString() })
646
- // wait for interrupt and restarting of pushing
647
- return yield* Effect.never
648
- }
649
-
650
- const { metadata } = pushResult.right
651
-
652
- // TODO try to do this in a single query
653
- for (let i = 0; i < queueItems.length; i++) {
654
- const mutationEventEncoded = queueItems[i]!
655
- yield* execSql(
656
- dbLog,
657
- ...updateRows({
658
- tableName: MUTATION_LOG_META_TABLE,
659
- columns: mutationLogMetaTable.sqliteDef.columns,
660
- where: { idGlobal: mutationEventEncoded.id.global, idLocal: mutationEventEncoded.id.local },
661
- updateValues: { syncMetadataJson: metadata[i]! },
662
- }),
663
- )
664
- }
665
- }
666
- }).pipe(Effect.interruptible, Effect.withSpan('@livestore/common:leader-thread:syncing:backend-pushing'))