@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,4 +1,4 @@
1
- import type { SynchronousDatabase } from '../adapter-types.js'
1
+ import type { SqliteDb } from '../adapter-types.js'
2
2
  import type { ParamsObject } from '../util.js'
3
3
  import { prepareBindValues } from '../util.js'
4
4
 
@@ -6,7 +6,7 @@ import { prepareBindValues } from '../util.js'
6
6
  // will require proper scope-aware cleanup etc (for testing and apps with multiple LiveStore instances)
7
7
  // const cachedStmts = new Map<string, PreparedStatement>()
8
8
 
9
- export const dbExecute = (db: SynchronousDatabase, queryStr: string, bindValues?: ParamsObject) => {
9
+ export const dbExecute = (db: SqliteDb, queryStr: string, bindValues?: ParamsObject) => {
10
10
  // let stmt = cachedStmts.get(queryStr)
11
11
  // if (!stmt) {
12
12
  const stmt = db.prepare(queryStr)
@@ -20,7 +20,7 @@ export const dbExecute = (db: SynchronousDatabase, queryStr: string, bindValues?
20
20
  stmt.finalize()
21
21
  }
22
22
 
23
- export const dbSelect = <T>(db: SynchronousDatabase, queryStr: string, bindValues?: ParamsObject) => {
23
+ export const dbSelect = <T>(db: SqliteDb, queryStr: string, bindValues?: ParamsObject) => {
24
24
  // let stmt = cachedStmts.get(queryStr)
25
25
  // if (!stmt) {
26
26
  const stmt = db.prepare(queryStr)
@@ -33,12 +33,12 @@ export const dbSelect = <T>(db: SynchronousDatabase, queryStr: string, bindValue
33
33
  }
34
34
 
35
35
  export interface SchemaManager {
36
- getMutationDefInfos: () => ReadonlyArray<MutationDefInfo>
36
+ getEventDefInfos: () => ReadonlyArray<EventDefInfo>
37
37
 
38
- setMutationDefInfo: (mutationDefInfo: MutationDefInfo) => void
38
+ setEventDefInfo: (eventDefInfo: EventDefInfo) => void
39
39
  }
40
40
 
41
- export type MutationDefInfo = {
42
- mutationName: string
41
+ export type EventDefInfo = {
42
+ eventName: string
43
43
  schemaHash: number
44
44
  }
@@ -1,42 +1,41 @@
1
- import { SqliteAst, SqliteDsl } from '@livestore/db-schema'
2
1
  import { memoizeByStringifyArgs } from '@livestore/utils'
3
2
  import { Effect, Schema as EffectSchema } from '@livestore/utils/effect'
4
3
 
5
- import type { SynchronousDatabase } from '../adapter-types.js'
4
+ import type { MigrationsReport, MigrationsReportEntry, SqliteDb, UnexpectedError } from '../adapter-types.js'
6
5
  import type { LiveStoreSchema } from '../schema/mod.js'
7
- import type { SchemaMetaRow, SchemaMutationsMetaRow } from '../schema/system-tables.js'
6
+ import { SqliteAst, SqliteDsl } from '../schema/state/sqlite/db-schema/mod.js'
7
+ import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables.js'
8
8
  import {
9
+ isStateSystemTable,
10
+ SCHEMA_EVENT_DEFS_META_TABLE,
9
11
  SCHEMA_META_TABLE,
10
- SCHEMA_MUTATIONS_META_TABLE,
11
- schemaMetaTable,
12
- schemaMutationsMetaTable,
13
- systemTables,
14
- } from '../schema/system-tables.js'
12
+ schemaEventDefsMetaTable,
13
+ stateSystemTables,
14
+ } from '../schema/state/sqlite/system-tables.js'
15
15
  import { sql } from '../util.js'
16
16
  import type { SchemaManager } from './common.js'
17
17
  import { dbExecute, dbSelect } from './common.js'
18
- import { validateSchema } from './validate-mutation-defs.js'
18
+ import { validateSchema } from './validate-schema.js'
19
19
 
20
20
  const getMemoizedTimestamp = memoizeByStringifyArgs(() => new Date().toISOString())
21
21
 
22
- export const makeSchemaManager = (db: SynchronousDatabase): Effect.Effect<SchemaManager> =>
22
+ export const makeSchemaManager = (db: SqliteDb): Effect.Effect<SchemaManager> =>
23
23
  Effect.gen(function* () {
24
24
  yield* migrateTable({
25
25
  db,
26
- tableAst: schemaMutationsMetaTable.sqliteDef.ast,
26
+ tableAst: schemaEventDefsMetaTable.sqliteDef.ast,
27
27
  behaviour: 'create-if-not-exists',
28
28
  })
29
29
 
30
30
  return {
31
- getMutationDefInfos: () =>
32
- dbSelect<SchemaMutationsMetaRow>(db, sql`SELECT * FROM ${SCHEMA_MUTATIONS_META_TABLE}`),
31
+ getEventDefInfos: () => dbSelect<SchemaEventDefsMetaRow>(db, sql`SELECT * FROM ${SCHEMA_EVENT_DEFS_META_TABLE}`),
33
32
 
34
- setMutationDefInfo: (info) => {
33
+ setEventDefInfo: (info) => {
35
34
  dbExecute(
36
35
  db,
37
- sql`INSERT OR REPLACE INTO ${SCHEMA_MUTATIONS_META_TABLE} (mutationName, schemaHash, updatedAt) VALUES ($mutationName, $schemaHash, $updatedAt)`,
36
+ sql`INSERT OR REPLACE INTO ${SCHEMA_EVENT_DEFS_META_TABLE} (eventName, schemaHash, updatedAt) VALUES ($eventName, $schemaHash, $updatedAt)`,
38
37
  {
39
- mutationName: info.mutationName,
38
+ eventName: info.eventName,
40
39
  schemaHash: info.schemaHash,
41
40
  updatedAt: new Date().toISOString(),
42
41
  },
@@ -51,16 +50,18 @@ export const migrateDb = ({
51
50
  schema,
52
51
  onProgress,
53
52
  }: {
54
- db: SynchronousDatabase
53
+ db: SqliteDb
55
54
  schema: LiveStoreSchema
56
55
  onProgress?: (opts: { done: number; total: number }) => Effect.Effect<void>
57
- }) =>
56
+ }): Effect.Effect<MigrationsReport, UnexpectedError> =>
58
57
  Effect.gen(function* () {
59
- yield* migrateTable({
60
- db,
61
- tableAst: schemaMetaTable.sqliteDef.ast,
62
- behaviour: 'create-if-not-exists',
63
- })
58
+ for (const tableDef of stateSystemTables) {
59
+ yield* migrateTable({
60
+ db,
61
+ tableAst: tableDef.sqliteDef.ast,
62
+ behaviour: 'create-if-not-exists',
63
+ })
64
+ }
64
65
 
65
66
  // TODO enforce that migrating tables isn't allowed once the store is running
66
67
 
@@ -73,14 +74,15 @@ export const migrateDb = ({
73
74
  schemaMetaRows.map(({ tableName, schemaHash }) => [tableName, schemaHash]),
74
75
  )
75
76
 
76
- const tableDefs = new Set([
77
+ const tableDefs = [
77
78
  // NOTE it's important the `SCHEMA_META_TABLE` comes first since we're writing to it below
78
- ...systemTables,
79
- ...Array.from(schema.tables.values()).filter((_) => _.sqliteDef.name !== SCHEMA_META_TABLE),
80
- ])
79
+ ...stateSystemTables,
80
+ ...Array.from(schema.state.sqlite.tables.values()).filter((_) => !isStateSystemTable(_.sqliteDef.name)),
81
+ ]
81
82
 
82
83
  const tablesToMigrate = new Set<{ tableAst: SqliteAst.Table; schemaHash: number }>()
83
84
 
85
+ const migrationsReportEntries: MigrationsReportEntry[] = []
84
86
  for (const tableDef of tableDefs) {
85
87
  const tableAst = tableDef.sqliteDef.ast
86
88
  const tableName = tableAst.name
@@ -90,9 +92,10 @@ export const migrateDb = ({
90
92
  if (schemaHash !== dbSchemaHash) {
91
93
  tablesToMigrate.add({ tableAst, schemaHash })
92
94
 
93
- console.log(
94
- `Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
95
- )
95
+ migrationsReportEntries.push({
96
+ tableName,
97
+ hashes: { expected: schemaHash, actual: dbSchemaHash },
98
+ })
96
99
  }
97
100
  }
98
101
 
@@ -107,6 +110,8 @@ export const migrateDb = ({
107
110
  yield* onProgress({ done: processedTables, total: tablesCount })
108
111
  }
109
112
  }
113
+
114
+ return { migrations: migrationsReportEntries }
110
115
  })
111
116
 
112
117
  export const migrateTable = ({
@@ -116,7 +121,7 @@ export const migrateTable = ({
116
121
  behaviour,
117
122
  skipMetaTable = false,
118
123
  }: {
119
- db: SynchronousDatabase
124
+ db: SqliteDb
120
125
  tableAst: SqliteAst.Table
121
126
  schemaHash?: number
122
127
  behaviour: 'drop-and-recreate' | 'create-if-not-exists'
@@ -182,6 +187,7 @@ const toSqliteColumnSpec = (column: SqliteAst.Column) => {
182
187
  const defaultValueStr = (() => {
183
188
  if (column.default._tag === 'None') return ''
184
189
 
190
+ if (column.default.value === null) return 'default null'
185
191
  if (SqliteDsl.isSqlDefaultValue(column.default.value)) return `default ${column.default.value.sql}`
186
192
 
187
193
  const encodeValue = EffectSchema.encodeSync(column.schema)
@@ -0,0 +1,61 @@
1
+ import { Effect, Schema } from '@livestore/utils/effect'
2
+
3
+ import { UnexpectedError } from '../adapter-types.js'
4
+ import type { EventDef } from '../schema/EventDef.js'
5
+ import type { LiveStoreSchema } from '../schema/mod.js'
6
+ import type { EventDefInfo, SchemaManager } from './common.js'
7
+
8
+ export const validateSchema = (schema: LiveStoreSchema, schemaManager: SchemaManager) =>
9
+ Effect.gen(function* () {
10
+ // Validate mutation definitions
11
+ const registeredEventDefInfos = schemaManager.getEventDefInfos()
12
+
13
+ const missingEventDefs = registeredEventDefInfos.filter(
14
+ (registeredEventDefInfo) => !schema.eventsDefsMap.has(registeredEventDefInfo.eventName),
15
+ )
16
+
17
+ if (missingEventDefs.length > 0) {
18
+ yield* new UnexpectedError({
19
+ cause: `Missing mutation definitions: ${missingEventDefs.map((info) => info.eventName).join(', ')}`,
20
+ })
21
+ }
22
+
23
+ for (const [, eventDef] of schema.eventsDefsMap) {
24
+ const registeredEventDefInfo = registeredEventDefInfos.find((info) => info.eventName === eventDef.name)
25
+
26
+ validateEventDef(eventDef, schemaManager, registeredEventDefInfo)
27
+ }
28
+
29
+ // Validate table schemas
30
+ })
31
+
32
+ export const validateEventDef = (
33
+ eventDef: EventDef.AnyWithoutFn,
34
+ schemaManager: SchemaManager,
35
+ registeredEventDefInfo: EventDefInfo | undefined,
36
+ ) => {
37
+ const schemaHash = Schema.hash(eventDef.schema)
38
+
39
+ if (registeredEventDefInfo === undefined) {
40
+ schemaManager.setEventDefInfo({
41
+ schemaHash,
42
+ eventName: eventDef.name,
43
+ })
44
+
45
+ return
46
+ }
47
+
48
+ if (schemaHash === registeredEventDefInfo.schemaHash) return
49
+
50
+ // TODO bring back some form of schema compatibility check (see https://github.com/livestorejs/livestore/issues/69)
51
+ // const newSchemaIsCompatibleWithOldSchema = Schema.isSubType(jsonSchemaDefFromMgmtStore, eventDef.schema)
52
+
53
+ // if (!newSchemaIsCompatibleWithOldSchema) {
54
+ // shouldNeverHappen(`Schema for mutation ${eventDef.name} has changed in an incompatible way`)
55
+ // }
56
+
57
+ schemaManager.setEventDefInfo({
58
+ schemaHash,
59
+ eventName: eventDef.name,
60
+ })
61
+ }
@@ -1,7 +1,7 @@
1
- import type { SqliteDsl } from '@livestore/db-schema'
2
1
  import { shouldNeverHappen } from '@livestore/utils'
3
2
  import { pipe, ReadonlyArray, Schema, TreeFormatter } from '@livestore/utils/effect'
4
3
 
4
+ import type { SqliteDsl } from '../schema/state/sqlite/db-schema/mod.js'
5
5
  import { sql } from '../util.js'
6
6
  import { objectEntries } from './misc.js'
7
7
  import * as ClientTypes from './types.js'
@@ -1,5 +1,4 @@
1
- import type { SqliteDsl } from '@livestore/db-schema'
2
-
1
+ import type { SqliteDsl } from '../schema/state/sqlite/db-schema/mod.js'
3
2
  import type { BindValues } from './sql-queries.js'
4
3
  import * as SqlQueries from './sql-queries.js'
5
4
  import type * as ClientTypes from './types.js'
@@ -1,6 +1,8 @@
1
- import type { Prettify, SqliteDsl } from '@livestore/db-schema'
1
+ import type { Prettify } from '@livestore/utils'
2
2
  import type { Schema } from '@livestore/utils/effect'
3
3
 
4
+ import type { SqliteDsl } from '../schema/state/sqlite/db-schema/mod.js'
5
+
4
6
  export type DecodedValuesForTableAll<TSchema extends SqliteDsl.DbSchema, TTableName extends keyof TSchema> = {
5
7
  [K in keyof GetColumns<TSchema, TTableName>]: Schema.Schema.Type<GetColumn<TSchema, TTableName, K>['schema']>
6
8
  }
@@ -0,0 +1,332 @@
1
+ /// <reference lib="dom" />
2
+ import { LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
3
+ import type { Runtime, Scope } from '@livestore/utils/effect'
4
+ import { BucketQueue, Effect, FiberHandle, Queue, Schema, Stream, Subscribable } from '@livestore/utils/effect'
5
+ import * as otel from '@opentelemetry/api'
6
+
7
+ import type { ClientSession, UnexpectedError } from '../adapter-types.js'
8
+ import * as EventSequenceNumber from '../schema/EventSequenceNumber.js'
9
+ import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
10
+ import { getEventDef, type LiveStoreSchema, SystemTables } from '../schema/mod.js'
11
+ import { sql } from '../util.js'
12
+ import * as SyncState from './syncstate.js'
13
+
14
+ /**
15
+ * Rebase behaviour:
16
+ * - We continously pull events from the leader and apply them to the local store.
17
+ * - If there was a race condition (i.e. the leader and client session have both advacned),
18
+ * we'll need to rebase the local pending events on top of the leader's head.
19
+ * - The goal is to never block the UI, so we'll interrupt rebasing if a new events is pushed by the client session.
20
+ * - We also want to avoid "backwards-jumping" in the UI, so we'll transactionally apply state changes during a rebase.
21
+ * - We might need to make the rebase behaviour configurable e.g. to let users manually trigger a rebase
22
+ *
23
+ * Longer term we should evalutate whether we can unify the ClientSessionSyncProcessor with the LeaderSyncProcessor.
24
+ */
25
+ export const makeClientSessionSyncProcessor = ({
26
+ schema,
27
+ clientSession,
28
+ runtime,
29
+ materializeEvent,
30
+ rollback,
31
+ refreshTables,
32
+ span,
33
+ params,
34
+ confirmUnsavedChanges,
35
+ }: {
36
+ schema: LiveStoreSchema
37
+ clientSession: ClientSession
38
+ runtime: Runtime.Runtime<Scope.Scope>
39
+ materializeEvent: (
40
+ eventDecoded: LiveStoreEvent.PartialAnyDecoded,
41
+ options: { otelContext: otel.Context; withChangeset: boolean },
42
+ ) => {
43
+ writeTables: Set<string>
44
+ sessionChangeset: { _tag: 'sessionChangeset'; data: Uint8Array; debug: any } | { _tag: 'no-op' } | { _tag: 'unset' }
45
+ }
46
+ rollback: (changeset: Uint8Array) => void
47
+ refreshTables: (tables: Set<string>) => void
48
+ span: otel.Span
49
+ params: {
50
+ leaderPushBatchSize: number
51
+ }
52
+ /**
53
+ * Currently only used in the web adapter:
54
+ * If true, registers a beforeunload event listener to confirm unsaved changes.
55
+ */
56
+ confirmUnsavedChanges: boolean
57
+ }): ClientSessionSyncProcessor => {
58
+ const eventSchema = LiveStoreEvent.makeEventDefSchemaMemo(schema)
59
+
60
+ const syncStateRef = {
61
+ // The initial state is identical to the leader's initial state
62
+ current: new SyncState.SyncState({
63
+ localHead: clientSession.leaderThread.initialState.leaderHead,
64
+ upstreamHead: clientSession.leaderThread.initialState.leaderHead,
65
+ // Given we're starting with the leader's snapshot, we don't have any pending events intially
66
+ pending: [],
67
+ }),
68
+ }
69
+
70
+ const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
71
+ const isClientEvent = (eventEncoded: LiveStoreEvent.EncodedWithMeta) =>
72
+ getEventDef(schema, eventEncoded.name).eventDef.options.clientOnly
73
+
74
+ /** We're queuing push requests to reduce the number of messages sent to the leader by batching them */
75
+ const leaderPushQueue = BucketQueue.make<LiveStoreEvent.EncodedWithMeta>().pipe(Effect.runSync)
76
+
77
+ const push: ClientSessionSyncProcessor['push'] = (batch, { otelContext }) => {
78
+ // TODO validate batch
79
+
80
+ let baseEventSequenceNumber = syncStateRef.current.localHead
81
+ const encodedEventDefs = batch.map(({ name, args }) => {
82
+ const eventDef = getEventDef(schema, name)
83
+ const nextNumPair = EventSequenceNumber.nextPair(baseEventSequenceNumber, eventDef.eventDef.options.clientOnly)
84
+ baseEventSequenceNumber = nextNumPair.seqNum
85
+ return new LiveStoreEvent.EncodedWithMeta(
86
+ Schema.encodeUnknownSync(eventSchema)({
87
+ name,
88
+ args,
89
+ ...nextNumPair,
90
+ clientId: clientSession.clientId,
91
+ sessionId: clientSession.sessionId,
92
+ }),
93
+ )
94
+ })
95
+
96
+ const mergeResult = SyncState.merge({
97
+ syncState: syncStateRef.current,
98
+ payload: { _tag: 'local-push', newEvents: encodedEventDefs },
99
+ isClientEvent,
100
+ isEqualEvent: LiveStoreEvent.isEqualEncoded,
101
+ })
102
+
103
+ if (mergeResult._tag === 'unexpected-error') {
104
+ return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.cause)
105
+ }
106
+
107
+ span.addEvent('local-push', {
108
+ batchSize: encodedEventDefs.length,
109
+ mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
110
+ })
111
+
112
+ if (mergeResult._tag !== 'advance') {
113
+ return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
114
+ }
115
+
116
+ syncStateRef.current = mergeResult.newSyncState
117
+ syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
118
+
119
+ const writeTables = new Set<string>()
120
+ for (const event of mergeResult.newEvents) {
121
+ // TODO avoid encoding and decoding here again
122
+ const decodedEventDef = Schema.decodeSync(eventSchema)(event)
123
+ const res = materializeEvent(decodedEventDef, { otelContext, withChangeset: true })
124
+ for (const table of res.writeTables) {
125
+ writeTables.add(table)
126
+ }
127
+ event.meta.sessionChangeset = res.sessionChangeset
128
+ }
129
+
130
+ // console.debug('pushToLeader', encodedEventDefs.length, ...encodedEventDefs.map((_) => _.toJSON()))
131
+ BucketQueue.offerAll(leaderPushQueue, encodedEventDefs).pipe(Effect.runSync)
132
+
133
+ return { writeTables }
134
+ }
135
+
136
+ const debugInfo = {
137
+ rebaseCount: 0,
138
+ advanceCount: 0,
139
+ rejectCount: 0,
140
+ }
141
+
142
+ const otelContext = otel.trace.setSpan(otel.context.active(), span)
143
+
144
+ const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
145
+ // eslint-disable-next-line unicorn/prefer-global-this
146
+ if (confirmUnsavedChanges && typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
147
+ const onBeforeUnload = (event: BeforeUnloadEvent) => {
148
+ if (syncStateRef.current.pending.length > 0) {
149
+ // Trigger the default browser dialog
150
+ event.preventDefault()
151
+ }
152
+ }
153
+
154
+ yield* Effect.acquireRelease(
155
+ Effect.sync(() => window.addEventListener('beforeunload', onBeforeUnload)),
156
+ () => Effect.sync(() => window.removeEventListener('beforeunload', onBeforeUnload)),
157
+ )
158
+ }
159
+
160
+ const leaderPushingFiberHandle = yield* FiberHandle.make()
161
+
162
+ const backgroundLeaderPushing = Effect.gen(function* () {
163
+ const batch = yield* BucketQueue.takeBetween(leaderPushQueue, 1, params.leaderPushBatchSize)
164
+ yield* clientSession.leaderThread.events.push(batch).pipe(
165
+ Effect.catchTag('LeaderAheadError', () => {
166
+ debugInfo.rejectCount++
167
+ return BucketQueue.clear(leaderPushQueue)
168
+ }),
169
+ )
170
+ }).pipe(Effect.forever, Effect.interruptible, Effect.tapCauseLogPretty)
171
+
172
+ yield* FiberHandle.run(leaderPushingFiberHandle, backgroundLeaderPushing)
173
+
174
+ const getMergeCounter = () =>
175
+ clientSession.sqliteDb.select<{ mergeCounter: number }>(
176
+ sql`SELECT mergeCounter FROM ${SystemTables.LEADER_MERGE_COUNTER_TABLE} WHERE id = 0`,
177
+ )[0]?.mergeCounter ?? 0
178
+
179
+ // NOTE We need to lazily call `.pull` as we want the cursor to be updated
180
+ yield* Stream.suspend(() =>
181
+ clientSession.leaderThread.events.pull({
182
+ cursor: { mergeCounter: getMergeCounter(), eventNum: syncStateRef.current.localHead },
183
+ }),
184
+ ).pipe(
185
+ Stream.tap(({ payload, mergeCounter: leaderMergeCounter }) =>
186
+ Effect.gen(function* () {
187
+ // yield* Effect.logDebug('ClientSessionSyncProcessor:pull', payload)
188
+
189
+ if (clientSession.devtools.enabled) {
190
+ yield* clientSession.devtools.pullLatch.await
191
+ }
192
+
193
+ const mergeResult = SyncState.merge({
194
+ syncState: syncStateRef.current,
195
+ payload,
196
+ isClientEvent,
197
+ isEqualEvent: LiveStoreEvent.isEqualEncoded,
198
+ })
199
+
200
+ if (mergeResult._tag === 'unexpected-error') {
201
+ return yield* Effect.fail(mergeResult.cause)
202
+ } else if (mergeResult._tag === 'reject') {
203
+ return shouldNeverHappen('Unexpected reject in client-session-sync-processor', mergeResult)
204
+ }
205
+
206
+ syncStateRef.current = mergeResult.newSyncState
207
+ syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
208
+
209
+ if (mergeResult._tag === 'rebase') {
210
+ span.addEvent('merge:pull:rebase', {
211
+ payloadTag: payload._tag,
212
+ payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
213
+ newEventsCount: mergeResult.newEvents.length,
214
+ rollbackCount: mergeResult.rollbackEvents.length,
215
+ res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
216
+ leaderMergeCounter,
217
+ })
218
+
219
+ debugInfo.rebaseCount++
220
+
221
+ yield* FiberHandle.clear(leaderPushingFiberHandle)
222
+
223
+ // Reset the leader push queue since we're rebasing and will push again
224
+ yield* BucketQueue.clear(leaderPushQueue)
225
+
226
+ yield* FiberHandle.run(leaderPushingFiberHandle, backgroundLeaderPushing)
227
+
228
+ if (LS_DEV) {
229
+ Effect.logDebug(
230
+ 'merge:pull:rebase: rollback',
231
+ mergeResult.rollbackEvents.length,
232
+ ...mergeResult.rollbackEvents.slice(0, 10).map((_) => _.toJSON()),
233
+ { leaderMergeCounter },
234
+ ).pipe(Effect.provide(runtime), Effect.runSync)
235
+ }
236
+
237
+ for (let i = mergeResult.rollbackEvents.length - 1; i >= 0; i--) {
238
+ const event = mergeResult.rollbackEvents[i]!
239
+ if (event.meta.sessionChangeset._tag !== 'no-op' && event.meta.sessionChangeset._tag !== 'unset') {
240
+ rollback(event.meta.sessionChangeset.data)
241
+ event.meta.sessionChangeset = { _tag: 'unset' }
242
+ }
243
+ }
244
+
245
+ yield* BucketQueue.offerAll(leaderPushQueue, mergeResult.newSyncState.pending)
246
+ } else {
247
+ span.addEvent('merge:pull:advance', {
248
+ payloadTag: payload._tag,
249
+ payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
250
+ newEventsCount: mergeResult.newEvents.length,
251
+ res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
252
+ leaderMergeCounter,
253
+ })
254
+
255
+ debugInfo.advanceCount++
256
+ }
257
+
258
+ if (mergeResult.newEvents.length === 0) return
259
+
260
+ const writeTables = new Set<string>()
261
+ for (const event of mergeResult.newEvents) {
262
+ // TODO apply changeset if available (will require tracking of write tables as well)
263
+ const decodedEventDef = Schema.decodeSync(eventSchema)(event)
264
+ const res = materializeEvent(decodedEventDef, { otelContext, withChangeset: true })
265
+ for (const table of res.writeTables) {
266
+ writeTables.add(table)
267
+ }
268
+
269
+ event.meta.sessionChangeset = res.sessionChangeset
270
+ }
271
+
272
+ refreshTables(writeTables)
273
+ }).pipe(
274
+ Effect.tapCauseLogPretty,
275
+ Effect.catchAllCause((cause) => clientSession.shutdown(cause)),
276
+ ),
277
+ ),
278
+ Stream.runDrain,
279
+ Effect.forever, // NOTE Whenever the leader changes, we need to re-start the stream
280
+ Effect.interruptible,
281
+ Effect.withSpan('client-session-sync-processor:pull'),
282
+ Effect.tapCauseLogPretty,
283
+ Effect.forkScoped,
284
+ )
285
+ })
286
+
287
+ return {
288
+ push,
289
+ boot,
290
+ syncState: Subscribable.make({
291
+ get: Effect.gen(function* () {
292
+ const syncState = syncStateRef.current
293
+ if (syncStateRef === undefined) return shouldNeverHappen('Not initialized')
294
+ return syncState
295
+ }),
296
+ changes: Stream.fromQueue(syncStateUpdateQueue),
297
+ }),
298
+ debug: {
299
+ print: () =>
300
+ Effect.gen(function* () {
301
+ console.log('debugInfo', debugInfo)
302
+ console.log('syncState', syncStateRef.current)
303
+ const pushQueueSize = yield* BucketQueue.size(leaderPushQueue)
304
+ console.log('pushQueueSize', pushQueueSize)
305
+ const pushQueueItems = yield* BucketQueue.peekAll(leaderPushQueue)
306
+ console.log(
307
+ 'pushQueueItems',
308
+ pushQueueItems.map((_) => _.toJSON()),
309
+ )
310
+ }).pipe(Effect.provide(runtime), Effect.runSync),
311
+ debugInfo: () => debugInfo,
312
+ },
313
+ } satisfies ClientSessionSyncProcessor
314
+ }
315
+
316
+ export interface ClientSessionSyncProcessor {
317
+ push: (
318
+ batch: ReadonlyArray<LiveStoreEvent.PartialAnyDecoded>,
319
+ options: { otelContext: otel.Context },
320
+ ) => {
321
+ writeTables: Set<string>
322
+ }
323
+ boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
324
+ syncState: Subscribable.Subscribable<SyncState.SyncState>
325
+ debug: {
326
+ print: () => void
327
+ debugInfo: () => {
328
+ rebaseCount: number
329
+ advanceCount: number
330
+ }
331
+ }
332
+ }
package/src/sync/index.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from './sync.js'
2
2
  export * from './validate-push-payload.js'
3
- export * from './client-session-sync-processor.js'
3
+ export * from './ClientSessionSyncProcessor.js'