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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +20 -12
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  4. package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
  5. package/dist/adapter-types.d.ts +14 -6
  6. package/dist/adapter-types.d.ts.map +1 -1
  7. package/dist/adapter-types.js.map +1 -1
  8. package/dist/debug-info.d.ts.map +1 -1
  9. package/dist/debug-info.js +33 -6
  10. package/dist/debug-info.js.map +1 -1
  11. package/dist/devtools/devtools-messages-client-session.d.ts +28 -23
  12. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  13. package/dist/devtools/devtools-messages-client-session.js +2 -2
  14. package/dist/devtools/devtools-messages-client-session.js.map +1 -1
  15. package/dist/devtools/devtools-messages-common.d.ts +7 -14
  16. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  17. package/dist/devtools/devtools-messages-common.js +1 -6
  18. package/dist/devtools/devtools-messages-common.js.map +1 -1
  19. package/dist/devtools/devtools-messages-leader.d.ts +38 -29
  20. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  21. package/dist/devtools/devtools-messages-leader.js +9 -8
  22. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  23. package/dist/devtools/devtools-sessioninfo.d.ts +14 -2
  24. package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -1
  25. package/dist/devtools/devtools-sessioninfo.js +7 -4
  26. package/dist/devtools/devtools-sessioninfo.js.map +1 -1
  27. package/dist/devtools/mod.d.ts +13 -2
  28. package/dist/devtools/mod.d.ts.map +1 -1
  29. package/dist/devtools/mod.js +10 -3
  30. package/dist/devtools/mod.js.map +1 -1
  31. package/dist/errors.d.ts +52 -10
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +25 -6
  34. package/dist/errors.js.map +1 -1
  35. package/dist/index.d.ts +2 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/leader-thread/LeaderSyncProcessor.d.ts +41 -4
  40. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  41. package/dist/leader-thread/LeaderSyncProcessor.js +158 -75
  42. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  43. package/dist/leader-thread/eventlog.d.ts +21 -22
  44. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  45. package/dist/leader-thread/eventlog.js +77 -20
  46. package/dist/leader-thread/eventlog.js.map +1 -1
  47. package/dist/leader-thread/leader-worker-devtools.d.ts +2 -2
  48. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  49. package/dist/leader-thread/leader-worker-devtools.js +56 -45
  50. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  51. package/dist/leader-thread/make-leader-thread-layer.d.ts +6 -6
  52. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  53. package/dist/leader-thread/make-leader-thread-layer.js +79 -27
  54. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  55. package/dist/leader-thread/make-leader-thread-layer.test.d.ts +2 -0
  56. package/dist/leader-thread/make-leader-thread-layer.test.d.ts.map +1 -0
  57. package/dist/leader-thread/make-leader-thread-layer.test.js +32 -0
  58. package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -0
  59. package/dist/leader-thread/materialize-event.d.ts +3 -3
  60. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  61. package/dist/leader-thread/materialize-event.js +25 -11
  62. package/dist/leader-thread/materialize-event.js.map +1 -1
  63. package/dist/leader-thread/mod.d.ts +1 -0
  64. package/dist/leader-thread/mod.d.ts.map +1 -1
  65. package/dist/leader-thread/mod.js +1 -0
  66. package/dist/leader-thread/mod.js.map +1 -1
  67. package/dist/leader-thread/recreate-db.d.ts +2 -3
  68. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  69. package/dist/leader-thread/recreate-db.js +5 -5
  70. package/dist/leader-thread/recreate-db.js.map +1 -1
  71. package/dist/leader-thread/shutdown-channel.d.ts +2 -2
  72. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  73. package/dist/leader-thread/shutdown-channel.js +2 -2
  74. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  75. package/dist/leader-thread/stream-events.d.ts +56 -0
  76. package/dist/leader-thread/stream-events.d.ts.map +1 -0
  77. package/dist/leader-thread/stream-events.js +166 -0
  78. package/dist/leader-thread/stream-events.js.map +1 -0
  79. package/dist/leader-thread/types.d.ts +98 -20
  80. package/dist/leader-thread/types.d.ts.map +1 -1
  81. package/dist/leader-thread/types.js +13 -0
  82. package/dist/leader-thread/types.js.map +1 -1
  83. package/dist/logging.d.ts +40 -0
  84. package/dist/logging.d.ts.map +1 -0
  85. package/dist/logging.js +33 -0
  86. package/dist/logging.js.map +1 -0
  87. package/dist/make-client-session.d.ts +5 -3
  88. package/dist/make-client-session.d.ts.map +1 -1
  89. package/dist/make-client-session.js +5 -2
  90. package/dist/make-client-session.js.map +1 -1
  91. package/dist/materializer-helper.d.ts +6 -6
  92. package/dist/materializer-helper.d.ts.map +1 -1
  93. package/dist/materializer-helper.js +20 -4
  94. package/dist/materializer-helper.js.map +1 -1
  95. package/dist/otel.d.ts +2 -1
  96. package/dist/otel.d.ts.map +1 -1
  97. package/dist/otel.js +5 -0
  98. package/dist/otel.js.map +1 -1
  99. package/dist/rematerialize-from-eventlog.d.ts +2 -2
  100. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  101. package/dist/rematerialize-from-eventlog.js +29 -20
  102. package/dist/rematerialize-from-eventlog.js.map +1 -1
  103. package/dist/schema/EventDef/define.d.ts +147 -0
  104. package/dist/schema/EventDef/define.d.ts.map +1 -0
  105. package/dist/schema/EventDef/define.js +139 -0
  106. package/dist/schema/EventDef/define.js.map +1 -0
  107. package/dist/schema/EventDef/event-def.d.ts +106 -0
  108. package/dist/schema/EventDef/event-def.d.ts.map +1 -0
  109. package/dist/schema/EventDef/event-def.js +2 -0
  110. package/dist/schema/EventDef/event-def.js.map +1 -0
  111. package/dist/schema/EventDef/facts.d.ts +118 -0
  112. package/dist/schema/EventDef/facts.d.ts.map +1 -0
  113. package/dist/schema/EventDef/facts.js +53 -0
  114. package/dist/schema/EventDef/facts.js.map +1 -0
  115. package/dist/schema/EventDef/materializer.d.ts +155 -0
  116. package/dist/schema/EventDef/materializer.d.ts.map +1 -0
  117. package/dist/schema/EventDef/materializer.js +83 -0
  118. package/dist/schema/EventDef/materializer.js.map +1 -0
  119. package/dist/schema/EventDef/mod.d.ts +5 -0
  120. package/dist/schema/EventDef/mod.d.ts.map +1 -0
  121. package/dist/schema/EventDef/mod.js +5 -0
  122. package/dist/schema/EventDef/mod.js.map +1 -0
  123. package/dist/schema/EventSequenceNumber/client.d.ts +136 -0
  124. package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -0
  125. package/dist/schema/EventSequenceNumber/client.js +193 -0
  126. package/dist/schema/EventSequenceNumber/client.js.map +1 -0
  127. package/dist/schema/EventSequenceNumber/global.d.ts +15 -0
  128. package/dist/schema/EventSequenceNumber/global.d.ts.map +1 -0
  129. package/dist/schema/EventSequenceNumber/global.js +14 -0
  130. package/dist/schema/EventSequenceNumber/global.js.map +1 -0
  131. package/dist/schema/EventSequenceNumber/mod.d.ts +37 -0
  132. package/dist/schema/EventSequenceNumber/mod.d.ts.map +1 -0
  133. package/dist/schema/EventSequenceNumber/mod.js +37 -0
  134. package/dist/schema/EventSequenceNumber/mod.js.map +1 -0
  135. package/dist/schema/EventSequenceNumber.test.js +43 -43
  136. package/dist/schema/EventSequenceNumber.test.js.map +1 -1
  137. package/dist/schema/{LiveStoreEvent.d.ts → LiveStoreEvent/client.d.ts} +89 -106
  138. package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -0
  139. package/dist/schema/{LiveStoreEvent.js → LiveStoreEvent/client.js} +74 -58
  140. package/dist/schema/LiveStoreEvent/client.js.map +1 -0
  141. package/dist/schema/LiveStoreEvent/for-event-def.d.ts +52 -0
  142. package/dist/schema/LiveStoreEvent/for-event-def.d.ts.map +1 -0
  143. package/dist/schema/LiveStoreEvent/for-event-def.js +2 -0
  144. package/dist/schema/LiveStoreEvent/for-event-def.js.map +1 -0
  145. package/dist/schema/LiveStoreEvent/global.d.ts +36 -0
  146. package/dist/schema/LiveStoreEvent/global.d.ts.map +1 -0
  147. package/dist/schema/LiveStoreEvent/global.js +31 -0
  148. package/dist/schema/LiveStoreEvent/global.js.map +1 -0
  149. package/dist/schema/LiveStoreEvent/input.d.ts +46 -0
  150. package/dist/schema/LiveStoreEvent/input.d.ts.map +1 -0
  151. package/dist/schema/LiveStoreEvent/input.js +26 -0
  152. package/dist/schema/LiveStoreEvent/input.js.map +1 -0
  153. package/dist/schema/LiveStoreEvent/mod.d.ts +5 -0
  154. package/dist/schema/LiveStoreEvent/mod.d.ts.map +1 -0
  155. package/dist/schema/LiveStoreEvent/mod.js +5 -0
  156. package/dist/schema/LiveStoreEvent/mod.js.map +1 -0
  157. package/dist/schema/events.d.ts +1 -1
  158. package/dist/schema/events.d.ts.map +1 -1
  159. package/dist/schema/events.js +1 -1
  160. package/dist/schema/events.js.map +1 -1
  161. package/dist/schema/mod.d.ts +6 -4
  162. package/dist/schema/mod.d.ts.map +1 -1
  163. package/dist/schema/mod.js +5 -4
  164. package/dist/schema/mod.js.map +1 -1
  165. package/dist/schema/schema.d.ts +16 -1
  166. package/dist/schema/schema.d.ts.map +1 -1
  167. package/dist/schema/schema.js +27 -2
  168. package/dist/schema/schema.js.map +1 -1
  169. package/dist/schema/state/sqlite/client-document-def.d.ts +36 -6
  170. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  171. package/dist/schema/state/sqlite/client-document-def.js +97 -6
  172. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  173. package/dist/schema/state/sqlite/client-document-def.test.js +16 -0
  174. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  175. package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
  176. package/dist/schema/state/sqlite/column-annotations.js +14 -6
  177. package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
  178. package/dist/schema/state/sqlite/column-annotations.test.js +1 -1
  179. package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -1
  180. package/dist/schema/state/sqlite/column-def.js +69 -22
  181. package/dist/schema/state/sqlite/column-def.js.map +1 -1
  182. package/dist/schema/state/sqlite/column-def.test.js +48 -10
  183. package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
  184. package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
  185. package/dist/schema/state/sqlite/column-spec.js +30 -12
  186. package/dist/schema/state/sqlite/column-spec.js.map +1 -1
  187. package/dist/schema/state/sqlite/column-spec.test.js +23 -14
  188. package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
  189. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +2 -1
  190. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
  191. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +23 -6
  192. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
  193. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +14 -8
  194. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
  195. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +5 -3
  196. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
  197. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
  198. package/dist/schema/state/sqlite/db-schema/dsl/mod.js +2 -1
  199. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
  200. package/dist/schema/state/sqlite/mod.d.ts +3 -3
  201. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  202. package/dist/schema/state/sqlite/mod.js +3 -3
  203. package/dist/schema/state/sqlite/mod.js.map +1 -1
  204. package/dist/schema/state/sqlite/query-builder/api.d.ts +19 -11
  205. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  206. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
  207. package/dist/schema/state/sqlite/query-builder/astToSql.js +22 -15
  208. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
  209. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  210. package/dist/schema/state/sqlite/query-builder/impl.js +6 -3
  211. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  212. package/dist/schema/state/sqlite/query-builder/impl.test.js +252 -88
  213. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  214. package/dist/schema/state/sqlite/schema-helpers.d.ts +2 -2
  215. package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -1
  216. package/dist/schema/state/sqlite/schema-helpers.js +22 -12
  217. package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
  218. package/dist/schema/state/sqlite/schema-helpers.test.d.ts +2 -0
  219. package/dist/schema/state/sqlite/schema-helpers.test.d.ts.map +1 -0
  220. package/dist/schema/state/sqlite/schema-helpers.test.js +36 -0
  221. package/dist/schema/state/sqlite/schema-helpers.test.js.map +1 -0
  222. package/dist/schema/state/sqlite/{system-tables.d.ts → system-tables/eventlog-tables.d.ts} +63 -456
  223. package/dist/schema/state/sqlite/system-tables/eventlog-tables.d.ts.map +1 -0
  224. package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +54 -0
  225. package/dist/schema/state/sqlite/system-tables/eventlog-tables.js.map +1 -0
  226. package/dist/schema/state/sqlite/system-tables/mod.d.ts +3 -0
  227. package/dist/schema/state/sqlite/system-tables/mod.d.ts.map +1 -0
  228. package/dist/schema/state/sqlite/system-tables/mod.js +3 -0
  229. package/dist/schema/state/sqlite/system-tables/mod.js.map +1 -0
  230. package/dist/schema/state/sqlite/system-tables/state-tables.d.ts +456 -0
  231. package/dist/schema/state/sqlite/system-tables/state-tables.d.ts.map +1 -0
  232. package/dist/schema/state/sqlite/system-tables/state-tables.js +55 -0
  233. package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -0
  234. package/dist/schema/state/sqlite/table-def.d.ts +4 -4
  235. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  236. package/dist/schema/state/sqlite/table-def.js +2 -2
  237. package/dist/schema/state/sqlite/table-def.js.map +1 -1
  238. package/dist/schema/state/sqlite/table-def.test.js +80 -0
  239. package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
  240. package/dist/schema/unknown-events.d.ts +47 -0
  241. package/dist/schema/unknown-events.d.ts.map +1 -0
  242. package/dist/schema/unknown-events.js +69 -0
  243. package/dist/schema/unknown-events.js.map +1 -0
  244. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts +2 -0
  245. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.d.ts.map +1 -0
  246. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +73 -0
  247. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -0
  248. package/dist/schema-management/migrations.d.ts +32 -2
  249. package/dist/schema-management/migrations.d.ts.map +1 -1
  250. package/dist/schema-management/migrations.js +37 -5
  251. package/dist/schema-management/migrations.js.map +1 -1
  252. package/dist/schema-management/validate-schema.d.ts +3 -3
  253. package/dist/schema-management/validate-schema.d.ts.map +1 -1
  254. package/dist/schema-management/validate-schema.js +2 -2
  255. package/dist/schema-management/validate-schema.js.map +1 -1
  256. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  257. package/dist/sql-queries/sql-queries.js +11 -1
  258. package/dist/sql-queries/sql-queries.js.map +1 -1
  259. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  260. package/dist/sql-queries/sql-query-builder.js +2 -1
  261. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  262. package/dist/sqlite-types.d.ts +3 -3
  263. package/dist/sqlite-types.d.ts.map +1 -1
  264. package/dist/sync/ClientSessionSyncProcessor.d.ts +11 -13
  265. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  266. package/dist/sync/ClientSessionSyncProcessor.js +45 -42
  267. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  268. package/dist/sync/errors.d.ts +66 -0
  269. package/dist/sync/errors.d.ts.map +1 -0
  270. package/dist/sync/errors.js +36 -0
  271. package/dist/sync/errors.js.map +1 -0
  272. package/dist/sync/index.d.ts +3 -0
  273. package/dist/sync/index.d.ts.map +1 -1
  274. package/dist/sync/index.js +3 -0
  275. package/dist/sync/index.js.map +1 -1
  276. package/dist/sync/mock-sync-backend.d.ts +23 -0
  277. package/dist/sync/mock-sync-backend.d.ts.map +1 -0
  278. package/dist/sync/mock-sync-backend.js +114 -0
  279. package/dist/sync/mock-sync-backend.js.map +1 -0
  280. package/dist/sync/next/compact-events.d.ts.map +1 -1
  281. package/dist/sync/next/compact-events.js +6 -7
  282. package/dist/sync/next/compact-events.js.map +1 -1
  283. package/dist/sync/next/facts.d.ts +5 -5
  284. package/dist/sync/next/facts.d.ts.map +1 -1
  285. package/dist/sync/next/facts.js +1 -2
  286. package/dist/sync/next/facts.js.map +1 -1
  287. package/dist/sync/next/history-dag-common.d.ts +54 -15
  288. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  289. package/dist/sync/next/history-dag-common.js +198 -9
  290. package/dist/sync/next/history-dag-common.js.map +1 -1
  291. package/dist/sync/next/history-dag.d.ts.map +1 -1
  292. package/dist/sync/next/history-dag.js +10 -8
  293. package/dist/sync/next/history-dag.js.map +1 -1
  294. package/dist/sync/next/rebase-events.d.ts +5 -5
  295. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  296. package/dist/sync/next/rebase-events.js +5 -5
  297. package/dist/sync/next/rebase-events.js.map +1 -1
  298. package/dist/sync/next/test/event-fixtures.d.ts +2 -2
  299. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  300. package/dist/sync/next/test/event-fixtures.js +9 -9
  301. package/dist/sync/next/test/event-fixtures.js.map +1 -1
  302. package/dist/sync/sync-backend-kv.d.ts +7 -0
  303. package/dist/sync/sync-backend-kv.d.ts.map +1 -0
  304. package/dist/sync/sync-backend-kv.js +18 -0
  305. package/dist/sync/sync-backend-kv.js.map +1 -0
  306. package/dist/sync/sync-backend.d.ts +105 -0
  307. package/dist/sync/sync-backend.d.ts.map +1 -0
  308. package/dist/sync/sync-backend.js +61 -0
  309. package/dist/sync/sync-backend.js.map +1 -0
  310. package/dist/sync/sync.d.ts +9 -86
  311. package/dist/sync/sync.d.ts.map +1 -1
  312. package/dist/sync/sync.js +2 -27
  313. package/dist/sync/sync.js.map +1 -1
  314. package/dist/sync/syncstate.d.ts +57 -44
  315. package/dist/sync/syncstate.d.ts.map +1 -1
  316. package/dist/sync/syncstate.js +50 -45
  317. package/dist/sync/syncstate.js.map +1 -1
  318. package/dist/sync/syncstate.test.js +83 -46
  319. package/dist/sync/syncstate.test.js.map +1 -1
  320. package/dist/sync/transport-chunking.d.ts +36 -0
  321. package/dist/sync/transport-chunking.d.ts.map +1 -0
  322. package/dist/sync/transport-chunking.js +56 -0
  323. package/dist/sync/transport-chunking.js.map +1 -0
  324. package/dist/sync/validate-push-payload.d.ts +2 -2
  325. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  326. package/dist/sync/validate-push-payload.js +6 -6
  327. package/dist/sync/validate-push-payload.js.map +1 -1
  328. package/dist/testing/event-factory.d.ts +68 -0
  329. package/dist/testing/event-factory.d.ts.map +1 -0
  330. package/dist/testing/event-factory.js +78 -0
  331. package/dist/testing/event-factory.js.map +1 -0
  332. package/dist/testing/mod.d.ts +2 -0
  333. package/dist/testing/mod.d.ts.map +1 -0
  334. package/dist/testing/mod.js +2 -0
  335. package/dist/testing/mod.js.map +1 -0
  336. package/dist/version.d.ts +16 -6
  337. package/dist/version.d.ts.map +1 -1
  338. package/dist/version.js +16 -6
  339. package/dist/version.js.map +1 -1
  340. package/package.json +7 -8
  341. package/src/ClientSessionLeaderThreadProxy.ts +20 -12
  342. package/src/adapter-types.ts +18 -6
  343. package/src/debug-info.ts +37 -6
  344. package/src/devtools/devtools-messages-client-session.ts +2 -2
  345. package/src/devtools/devtools-messages-common.ts +1 -8
  346. package/src/devtools/devtools-messages-leader.ts +9 -8
  347. package/src/devtools/devtools-sessioninfo.ts +8 -5
  348. package/src/devtools/mod.ts +11 -2
  349. package/src/errors.ts +38 -11
  350. package/src/index.ts +2 -1
  351. package/src/leader-thread/LeaderSyncProcessor.ts +277 -105
  352. package/src/leader-thread/eventlog.ts +113 -38
  353. package/src/leader-thread/leader-worker-devtools.ts +86 -55
  354. package/src/leader-thread/make-leader-thread-layer.test.ts +44 -0
  355. package/src/leader-thread/make-leader-thread-layer.ts +156 -37
  356. package/src/leader-thread/materialize-event.ts +37 -12
  357. package/src/leader-thread/mod.ts +1 -0
  358. package/src/leader-thread/recreate-db.ts +15 -7
  359. package/src/leader-thread/shutdown-channel.ts +16 -2
  360. package/src/leader-thread/stream-events.ts +201 -0
  361. package/src/leader-thread/types.ts +70 -20
  362. package/src/logging.ts +62 -0
  363. package/src/make-client-session.ts +9 -3
  364. package/src/materializer-helper.ts +27 -10
  365. package/src/otel.ts +10 -0
  366. package/src/rematerialize-from-eventlog.ts +37 -27
  367. package/src/schema/EventDef/define.ts +201 -0
  368. package/src/schema/EventDef/event-def.ts +120 -0
  369. package/src/schema/EventDef/facts.ts +135 -0
  370. package/src/schema/EventDef/materializer.ts +172 -0
  371. package/src/schema/EventDef/mod.ts +4 -0
  372. package/src/schema/EventSequenceNumber/client.ts +257 -0
  373. package/src/schema/EventSequenceNumber/global.ts +19 -0
  374. package/src/schema/EventSequenceNumber/mod.ts +37 -0
  375. package/src/schema/EventSequenceNumber.test.ts +70 -52
  376. package/src/schema/LiveStoreEvent/client.ts +221 -0
  377. package/src/schema/LiveStoreEvent/for-event-def.ts +60 -0
  378. package/src/schema/LiveStoreEvent/global.ts +45 -0
  379. package/src/schema/LiveStoreEvent/input.ts +63 -0
  380. package/src/schema/LiveStoreEvent/mod.ts +4 -0
  381. package/src/schema/events.ts +1 -1
  382. package/src/schema/mod.ts +6 -4
  383. package/src/schema/schema.ts +39 -3
  384. package/src/schema/state/sqlite/client-document-def.test.ts +19 -2
  385. package/src/schema/state/sqlite/client-document-def.ts +127 -25
  386. package/src/schema/state/sqlite/column-annotations.test.ts +1 -1
  387. package/src/schema/state/sqlite/column-annotations.ts +16 -6
  388. package/src/schema/state/sqlite/column-def.test.ts +62 -10
  389. package/src/schema/state/sqlite/column-def.ts +88 -21
  390. package/src/schema/state/sqlite/column-spec.test.ts +29 -16
  391. package/src/schema/state/sqlite/column-spec.ts +36 -11
  392. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +26 -6
  393. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +29 -12
  394. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +12 -17
  395. package/src/schema/state/sqlite/mod.ts +4 -3
  396. package/src/schema/state/sqlite/query-builder/api.ts +25 -11
  397. package/src/schema/state/sqlite/query-builder/astToSql.ts +23 -14
  398. package/src/schema/state/sqlite/query-builder/impl.test.ts +305 -92
  399. package/src/schema/state/sqlite/query-builder/impl.ts +8 -3
  400. package/src/schema/state/sqlite/schema-helpers.test.ts +44 -0
  401. package/src/schema/state/sqlite/schema-helpers.ts +28 -20
  402. package/src/schema/state/sqlite/system-tables/eventlog-tables.ts +64 -0
  403. package/src/schema/state/sqlite/system-tables/mod.ts +2 -0
  404. package/src/schema/state/sqlite/system-tables/state-tables.ts +69 -0
  405. package/src/schema/state/sqlite/table-def.test.ts +101 -0
  406. package/src/schema/state/sqlite/table-def.ts +8 -6
  407. package/src/schema/unknown-events.ts +131 -0
  408. package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +86 -0
  409. package/src/schema-management/migrations.ts +41 -8
  410. package/src/schema-management/validate-schema.ts +3 -3
  411. package/src/sql-queries/sql-queries.ts +9 -1
  412. package/src/sql-queries/sql-query-builder.ts +2 -1
  413. package/src/sqlite-types.ts +3 -3
  414. package/src/sync/ClientSessionSyncProcessor.ts +69 -62
  415. package/src/sync/errors.ts +38 -0
  416. package/src/sync/index.ts +3 -0
  417. package/src/sync/mock-sync-backend.ts +184 -0
  418. package/src/sync/next/compact-events.ts +6 -7
  419. package/src/sync/next/facts.ts +7 -9
  420. package/src/sync/next/history-dag-common.ts +277 -26
  421. package/src/sync/next/history-dag.ts +16 -10
  422. package/src/sync/next/rebase-events.ts +11 -11
  423. package/src/sync/next/test/event-fixtures.ts +11 -11
  424. package/src/sync/sync-backend-kv.ts +22 -0
  425. package/src/sync/sync-backend.ts +185 -0
  426. package/src/sync/sync.ts +9 -91
  427. package/src/sync/syncstate.test.ts +96 -52
  428. package/src/sync/syncstate.ts +69 -58
  429. package/src/sync/transport-chunking.ts +90 -0
  430. package/src/sync/validate-push-payload.ts +8 -9
  431. package/src/testing/event-factory.ts +131 -0
  432. package/src/testing/mod.ts +1 -0
  433. package/src/version.ts +16 -6
  434. package/dist/schema/EventDef.d.ts +0 -123
  435. package/dist/schema/EventDef.d.ts.map +0 -1
  436. package/dist/schema/EventDef.js +0 -46
  437. package/dist/schema/EventDef.js.map +0 -1
  438. package/dist/schema/EventSequenceNumber.d.ts +0 -80
  439. package/dist/schema/EventSequenceNumber.d.ts.map +0 -1
  440. package/dist/schema/EventSequenceNumber.js +0 -139
  441. package/dist/schema/EventSequenceNumber.js.map +0 -1
  442. package/dist/schema/LiveStoreEvent.d.ts.map +0 -1
  443. package/dist/schema/LiveStoreEvent.js.map +0 -1
  444. package/dist/schema/state/sqlite/system-tables.d.ts.map +0 -1
  445. package/dist/schema/state/sqlite/system-tables.js +0 -79
  446. package/dist/schema/state/sqlite/system-tables.js.map +0 -1
  447. package/dist/schema-management/migrations.test.d.ts +0 -2
  448. package/dist/schema-management/migrations.test.d.ts.map +0 -1
  449. package/dist/schema-management/migrations.test.js +0 -52
  450. package/dist/schema-management/migrations.test.js.map +0 -1
  451. package/dist/sync/next/graphology.d.ts +0 -8
  452. package/dist/sync/next/graphology.d.ts.map +0 -1
  453. package/dist/sync/next/graphology.js +0 -30
  454. package/dist/sync/next/graphology.js.map +0 -1
  455. package/dist/sync/next/graphology_.d.ts +0 -3
  456. package/dist/sync/next/graphology_.d.ts.map +0 -1
  457. package/dist/sync/next/graphology_.js +0 -3
  458. package/dist/sync/next/graphology_.js.map +0 -1
  459. package/src/schema/EventDef.ts +0 -219
  460. package/src/schema/EventSequenceNumber.ts +0 -199
  461. package/src/schema/LiveStoreEvent.ts +0 -287
  462. package/src/schema/state/sqlite/system-tables.ts +0 -104
  463. package/src/sync/next/ambient.d.ts +0 -3
  464. package/src/sync/next/graphology.ts +0 -41
  465. package/src/sync/next/graphology_.ts +0 -2
