@livestore/common 0.3.0-dev.5 → 0.3.0-dev.50

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