@@ -1,20 +1,51 @@
1
+ /**
2
+ * AUTOMATIC HASH-BASED SCHEMA MIGRATIONS
3
+ *
4
+ * This module implements automatic schema versioning using hash-based change detection.
5
+ *
6
+ * ⚠️ CRITICAL DISTINCTION:
7
+ * - STATE TABLES (safe to modify): Changes trigger rematerialization from eventlog
8
+ * - EVENTLOG TABLES (NEVER modify): Changes cause data loss - need manual versioning!
9
+ *
10
+ * How it works:
11
+ * 1. Each table's schema is hashed using SqliteAst.hash()
12
+ * 2. Hashes are stored in SCHEMA_META_TABLE after successful migrations
13
+ * 3. On app start, current schema hashes are compared with stored hashes
14
+ * 4. Mismatches trigger migrations:
15
+ * - State tables: Recreated and repopulated from eventlog (safe, no data loss)
16
+ * - Eventlog tables: Uses 'create-if-not-exists' (UNSAFE - causes data loss!)
17
+ *
18
+ * State Table Changes (SAFE):
19
+ * - User-defined tables are rebuilt from eventlog
20
+ * - System tables (schemaMetaTable, etc.) are recreated
21
+ * - Data preserved through rematerializeFromEventlog()
22
+ *
23
+ * Eventlog Table Changes (UNSAFE):
24
+ * - eventlogMetaTable, syncStatusTable changes cause "soft reset"
25
+ * - Old table becomes inaccessible (but remains in DB)
26
+ * - No automatic migration - effectively data loss
27
+ * - TODO: Implement proper EVENTLOG_PERSISTENCE_FORMAT_VERSION system
28
+ *
29
+ * See system-tables/state-tables.ts and system-tables/eventlog-tables.ts for detailed documentation on each table type.
30
+ */
31
+
1
32
  import { memoizeByStringifyArgs } from '@livestore/utils'
2
33
  import { Effect } from '@livestore/utils/effect'
3
34
 
4
35
  import type { SqliteDb } from '../adapter-types.ts'
5
36
  import type { MigrationsReport, MigrationsReportEntry } from '../defs.ts'
6
- import type { UnexpectedError } from '../errors.ts'
37
+ import type { UnknownError } from '../errors.ts'
7
38
  import type { LiveStoreSchema } from '../schema/mod.ts'
8
39
  import { makeColumnSpec } from '../schema/state/sqlite/column-spec.ts'
9
40
  import { SqliteAst } from '../schema/state/sqlite/db-schema/mod.ts'
10
- import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables.ts'
41
+ import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables/state-tables.ts'
11
42
  import {
12
43
  isStateSystemTable,
13
44
  SCHEMA_EVENT_DEFS_META_TABLE,
14
45
  SCHEMA_META_TABLE,
15
46
  schemaEventDefsMetaTable,
16
47
  stateSystemTables,
17
- } from '../schema/state/sqlite/system-tables.ts'
48
+ } from '../schema/state/sqlite/system-tables/state-tables.ts'
18
49
  import { sql } from '../util.ts'
19
50
  import type { SchemaManager } from './common.ts'
20
51
  import { dbExecute, dbSelect } from './common.ts'
@@ -56,7 +87,7 @@ export const migrateDb = ({
56
87
  db: SqliteDb
57
88
  schema: LiveStoreSchema
58
89
  onProgress?: (opts: { done: number; total: number }) => Effect.Effect<void>
59
- }): Effect.Effect<MigrationsReport, UnexpectedError> =>
90
+ }): Effect.Effect<MigrationsReport, UnknownError> =>
60
91
  Effect.gen(function* () {
61
92
  for (const tableDef of stateSystemTables) {
62
93
  yield* migrateTable({
@@ -137,10 +168,10 @@ export const migrateTable = ({
137
168
 
138
169
  if (behaviour === 'drop-and-recreate') {
139
170
  // TODO need to possibly handle cascading deletes due to foreign keys
140
- dbExecute(db, sql`drop table if exists '${tableName}'`)
141
- dbExecute(db, sql`create table if not exists '${tableName}' (${columnSpec}) strict`)
171
+ dbExecute(db, sql`drop table if exists "${tableName}"`)
172
+ dbExecute(db, sql`create table if not exists "${tableName}" (${columnSpec}) strict`)
142
173
  } else if (behaviour === 'create-if-not-exists') {
143
- dbExecute(db, sql`create table if not exists '${tableName}' (${columnSpec}) strict`)
174
+ dbExecute(db, sql`create table if not exists "${tableName}" (${columnSpec}) strict`)
144
175
  }
145
176
 
146
177
  for (const index of tableAst.indexes) {
@@ -170,5 +201,7 @@ export const migrateTable = ({
170
201
 
171
202
  const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) => {
172
203
  const uniqueStr = index.unique ? 'UNIQUE' : ''
173
- return sql`create ${uniqueStr} index if not exists '${index.name}' on '${tableName}' (${index.columns.map((col) => `'${col}'`).join(', ')})`
204
+ return sql`create ${uniqueStr} index if not exists "${index.name}" on "${tableName}" (${index.columns
205
+ .map((col) => `"${col}"`)
206
+ .join(', ')})`
174
207
  }
@@ -1,7 +1,7 @@
1
1
  import { Effect, Schema } from '@livestore/utils/effect'
2
2
 
3
- import { UnexpectedError } from '../adapter-types.ts'
4
- import type { EventDef } from '../schema/EventDef.ts'
3
+ import { UnknownError } from '../adapter-types.ts'
4
+ import type { EventDef } from '../schema/EventDef/mod.ts'
5
5
  import type { LiveStoreSchema } from '../schema/mod.ts'
6
6
  import type { EventDefInfo, SchemaManager } from './common.ts'
7
7
 
@@ -15,7 +15,7 @@ export const validateSchema = (schema: LiveStoreSchema, schemaManager: SchemaMan
15
15
  )
16
16
 
17
17
  if (missingEventDefs.length > 0) {
18
- return yield* new UnexpectedError({
18
+ return yield* new UnknownError({
19
19
  cause: `Missing mutation definitions: ${missingEventDefs.map((info) => info.eventName).join(', ')}`,
20
20
  })
21
21
  }
@@ -247,7 +247,15 @@ export const createTable = ({
247
247
  .map(([columnName, _]) => columnName)
248
248
  const columnDefStrs = Object.entries(table.columns).map(([columnName, columnDef]) => {
249
249
  const nullModifier = columnDef.nullable === true ? '' : 'NOT NULL'
250
- const defaultModifier = columnDef.default._tag === 'None' ? '' : `DEFAULT ${columnDef.default.value}`
250
+ const defaultModifier = (() => {
251
+ if (columnDef.default._tag === 'None') return ''
252
+ const defaultValue = columnDef.default.value
253
+ if (typeof defaultValue === 'function') return ''
254
+ if (defaultValue && typeof defaultValue === 'object' && 'sql' in defaultValue) {
255
+ return `DEFAULT ${defaultValue.sql}`
256
+ }
257
+ return `DEFAULT ${defaultValue}`
258
+ })()
251
259
  return sql`${columnName} ${columnDef.columnType} ${nullModifier} ${defaultModifier}`
252
260
  })
253
261
 
@@ -1,3 +1,4 @@
1
+ import { omitUndefineds } from '@livestore/utils'
1
2
  import type { SqliteDsl } from '../schema/state/sqlite/db-schema/mod.ts'
2
3
  import type { BindValues } from './sql-queries.ts'
3
4
  import * as SqlQueries from './sql-queries.ts'
@@ -16,7 +17,7 @@ export const makeSqlQueryBuilder = <TSchema extends SqliteDsl.DbSchema>(schema:
16
17
  limit?: number
17
18
  }): [string, BindValues, TTableName] => {
18
19
  const columns = schema[tableName]!.columns
19
- const [stmt, bindValues] = SqlQueries.findManyRows({ columns, tableName, where, limit })
20
+ const [stmt, bindValues] = SqlQueries.findManyRows({ columns, tableName, where, ...omitUndefineds({ limit }) })
20
21
  return [stmt, bindValues, tableName]
21
22
  }
22
23
 
@@ -1,5 +1,5 @@
1
1
  import { type Effect, Schema } from '@livestore/utils/effect'
2
- import type { SqliteError, UnexpectedError } from './errors.ts'
2
+ import type { SqliteError, UnknownError } from './errors.ts'
3
3
  import type { EventSequenceNumber } from './schema/mod.ts'
4
4
  import type { QueryBuilder } from './schema/state/sqlite/query-builder/api.ts'
5
5
  import type { PreparedBindValues } from './util.ts'
@@ -33,7 +33,7 @@ export interface SqliteDb<TReq = any, TMetadata extends TReq = TReq> {
33
33
  makeChangeset: (data: Uint8Array<ArrayBuffer>) => SqliteDbChangeset
34
34
  }
35
35
 
36
- export type SqliteDebugInfo = { head: EventSequenceNumber.EventSequenceNumber }
36
+ export type SqliteDebugInfo = { head: EventSequenceNumber.Client.Composite }
37
37
 
38
38
  // TODO refactor this helper type. It's quite cumbersome to use and should be revisited.
39
39
  export type MakeSqliteDb<
@@ -46,7 +46,7 @@ export type MakeSqliteDb<
46
46
  TMetadata extends TMetadata_ & { _tag: TInput['_tag'] } = TMetadata_ & { _tag: TInput['_tag'] },
47
47
  >(
48
48
  input: TInput,
49
- ) => Effect.Effect<SqliteDb<TReq, Extract<TMetadata, { _tag: TInput['_tag'] }>>, SqliteError | UnexpectedError, R>
49
+ ) => Effect.Effect<SqliteDb<TReq, Extract<TMetadata, { _tag: TInput['_tag'] }>>, SqliteError | UnknownError, R>
50
50
 
51
51
  export interface PreparedStatement {
52
52
  execute(bindValues: PreparedBindValues | undefined, options?: { onRowsChanged?: (rowsChanged: number) => void }): void
@@ -13,12 +13,13 @@ import {
13
13
  Stream,
14
14
  Subscribable,
15
15
  } from '@livestore/utils/effect'
16
- import * as otel from '@opentelemetry/api'
16
+ import type * as otel from '@opentelemetry/api'
17
17
 
18
- import { type ClientSession, SyncError, type UnexpectedError } from '../adapter-types.ts'
19
- import * as EventSequenceNumber from '../schema/EventSequenceNumber.ts'
20
- import * as LiveStoreEvent from '../schema/LiveStoreEvent.ts'
21
- import { getEventDef, type LiveStoreSchema } from '../schema/mod.ts'
18
+ import { type ClientSession, UnknownError } from '../adapter-types.ts'
19
+ import type { MaterializeError } from '../errors.ts'
20
+ import * as EventSequenceNumber from '../schema/EventSequenceNumber/mod.ts'
21
+ import * as LiveStoreEvent from '../schema/LiveStoreEvent/mod.ts'
22
+ import type { LiveStoreSchema } from '../schema/mod.ts'
22
23
  import * as SyncState from './syncstate.ts'
23
24
 
24
25
  /**
@@ -51,16 +52,19 @@ export const makeClientSessionSyncProcessor = ({
51
52
  clientSession: ClientSession
52
53
  runtime: Runtime.Runtime<Scope.Scope>
53
54
  materializeEvent: (
54
- eventDecoded: LiveStoreEvent.AnyDecoded,
55
- options: { otelContext: otel.Context; withChangeset: boolean; materializerHashLeader: Option.Option<number> },
56
- ) => {
57
- writeTables: Set<string>
58
- sessionChangeset:
59
- | { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
60
- | { _tag: 'no-op' }
61
- | { _tag: 'unset' }
62
- materializerHash: Option.Option<number>
63
- }
55
+ eventEncoded: LiveStoreEvent.Client.EncodedWithMeta,
56
+ options: { withChangeset: boolean; materializerHashLeader: Option.Option<number> },
57
+ ) => Effect.Effect<
58
+ {
59
+ writeTables: Set<string>
60
+ sessionChangeset:
61
+ | { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
62
+ | { _tag: 'no-op' }
63
+ | { _tag: 'unset' }
64
+ materializerHash: Option.Option<number>
65
+ },
66
+ MaterializeError
67
+ >
64
68
  rollback: (changeset: Uint8Array<ArrayBuffer>) => void
65
69
  refreshTables: (tables: Set<string>) => void
66
70
  span: otel.Span
@@ -74,7 +78,7 @@ export const makeClientSessionSyncProcessor = ({
74
78
  */
75
79
  confirmUnsavedChanges: boolean
76
80
  }): ClientSessionSyncProcessor => {
77
- const eventSchema = LiveStoreEvent.makeEventDefSchemaMemo(schema)
81
+ const eventSchema = LiveStoreEvent.Client.makeSchemaMemo(schema)
78
82
 
79
83
  const simSleep = <TKey extends keyof ClientSessionSyncProcessorSimulationParams>(
80
84
  key: TKey,
@@ -91,26 +95,30 @@ export const makeClientSessionSyncProcessor = ({
91
95
  }),
92
96
  }
93
97
 
94
- /** Only used for debugging / observability, it's not relied upon for correctness of the sync processor. */
98
+ /** Only used for debugging / observability / testing, it's not relied upon for correctness of the sync processor. */
95
99
  const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
96
- const isClientEvent = (eventEncoded: LiveStoreEvent.EncodedWithMeta) =>
97
- getEventDef(schema, eventEncoded.name).eventDef.options.clientOnly
100
+ const isClientEvent = (eventEncoded: LiveStoreEvent.Client.EncodedWithMeta) =>
101
+ schema.eventsDefsMap.get(eventEncoded.name)?.options.clientOnly ?? false
98
102
 
99
103
  /** We're queuing push requests to reduce the number of messages sent to the leader by batching them */
100
- const leaderPushQueue = BucketQueue.make<LiveStoreEvent.EncodedWithMeta>().pipe(Effect.runSync)
104
+ const leaderPushQueue = BucketQueue.make<LiveStoreEvent.Client.EncodedWithMeta>().pipe(Effect.runSync)
101
105
 
102
- const push: ClientSessionSyncProcessor['push'] = (batch, { otelContext }) => {
106
+ const push: ClientSessionSyncProcessor['push'] = Effect.fn('client-session-sync-processor:push')(function* (batch) {
103
107
  // TODO validate batch
104
108
 
105
109
  let baseEventSequenceNumber = syncStateRef.current.localHead
106
110
  const encodedEventDefs = batch.map(({ name, args }) => {
107
- const eventDef = getEventDef(schema, name)
108
- const nextNumPair = EventSequenceNumber.nextPair({
111
+ const eventDef = schema.eventsDefsMap.get(name)
112
+ if (eventDef === undefined) {
113
+ return shouldNeverHappen(`No event definition found for \`${name}\`.`)
114
+ }
115
+ const nextNumPair = EventSequenceNumber.Client.nextPair({
109
116
  seqNum: baseEventSequenceNumber,
110
- isClient: eventDef.eventDef.options.clientOnly,
117
+ isClient: eventDef.options.clientOnly,
118
+ rebaseGeneration: baseEventSequenceNumber.rebaseGeneration,
111
119
  })
112
120
  baseEventSequenceNumber = nextNumPair.seqNum
113
- return new LiveStoreEvent.EncodedWithMeta(
121
+ return new LiveStoreEvent.Client.EncodedWithMeta(
114
122
  Schema.encodeUnknownSync(eventSchema)({
115
123
  name,
116
124
  args,
@@ -125,36 +133,38 @@ export const makeClientSessionSyncProcessor = ({
125
133
  syncState: syncStateRef.current,
126
134
  payload: { _tag: 'local-push', newEvents: encodedEventDefs },
127
135
  isClientEvent,
128
- isEqualEvent: LiveStoreEvent.isEqualEncoded,
136
+ isEqualEvent: LiveStoreEvent.Client.isEqualEncoded,
129
137
  })
130
138
 
131
- if (mergeResult._tag === 'unexpected-error') {
132
- return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.message)
133
- }
134
-
135
- span.addEvent('local-push', {
139
+ yield* Effect.annotateCurrentSpan({
136
140
  batchSize: encodedEventDefs.length,
137
- mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
141
+ mergeResultTag: mergeResult._tag,
142
+ eventCounts: encodedEventDefs.reduce<Record<string, number>>((acc, event) => {
143
+ acc[event.name] = (acc[event.name] ?? 0) + 1
144
+ return acc
145
+ }, {}),
146
+ ...(TRACE_VERBOSE && { mergeResult: JSON.stringify(mergeResult) }),
138
147
  })
139
148
 
149
+ if (mergeResult._tag === 'unknown-error') {
150
+ return shouldNeverHappen('Unknown error in client-session-sync-processor', mergeResult.message)
151
+ }
152
+
140
153
  if (mergeResult._tag !== 'advance') {
141
154
  return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
142
155
  }
143
156
 
144
157
  syncStateRef.current = mergeResult.newSyncState
145
- syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
158
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
146
159
 
147
160
  // Materialize events to state
148
161
  const writeTables = new Set<string>()
149
162
  for (const event of mergeResult.newEvents) {
150
- // TODO avoid encoding and decoding here again
151
- const decodedEventDef = Schema.decodeSync(eventSchema)(event)
152
163
  const {
153
164
  writeTables: newWriteTables,
154
165
  sessionChangeset,
155
166
  materializerHash,
156
- } = materializeEvent(decodedEventDef, {
157
- otelContext,
167
+ } = yield* materializeEvent(event, {
158
168
  withChangeset: true,
159
169
  materializerHashLeader: Option.none(),
160
170
  })
@@ -167,10 +177,10 @@ export const makeClientSessionSyncProcessor = ({
167
177
 
168
178
  // Trigger push to leader
169
179
  // console.debug('pushToLeader', encodedEventDefs.length, ...encodedEventDefs.map((_) => _.toJSON()))
170
- BucketQueue.offerAll(leaderPushQueue, encodedEventDefs).pipe(Effect.runSync)
180
+ yield* BucketQueue.offerAll(leaderPushQueue, encodedEventDefs)
171
181
 
172
182
  return { writeTables }
173
- }
183
+ })
174
184
 
175
185
  const debugInfo = {
176
186
  rebaseCount: 0,
@@ -178,8 +188,6 @@ export const makeClientSessionSyncProcessor = ({
178
188
  rejectCount: 0,
179
189
  }
180
190
 
181
- const otelContext = otel.trace.setSpan(otel.context.active(), span)
182
-
183
191
  const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
184
192
  if (confirmUnsavedChanges && typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
185
193
  const onBeforeUnload = (event: BeforeUnloadEvent) => {
@@ -225,17 +233,16 @@ export const makeClientSessionSyncProcessor = ({
225
233
  syncState: syncStateRef.current,
226
234
  payload,
227
235
  isClientEvent,
228
- isEqualEvent: LiveStoreEvent.isEqualEncoded,
236
+ isEqualEvent: LiveStoreEvent.Client.isEqualEncoded,
229
237
  })
230
238
 
231
- if (mergeResult._tag === 'unexpected-error') {
232
- return yield* new SyncError({ cause: mergeResult.message })
239
+ if (mergeResult._tag === 'unknown-error') {
240
+ return yield* new UnknownError({ cause: mergeResult.message })
233
241
  } else if (mergeResult._tag === 'reject') {
234
242
  return shouldNeverHappen('Unexpected reject in client-session-sync-processor', mergeResult)
235
243
  }
236
244
 
237
245
  syncStateRef.current = mergeResult.newSyncState
238
- yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
239
246
 
240
247
  if (mergeResult._tag === 'rebase') {
241
248
  span.addEvent('merge:pull:rebase', {
@@ -244,7 +251,6 @@ export const makeClientSessionSyncProcessor = ({
244
251
  newEventsCount: mergeResult.newEvents.length,
245
252
  rollbackCount: mergeResult.rollbackEvents.length,
246
253
  res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
247
- rebaseGeneration: mergeResult.newSyncState.localHead.rebaseGeneration,
248
254
  })
249
255
 
250
256
  debugInfo.rebaseCount++
@@ -294,18 +300,19 @@ export const makeClientSessionSyncProcessor = ({
294
300
  debugInfo.advanceCount++
295
301
  }
296
302
 
297
- if (mergeResult.newEvents.length === 0) return
303
+ if (mergeResult.newEvents.length === 0) {
304
+ // If there are no new events, we need to update the sync state as well
305
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
306
+ return
307
+ }
298
308
 
299
309
  const writeTables = new Set<string>()
300
310
  for (const event of mergeResult.newEvents) {
301
- // TODO apply changeset if available (will require tracking of write tables as well)
302
- const decodedEventDef = Schema.decodeSync(eventSchema)(event)
303
311
  const {
304
312
  writeTables: newWriteTables,
305
313
  sessionChangeset,
306
314
  materializerHash,
307
- } = materializeEvent(decodedEventDef, {
308
- otelContext,
315
+ } = yield* materializeEvent(event, {
309
316
  withChangeset: true,
310
317
  materializerHashLeader: event.meta.materializerHashLeader,
311
318
  })
@@ -318,6 +325,9 @@ export const makeClientSessionSyncProcessor = ({
318
325
  }
319
326
 
320
327
  refreshTables(writeTables)
328
+
329
+ // We're only triggering the sync state update after all events have been materialized
330
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
321
331
  }).pipe(
322
332
  Effect.tapCauseLogPretty,
323
333
  Effect.catchAllCause((cause) => clientSession.shutdown(Exit.failCause(cause))),
@@ -363,12 +373,9 @@ export const makeClientSessionSyncProcessor = ({
363
373
 
364
374
  export interface ClientSessionSyncProcessor {
365
375
  push: (
366
- batch: ReadonlyArray<LiveStoreEvent.PartialAnyDecoded>,
367
- options: { otelContext: otel.Context },
368
- ) => {
369
- writeTables: Set<string>
370
- }
371
- boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
376
+ batch: ReadonlyArray<LiveStoreEvent.Input.Decoded>,
377
+ ) => Effect.Effect<{ writeTables: Set<string> }, MaterializeError>
378
+ boot: Effect.Effect<void, UnknownError, Scope.Scope>
372
379
  /**
373
380
  * Only used for debugging / observability.
374
381
  */
@@ -388,11 +395,11 @@ const SIMULATION_ENABLED = true
388
395
  // Warning: High values for the simulation params can lead to very long test runs since those get multiplied with the number of events
389
396
  export const ClientSessionSyncProcessorSimulationParams = Schema.Struct({
390
397
  pull: Schema.Struct({
391
- '1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0, 25)),
392
- '2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0, 25)),
393
- '3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 25)),
394
- '4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 25)),
395
- '5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 25)),
398
+ '1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0, 15)),
399
+ '2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0, 15)),
400
+ '3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 15)),
401
+ '4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 15)),
402
+ '5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 15)),
396
403
  }),
397
404
  })
398
405
  type ClientSessionSyncProcessorSimulationParams = typeof ClientSessionSyncProcessorSimulationParams.Type
@@ -0,0 +1,38 @@
1
+ import { Schema } from '@livestore/utils/effect'
2
+ import { UnknownError } from '../errors.ts'
3
+ import { EventSequenceNumber } from '../schema/mod.ts'
4
+
5
+ export class IsOfflineError extends Schema.TaggedError<IsOfflineError>()('IsOfflineError', {
6
+ cause: Schema.Defect,
7
+ }) {}
8
+
9
+ /** Unique ID generated by the backend when its created. Used to check whether the backend identity has changed. */
10
+ export const BackendId = Schema.String.annotations({ title: '@livestore/sync-cf:BackendId' })
11
+
12
+ export class BackendIdMismatchError extends Schema.TaggedError<BackendIdMismatchError>()('BackendIdMismatchError', {
13
+ expected: BackendId,
14
+ received: BackendId,
15
+ }) {}
16
+
17
+ export class ServerAheadError extends Schema.TaggedError<ServerAheadError>()('ServerAheadError', {
18
+ minimumExpectedNum: EventSequenceNumber.Global.Schema,
19
+ providedNum: EventSequenceNumber.Global.Schema,
20
+ }) {}
21
+
22
+ export class InvalidPushError extends Schema.TaggedError<InvalidPushError>()('InvalidPushError', {
23
+ cause: Schema.Union(UnknownError, ServerAheadError, BackendIdMismatchError),
24
+ }) {}
25
+
26
+ export class InvalidPullError extends Schema.TaggedError<InvalidPullError>()('InvalidPullError', {
27
+ cause: Schema.Defect,
28
+ }) {}
29
+
30
+ export class LeaderAheadError extends Schema.TaggedError<LeaderAheadError>()('LeaderAheadError', {
31
+ minimumExpectedNum: EventSequenceNumber.Client.Composite,
32
+ providedNum: EventSequenceNumber.Client.Composite,
33
+ /** Generation number the client session should use for subsequent pushes */
34
+ // nextGeneration: Schema.Number,
35
+ }) {}
36
+
37
+ export const SyncError = Schema.Union(InvalidPushError, InvalidPullError)
38
+ export type SyncError = typeof SyncError.Type
package/src/sync/index.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export * from './ClientSessionSyncProcessor.ts'
2
+ export * from './mock-sync-backend.ts'
2
3
  export * from './sync.ts'
4
+ export { NetworkStatus } from './sync-backend.ts'
5
+ export * from './transport-chunking.ts'
3
6
  export * from './validate-push-payload.ts'
@@ -0,0 +1,184 @@
1
+ import type { Schema, Scope } from '@livestore/utils/effect'
2
+ import { Effect, Mailbox, Option, Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
3
+ import { UnknownError } from '../errors.ts'
4
+ import { EventSequenceNumber, type LiveStoreEvent } from '../schema/mod.ts'
5
+ import { InvalidPushError } from './errors.ts'
6
+ import * as SyncBackend from './sync-backend.ts'
7
+ import { validatePushPayload } from './validate-push-payload.ts'
8
+
9
+ export interface MockSyncBackend {
10
+ pushedEvents: Stream.Stream<LiveStoreEvent.Global.Encoded>
11
+ connect: Effect.Effect<void>
12
+ disconnect: Effect.Effect<void>
13
+ makeSyncBackend: Effect.Effect<SyncBackend.SyncBackend, UnknownError, Scope.Scope>
14
+ advance: (...batch: LiveStoreEvent.Global.Encoded[]) => Effect.Effect<void>
15
+ /** Fail the next N push calls with an InvalidPushError (or custom error) */
16
+ failNextPushes: (
17
+ count: number,
18
+ error?: (batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>,
19
+ ) => Effect.Effect<void>
20
+ }
21
+
22
+ export interface MockSyncBackendOptions {
23
+ /** Chunk size for non-live pulls; defaults to 100 */
24
+ nonLiveChunkSize?: number
25
+ /** Initial connected state; defaults to false */
26
+ startConnected?: boolean
27
+ // TODO add a "flaky" mode to simulate transient network / server failures for pull/push
28
+ }
29
+
30
+ export const makeMockSyncBackend = (
31
+ options?: MockSyncBackendOptions,
32
+ ): Effect.Effect<MockSyncBackend, UnknownError, Scope.Scope> =>
33
+ Effect.gen(function* () {
34
+ const syncEventSequenceNumberRef = { current: EventSequenceNumber.Client.ROOT.global }
35
+ const syncPullQueue = yield* Queue.unbounded<LiveStoreEvent.Global.Encoded>()
36
+ const pushedEventsQueue = yield* Mailbox.make<LiveStoreEvent.Global.Encoded>()
37
+ const syncIsConnectedRef = yield* SubscriptionRef.make(options?.startConnected ?? false)
38
+ const allEventsRef: { current: LiveStoreEvent.Global.Encoded[] } = { current: [] }
39
+
40
+ const span = yield* Effect.currentSpan.pipe(Effect.orDie)
41
+
42
+ const semaphore = yield* Effect.makeSemaphore(1)
43
+
44
+ // TODO improve the API and implementation of simulating errors
45
+ const failCounterRef = yield* SubscriptionRef.make(0)
46
+ const failEffectRef = yield* SubscriptionRef.make<
47
+ ((batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>) | undefined
48
+ >(undefined)
49
+
50
+ const makeSyncBackend = Effect.gen(function* () {
51
+ const nonLiveChunkSize = Math.max(1, options?.nonLiveChunkSize ?? 100)
52
+
53
+ // TODO consider making offline state actively error pull/push.
54
+ // Currently, offline only reflects in `isConnected`, while operations still succeed,
55
+ // mirroring how some real providers behave during transient disconnects.
56
+ return SyncBackend.of<Schema.JsonValue>({
57
+ isConnected: syncIsConnectedRef,
58
+ connect: SubscriptionRef.set(syncIsConnectedRef, true),
59
+ ping: Effect.void,
60
+ pull: (cursor, options) =>
61
+ (options?.live
62
+ ? Stream.concat(
63
+ Stream.make(SyncBackend.pullResItemEmpty()),
64
+ Stream.fromQueue(syncPullQueue).pipe(
65
+ Stream.chunks,
66
+ Stream.map((chunk) => ({
67
+ batch: [...chunk].map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
68
+ pageInfo: SyncBackend.pageInfoNoMore,
69
+ })),
70
+ ),
71
+ )
72
+ : Stream.fromEffect(
73
+ Effect.sync(() => {
74
+ const lastSeen = cursor.pipe(
75
+ Option.match({
76
+ onNone: () => EventSequenceNumber.Client.ROOT.global,
77
+ onSome: (_) => _.eventSequenceNumber,
78
+ }),
79
+ )
80
+ // All events with seqNum greater than lastSeen
81
+ const slice = allEventsRef.current.filter((e) => e.seqNum > lastSeen)
82
+ // Split into configured chunk size
83
+ const chunks: { events: LiveStoreEvent.Global.Encoded[]; remaining: number }[] = []
84
+ for (let i = 0; i < slice.length; i += nonLiveChunkSize) {
85
+ const end = Math.min(i + nonLiveChunkSize, slice.length)
86
+ const remaining = Math.max(slice.length - end, 0)
87
+ chunks.push({ events: slice.slice(i, end), remaining })
88
+ }
89
+ if (chunks.length === 0) {
90
+ chunks.push({ events: [], remaining: 0 })
91
+ }
92
+ return chunks
93
+ }),
94
+ ).pipe(
95
+ Stream.flatMap((chunks) =>
96
+ Stream.fromIterable(chunks).pipe(
97
+ Stream.map(({ events, remaining }) => ({
98
+ batch: events.map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
99
+ pageInfo: remaining > 0 ? SyncBackend.pageInfoMoreKnown(remaining) : SyncBackend.pageInfoNoMore,
100
+ })),
101
+ ),
102
+ ),
103
+ )
104
+ ).pipe(Stream.withSpan('MockSyncBackend:pull', { parent: span })),
105
+ push: (batch) =>
106
+ Effect.gen(function* () {
107
+ yield* validatePushPayload(batch, syncEventSequenceNumberRef.current)
108
+
109
+ const remaining = yield* SubscriptionRef.get(failCounterRef)
110
+ if (remaining > 0) {
111
+ const maybeFail = yield* SubscriptionRef.get(failEffectRef)
112
+ // decrement counter first
113
+ yield* SubscriptionRef.set(failCounterRef, remaining - 1)
114
+ if (maybeFail) {
115
+ return yield* maybeFail(batch)
116
+ }
117
+ return yield* new InvalidPushError({
118
+ cause: new UnknownError({ cause: new Error('MockSyncBackend: simulated push failure') }),
119
+ })
120
+ }
121
+
122
+ yield* Effect.sleep(10).pipe(Effect.withSpan('MockSyncBackend:push:sleep')) // Simulate network latency
123
+
124
+ yield* pushedEventsQueue.offerAll(batch)
125
+ yield* syncPullQueue.offerAll(batch)
126
+ allEventsRef.current = allEventsRef.current.concat(batch)
127
+
128
+ syncEventSequenceNumberRef.current = batch.at(-1)!.seqNum
129
+ }).pipe(
130
+ Effect.withSpan('MockSyncBackend:push', {
131
+ parent: span,
132
+ attributes: {
133
+ nums: batch.map((_) => _.seqNum),
134
+ },
135
+ }),
136
+ semaphore.withPermits(1),
137
+ ),
138
+ metadata: {
139
+ name: '@livestore/mock-sync',
140
+ description: 'Just a mock sync backend',
141
+ },
142
+ supports: {
143
+ pullPageInfoKnown: true,
144
+ pullLive: true,
145
+ },
146
+ })
147
+ })
148
+
149
+ const advance = (...batch: LiveStoreEvent.Global.Encoded[]) =>
150
+ Effect.gen(function* () {
151
+ syncEventSequenceNumberRef.current = batch.at(-1)!.seqNum
152
+ allEventsRef.current = allEventsRef.current.concat(batch)
153
+ yield* syncPullQueue.offerAll(batch)
154
+ }).pipe(
155
+ Effect.withSpan('MockSyncBackend:advance', {
156
+ parent: span,
157
+ attributes: { nums: batch.map((_) => _.seqNum) },
158
+ }),
159
+ semaphore.withPermits(1),
160
+ )
161
+
162
+ const connect = SubscriptionRef.set(syncIsConnectedRef, true)
163
+ const disconnect = SubscriptionRef.set(syncIsConnectedRef, false)
164
+
165
+ const failNextPushes = (
166
+ count: number,
167
+ error?: (batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>,
168
+ ) =>
169
+ Effect.gen(function* () {
170
+ yield* SubscriptionRef.set(failCounterRef, count)
171
+ yield* SubscriptionRef.set(failEffectRef, error)
172
+ })
173
+
174
+ return {
175
+ syncEventSequenceNumberRef,
176
+ syncPullQueue,
177
+ pushedEvents: Mailbox.toStream(pushedEventsQueue),
178
+ connect,
179
+ disconnect,
180
+ makeSyncBackend,
181
+ advance,
182
+ failNextPushes,
183
+ }
184
+ }).pipe(Effect.withSpanScoped('MockSyncBackend'))