@livestore/common 0.3.0-dev.9 → 0.3.1-dev.0

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 (479) hide show
  1. package/LICENSE +201 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/fixture.d.ts +83 -221
  4. package/dist/__tests__/fixture.d.ts.map +1 -1
  5. package/dist/__tests__/fixture.js +33 -11
  6. package/dist/__tests__/fixture.js.map +1 -1
  7. package/dist/adapter-types.d.ts +120 -64
  8. package/dist/adapter-types.d.ts.map +1 -1
  9. package/dist/adapter-types.js +39 -8
  10. package/dist/adapter-types.js.map +1 -1
  11. package/dist/bounded-collections.d.ts.map +1 -1
  12. package/dist/debug-info.d.ts +1 -1
  13. package/dist/debug-info.d.ts.map +1 -1
  14. package/dist/debug-info.js +1 -0
  15. package/dist/debug-info.js.map +1 -1
  16. package/dist/devtools/devtools-messages-client-session.d.ts +390 -0
  17. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -0
  18. package/dist/devtools/devtools-messages-client-session.js +97 -0
  19. package/dist/devtools/devtools-messages-client-session.js.map +1 -0
  20. package/dist/devtools/devtools-messages-common.d.ts +68 -0
  21. package/dist/devtools/devtools-messages-common.d.ts.map +1 -0
  22. package/dist/devtools/devtools-messages-common.js +60 -0
  23. package/dist/devtools/devtools-messages-common.js.map +1 -0
  24. package/dist/devtools/devtools-messages-leader.d.ts +394 -0
  25. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -0
  26. package/dist/devtools/devtools-messages-leader.js +147 -0
  27. package/dist/devtools/devtools-messages-leader.js.map +1 -0
  28. package/dist/devtools/devtools-messages.d.ts +3 -580
  29. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  30. package/dist/devtools/devtools-messages.js +3 -174
  31. package/dist/devtools/devtools-messages.js.map +1 -1
  32. package/dist/devtools/devtools-sessioninfo.d.ts +32 -0
  33. package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -0
  34. package/dist/devtools/devtools-sessioninfo.js +36 -0
  35. package/dist/devtools/devtools-sessioninfo.js.map +1 -0
  36. package/dist/devtools/mod.d.ts +55 -0
  37. package/dist/devtools/mod.d.ts.map +1 -0
  38. package/dist/devtools/mod.js +33 -0
  39. package/dist/devtools/mod.js.map +1 -0
  40. package/dist/index.d.ts +7 -9
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +7 -9
  43. package/dist/index.js.map +1 -1
  44. package/dist/leader-thread/LeaderSyncProcessor.d.ts +36 -11
  45. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  46. package/dist/leader-thread/LeaderSyncProcessor.js +426 -252
  47. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  48. package/dist/leader-thread/connection.d.ts +34 -6
  49. package/dist/leader-thread/connection.d.ts.map +1 -1
  50. package/dist/leader-thread/connection.js +22 -7
  51. package/dist/leader-thread/connection.js.map +1 -1
  52. package/dist/leader-thread/eventlog.d.ts +27 -0
  53. package/dist/leader-thread/eventlog.d.ts.map +1 -0
  54. package/dist/leader-thread/eventlog.js +119 -0
  55. package/dist/leader-thread/eventlog.js.map +1 -0
  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 +22 -9
  60. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  61. package/dist/leader-thread/make-leader-thread-layer.js +67 -45
  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 +28 -32
  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 +79 -38
  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/EventSequenceNumber.d.ts +57 -0
  104. package/dist/schema/EventSequenceNumber.d.ts.map +1 -0
  105. package/dist/schema/EventSequenceNumber.js +82 -0
  106. package/dist/schema/EventSequenceNumber.js.map +1 -0
  107. package/dist/schema/EventSequenceNumber.test.d.ts +2 -0
  108. package/dist/schema/EventSequenceNumber.test.d.ts.map +1 -0
  109. package/dist/schema/EventSequenceNumber.test.js +11 -0
  110. package/dist/schema/EventSequenceNumber.test.js.map +1 -0
  111. package/dist/schema/LiveStoreEvent.d.ts +257 -0
  112. package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
  113. package/dist/schema/LiveStoreEvent.js +117 -0
  114. package/dist/schema/LiveStoreEvent.js.map +1 -0
  115. package/dist/schema/events.d.ts +2 -0
  116. package/dist/schema/events.d.ts.map +1 -0
  117. package/dist/schema/events.js +2 -0
  118. package/dist/schema/events.js.map +1 -0
  119. package/dist/schema/mod.d.ts +8 -6
  120. package/dist/schema/mod.d.ts.map +1 -1
  121. package/dist/schema/mod.js +8 -6
  122. package/dist/schema/mod.js.map +1 -1
  123. package/dist/schema/schema.d.ts +50 -32
  124. package/dist/schema/schema.d.ts.map +1 -1
  125. package/dist/schema/schema.js +36 -43
  126. package/dist/schema/schema.js.map +1 -1
  127. package/dist/schema/state/mod.d.ts +3 -0
  128. package/dist/schema/state/mod.d.ts.map +1 -0
  129. package/dist/schema/state/mod.js +3 -0
  130. package/dist/schema/state/mod.js.map +1 -0
  131. package/dist/schema/state/sqlite/client-document-def.d.ts +223 -0
  132. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -0
  133. package/dist/schema/state/sqlite/client-document-def.js +170 -0
  134. package/dist/schema/state/sqlite/client-document-def.js.map +1 -0
  135. package/dist/schema/state/sqlite/client-document-def.test.d.ts +2 -0
  136. package/dist/schema/state/sqlite/client-document-def.test.d.ts.map +1 -0
  137. package/dist/schema/state/sqlite/client-document-def.test.js +201 -0
  138. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -0
  139. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +69 -0
  140. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -0
  141. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +71 -0
  142. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -0
  143. package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts +3 -0
  144. package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts.map +1 -0
  145. package/dist/schema/state/sqlite/db-schema/ast/validate.js +12 -0
  146. package/dist/schema/state/sqlite/db-schema/ast/validate.js.map +1 -0
  147. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +90 -0
  148. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -0
  149. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +87 -0
  150. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -0
  151. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.d.ts +2 -0
  152. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.d.ts.map +1 -0
  153. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +29 -0
  154. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -0
  155. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +90 -0
  156. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -0
  157. package/dist/schema/state/sqlite/db-schema/dsl/mod.js +41 -0
  158. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -0
  159. package/dist/schema/state/sqlite/db-schema/hash.d.ts +2 -0
  160. package/dist/schema/state/sqlite/db-schema/hash.d.ts.map +1 -0
  161. package/dist/schema/state/sqlite/db-schema/hash.js +14 -0
  162. package/dist/schema/state/sqlite/db-schema/hash.js.map +1 -0
  163. package/dist/schema/state/sqlite/db-schema/mod.d.ts +3 -0
  164. package/dist/schema/state/sqlite/db-schema/mod.d.ts.map +1 -0
  165. package/dist/schema/state/sqlite/db-schema/mod.js +3 -0
  166. package/dist/schema/state/sqlite/db-schema/mod.js.map +1 -0
  167. package/dist/schema/state/sqlite/mod.d.ts +17 -0
  168. package/dist/schema/state/sqlite/mod.d.ts.map +1 -0
  169. package/dist/schema/state/sqlite/mod.js +41 -0
  170. package/dist/schema/state/sqlite/mod.js.map +1 -0
  171. package/dist/schema/state/sqlite/query-builder/api.d.ts +294 -0
  172. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -0
  173. package/dist/schema/state/sqlite/query-builder/api.js +6 -0
  174. package/dist/schema/state/sqlite/query-builder/api.js.map +1 -0
  175. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts +7 -0
  176. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -0
  177. package/dist/schema/state/sqlite/query-builder/astToSql.js +190 -0
  178. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -0
  179. package/dist/schema/state/sqlite/query-builder/impl.d.ts +7 -0
  180. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -0
  181. package/dist/schema/state/sqlite/query-builder/impl.js +286 -0
  182. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -0
  183. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts +87 -0
  184. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts.map +1 -0
  185. package/dist/schema/state/sqlite/query-builder/impl.test.js +563 -0
  186. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -0
  187. package/dist/{query-builder → schema/state/sqlite/query-builder}/mod.d.ts +7 -0
  188. package/dist/schema/state/sqlite/query-builder/mod.d.ts.map +1 -0
  189. package/dist/{query-builder → schema/state/sqlite/query-builder}/mod.js +7 -0
  190. package/dist/schema/state/sqlite/query-builder/mod.js.map +1 -0
  191. package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -0
  192. package/dist/schema/{schema-helpers.js → state/sqlite/schema-helpers.js} +1 -1
  193. package/dist/schema/state/sqlite/schema-helpers.js.map +1 -0
  194. package/dist/schema/state/sqlite/system-tables.d.ts +574 -0
  195. package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -0
  196. package/dist/schema/state/sqlite/system-tables.js +88 -0
  197. package/dist/schema/state/sqlite/system-tables.js.map +1 -0
  198. package/dist/schema/state/sqlite/table-def.d.ts +84 -0
  199. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -0
  200. package/dist/schema/state/sqlite/table-def.js +36 -0
  201. package/dist/schema/state/sqlite/table-def.js.map +1 -0
  202. package/dist/schema-management/common.d.ts +7 -7
  203. package/dist/schema-management/common.d.ts.map +1 -1
  204. package/dist/schema-management/common.js.map +1 -1
  205. package/dist/schema-management/migrations.d.ts +6 -6
  206. package/dist/schema-management/migrations.d.ts.map +1 -1
  207. package/dist/schema-management/migrations.js +27 -18
  208. package/dist/schema-management/migrations.js.map +1 -1
  209. package/dist/schema-management/validate-schema.d.ts +8 -0
  210. package/dist/schema-management/validate-schema.d.ts.map +1 -0
  211. package/dist/schema-management/validate-schema.js +39 -0
  212. package/dist/schema-management/validate-schema.js.map +1 -0
  213. package/dist/sql-queries/misc.d.ts.map +1 -1
  214. package/dist/sql-queries/sql-queries.d.ts +1 -1
  215. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  216. package/dist/sql-queries/sql-queries.js.map +1 -1
  217. package/dist/sql-queries/sql-query-builder.d.ts +1 -1
  218. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  219. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  220. package/dist/sql-queries/types.d.ts +2 -1
  221. package/dist/sql-queries/types.d.ts.map +1 -1
  222. package/dist/sql-queries/types.js.map +1 -1
  223. package/dist/sync/ClientSessionSyncProcessor.d.ts +40 -19
  224. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  225. package/dist/sync/ClientSessionSyncProcessor.js +149 -73
  226. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  227. package/dist/sync/next/compact-events.d.ts.map +1 -1
  228. package/dist/sync/next/compact-events.js +38 -35
  229. package/dist/sync/next/compact-events.js.map +1 -1
  230. package/dist/sync/next/facts.d.ts +21 -21
  231. package/dist/sync/next/facts.d.ts.map +1 -1
  232. package/dist/sync/next/facts.js +11 -11
  233. package/dist/sync/next/facts.js.map +1 -1
  234. package/dist/sync/next/history-dag-common.d.ts +9 -7
  235. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  236. package/dist/sync/next/history-dag-common.js +10 -5
  237. package/dist/sync/next/history-dag-common.js.map +1 -1
  238. package/dist/sync/next/history-dag.d.ts +0 -2
  239. package/dist/sync/next/history-dag.d.ts.map +1 -1
  240. package/dist/sync/next/history-dag.js +16 -14
  241. package/dist/sync/next/history-dag.js.map +1 -1
  242. package/dist/sync/next/rebase-events.d.ts +10 -8
  243. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  244. package/dist/sync/next/rebase-events.js +18 -10
  245. package/dist/sync/next/rebase-events.js.map +1 -1
  246. package/dist/sync/next/test/compact-events.calculator.test.js +39 -34
  247. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  248. package/dist/sync/next/test/compact-events.test.js +77 -77
  249. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  250. package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +35 -25
  251. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
  252. package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +83 -38
  253. package/dist/sync/next/test/event-fixtures.js.map +1 -0
  254. package/dist/sync/next/test/mod.d.ts +1 -1
  255. package/dist/sync/next/test/mod.d.ts.map +1 -1
  256. package/dist/sync/next/test/mod.js +1 -1
  257. package/dist/sync/next/test/mod.js.map +1 -1
  258. package/dist/sync/sync.d.ts +46 -21
  259. package/dist/sync/sync.d.ts.map +1 -1
  260. package/dist/sync/sync.js +10 -6
  261. package/dist/sync/sync.js.map +1 -1
  262. package/dist/sync/syncstate.d.ts +193 -84
  263. package/dist/sync/syncstate.d.ts.map +1 -1
  264. package/dist/sync/syncstate.js +305 -151
  265. package/dist/sync/syncstate.js.map +1 -1
  266. package/dist/sync/syncstate.test.js +267 -303
  267. package/dist/sync/syncstate.test.js.map +1 -1
  268. package/dist/sync/validate-push-payload.d.ts +2 -2
  269. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  270. package/dist/sync/validate-push-payload.js +4 -4
  271. package/dist/sync/validate-push-payload.js.map +1 -1
  272. package/dist/util.d.ts +2 -2
  273. package/dist/util.d.ts.map +1 -1
  274. package/dist/version.d.ts +3 -3
  275. package/dist/version.js +3 -3
  276. package/package.json +11 -4
  277. package/src/__tests__/fixture.ts +36 -15
  278. package/src/adapter-types.ts +107 -68
  279. package/src/debug-info.ts +1 -0
  280. package/src/devtools/devtools-messages-client-session.ts +142 -0
  281. package/src/devtools/devtools-messages-common.ts +115 -0
  282. package/src/devtools/devtools-messages-leader.ts +191 -0
  283. package/src/devtools/devtools-messages.ts +3 -246
  284. package/src/devtools/devtools-sessioninfo.ts +101 -0
  285. package/src/devtools/mod.ts +59 -0
  286. package/src/index.ts +7 -9
  287. package/src/leader-thread/LeaderSyncProcessor.ts +664 -394
  288. package/src/leader-thread/connection.ts +54 -9
  289. package/src/leader-thread/eventlog.ts +199 -0
  290. package/src/leader-thread/leader-worker-devtools.ts +227 -104
  291. package/src/leader-thread/make-leader-thread-layer.ts +121 -72
  292. package/src/leader-thread/materialize-event.ts +173 -0
  293. package/src/leader-thread/mod.ts +1 -1
  294. package/src/leader-thread/recreate-db.ts +33 -38
  295. package/src/leader-thread/shutdown-channel.ts +2 -4
  296. package/src/leader-thread/types.ts +84 -46
  297. package/src/make-client-session.ts +136 -0
  298. package/src/materializer-helper.ts +138 -0
  299. package/src/otel.ts +8 -0
  300. package/src/rematerialize-from-eventlog.ts +117 -0
  301. package/src/schema/EventDef.ts +227 -0
  302. package/src/schema/EventSequenceNumber.test.ts +12 -0
  303. package/src/schema/EventSequenceNumber.ts +121 -0
  304. package/src/schema/LiveStoreEvent.ts +240 -0
  305. package/src/schema/events.ts +1 -0
  306. package/src/schema/mod.ts +8 -6
  307. package/src/schema/schema.ts +88 -84
  308. package/src/schema/state/mod.ts +2 -0
  309. package/src/schema/state/sqlite/client-document-def.test.ts +238 -0
  310. package/src/schema/state/sqlite/client-document-def.ts +444 -0
  311. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +142 -0
  312. package/src/schema/state/sqlite/db-schema/ast/validate.ts +13 -0
  313. package/src/schema/state/sqlite/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +206 -0
  314. package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +35 -0
  315. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +242 -0
  316. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +222 -0
  317. package/src/schema/state/sqlite/db-schema/hash.ts +14 -0
  318. package/src/schema/state/sqlite/db-schema/mod.ts +2 -0
  319. package/src/schema/state/sqlite/mod.ts +73 -0
  320. package/src/schema/state/sqlite/query-builder/api.ts +440 -0
  321. package/src/schema/state/sqlite/query-builder/astToSql.ts +232 -0
  322. package/src/schema/state/sqlite/query-builder/impl.test.ts +617 -0
  323. package/src/schema/state/sqlite/query-builder/impl.ts +351 -0
  324. package/src/{query-builder → schema/state/sqlite/query-builder}/mod.ts +7 -0
  325. package/src/schema/{schema-helpers.ts → state/sqlite/schema-helpers.ts} +1 -1
  326. package/src/schema/state/sqlite/system-tables.ts +117 -0
  327. package/src/schema/state/sqlite/table-def.ts +197 -0
  328. package/src/schema-management/common.ts +7 -7
  329. package/src/schema-management/migrations.ts +37 -31
  330. package/src/schema-management/validate-schema.ts +61 -0
  331. package/src/sql-queries/sql-queries.ts +1 -1
  332. package/src/sql-queries/sql-query-builder.ts +1 -2
  333. package/src/sql-queries/types.ts +3 -1
  334. package/src/sync/ClientSessionSyncProcessor.ts +218 -94
  335. package/src/sync/next/compact-events.ts +38 -35
  336. package/src/sync/next/facts.ts +43 -41
  337. package/src/sync/next/history-dag-common.ts +17 -10
  338. package/src/sync/next/history-dag.ts +16 -17
  339. package/src/sync/next/rebase-events.ts +29 -17
  340. package/src/sync/next/test/compact-events.calculator.test.ts +46 -46
  341. package/src/sync/next/test/compact-events.test.ts +79 -79
  342. package/src/sync/next/test/event-fixtures.ts +228 -0
  343. package/src/sync/next/test/mod.ts +1 -1
  344. package/src/sync/sync.ts +46 -21
  345. package/src/sync/syncstate.test.ts +312 -345
  346. package/src/sync/syncstate.ts +414 -224
  347. package/src/sync/validate-push-payload.ts +6 -6
  348. package/src/version.ts +3 -3
  349. package/dist/derived-mutations.d.ts +0 -109
  350. package/dist/derived-mutations.d.ts.map +0 -1
  351. package/dist/derived-mutations.js +0 -54
  352. package/dist/derived-mutations.js.map +0 -1
  353. package/dist/derived-mutations.test.d.ts +0 -2
  354. package/dist/derived-mutations.test.d.ts.map +0 -1
  355. package/dist/derived-mutations.test.js +0 -93
  356. package/dist/derived-mutations.test.js.map +0 -1
  357. package/dist/devtools/devtools-bridge.d.ts +0 -13
  358. package/dist/devtools/devtools-bridge.d.ts.map +0 -1
  359. package/dist/devtools/devtools-bridge.js +0 -2
  360. package/dist/devtools/devtools-bridge.js.map +0 -1
  361. package/dist/devtools/devtools-window-message.d.ts +0 -29
  362. package/dist/devtools/devtools-window-message.d.ts.map +0 -1
  363. package/dist/devtools/devtools-window-message.js +0 -33
  364. package/dist/devtools/devtools-window-message.js.map +0 -1
  365. package/dist/devtools/index.d.ts +0 -42
  366. package/dist/devtools/index.d.ts.map +0 -1
  367. package/dist/devtools/index.js +0 -48
  368. package/dist/devtools/index.js.map +0 -1
  369. package/dist/init-singleton-tables.d.ts +0 -4
  370. package/dist/init-singleton-tables.d.ts.map +0 -1
  371. package/dist/init-singleton-tables.js +0 -16
  372. package/dist/init-singleton-tables.js.map +0 -1
  373. package/dist/leader-thread/apply-mutation.d.ts +0 -11
  374. package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
  375. package/dist/leader-thread/apply-mutation.js +0 -107
  376. package/dist/leader-thread/apply-mutation.js.map +0 -1
  377. package/dist/leader-thread/leader-sync-processor.d.ts +0 -47
  378. package/dist/leader-thread/leader-sync-processor.d.ts.map +0 -1
  379. package/dist/leader-thread/leader-sync-processor.js +0 -430
  380. package/dist/leader-thread/leader-sync-processor.js.map +0 -1
  381. package/dist/leader-thread/mutationlog.d.ts +0 -10
  382. package/dist/leader-thread/mutationlog.d.ts.map +0 -1
  383. package/dist/leader-thread/mutationlog.js +0 -28
  384. package/dist/leader-thread/mutationlog.js.map +0 -1
  385. package/dist/leader-thread/pull-queue-set.d.ts +0 -7
  386. package/dist/leader-thread/pull-queue-set.d.ts.map +0 -1
  387. package/dist/leader-thread/pull-queue-set.js +0 -39
  388. package/dist/leader-thread/pull-queue-set.js.map +0 -1
  389. package/dist/mutation.d.ts +0 -20
  390. package/dist/mutation.d.ts.map +0 -1
  391. package/dist/mutation.js +0 -57
  392. package/dist/mutation.js.map +0 -1
  393. package/dist/query-builder/api.d.ts +0 -190
  394. package/dist/query-builder/api.d.ts.map +0 -1
  395. package/dist/query-builder/api.js +0 -8
  396. package/dist/query-builder/api.js.map +0 -1
  397. package/dist/query-builder/impl.d.ts +0 -12
  398. package/dist/query-builder/impl.d.ts.map +0 -1
  399. package/dist/query-builder/impl.js +0 -244
  400. package/dist/query-builder/impl.js.map +0 -1
  401. package/dist/query-builder/impl.test.d.ts +0 -2
  402. package/dist/query-builder/impl.test.d.ts.map +0 -1
  403. package/dist/query-builder/impl.test.js +0 -212
  404. package/dist/query-builder/impl.test.js.map +0 -1
  405. package/dist/query-builder/mod.d.ts.map +0 -1
  406. package/dist/query-builder/mod.js.map +0 -1
  407. package/dist/query-info.d.ts +0 -38
  408. package/dist/query-info.d.ts.map +0 -1
  409. package/dist/query-info.js +0 -7
  410. package/dist/query-info.js.map +0 -1
  411. package/dist/rehydrate-from-mutationlog.d.ts +0 -14
  412. package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
  413. package/dist/rehydrate-from-mutationlog.js +0 -66
  414. package/dist/rehydrate-from-mutationlog.js.map +0 -1
  415. package/dist/schema/EventId.d.ts +0 -39
  416. package/dist/schema/EventId.d.ts.map +0 -1
  417. package/dist/schema/EventId.js +0 -38
  418. package/dist/schema/EventId.js.map +0 -1
  419. package/dist/schema/EventId.test.d.ts +0 -2
  420. package/dist/schema/EventId.test.d.ts.map +0 -1
  421. package/dist/schema/EventId.test.js +0 -11
  422. package/dist/schema/EventId.test.js.map +0 -1
  423. package/dist/schema/MutationEvent.d.ts +0 -167
  424. package/dist/schema/MutationEvent.d.ts.map +0 -1
  425. package/dist/schema/MutationEvent.js +0 -72
  426. package/dist/schema/MutationEvent.js.map +0 -1
  427. package/dist/schema/MutationEvent.test.d.ts +0 -2
  428. package/dist/schema/MutationEvent.test.d.ts.map +0 -1
  429. package/dist/schema/MutationEvent.test.js +0 -2
  430. package/dist/schema/MutationEvent.test.js.map +0 -1
  431. package/dist/schema/mutations.d.ts +0 -107
  432. package/dist/schema/mutations.d.ts.map +0 -1
  433. package/dist/schema/mutations.js +0 -42
  434. package/dist/schema/mutations.js.map +0 -1
  435. package/dist/schema/schema-helpers.d.ts.map +0 -1
  436. package/dist/schema/schema-helpers.js.map +0 -1
  437. package/dist/schema/system-tables.d.ts +0 -399
  438. package/dist/schema/system-tables.d.ts.map +0 -1
  439. package/dist/schema/system-tables.js +0 -59
  440. package/dist/schema/system-tables.js.map +0 -1
  441. package/dist/schema/table-def.d.ts +0 -156
  442. package/dist/schema/table-def.d.ts.map +0 -1
  443. package/dist/schema/table-def.js +0 -79
  444. package/dist/schema/table-def.js.map +0 -1
  445. package/dist/schema-management/validate-mutation-defs.d.ts +0 -8
  446. package/dist/schema-management/validate-mutation-defs.d.ts.map +0 -1
  447. package/dist/schema-management/validate-mutation-defs.js +0 -39
  448. package/dist/schema-management/validate-mutation-defs.js.map +0 -1
  449. package/dist/sync/client-session-sync-processor.d.ts +0 -45
  450. package/dist/sync/client-session-sync-processor.d.ts.map +0 -1
  451. package/dist/sync/client-session-sync-processor.js +0 -131
  452. package/dist/sync/client-session-sync-processor.js.map +0 -1
  453. package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
  454. package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
  455. package/src/derived-mutations.test.ts +0 -101
  456. package/src/derived-mutations.ts +0 -170
  457. package/src/devtools/devtools-bridge.ts +0 -14
  458. package/src/devtools/devtools-window-message.ts +0 -27
  459. package/src/devtools/index.ts +0 -48
  460. package/src/init-singleton-tables.ts +0 -24
  461. package/src/leader-thread/apply-mutation.ts +0 -161
  462. package/src/leader-thread/mutationlog.ts +0 -46
  463. package/src/leader-thread/pull-queue-set.ts +0 -58
  464. package/src/mutation.ts +0 -91
  465. package/src/query-builder/api.ts +0 -289
  466. package/src/query-builder/impl.test.ts +0 -239
  467. package/src/query-builder/impl.ts +0 -285
  468. package/src/query-info.ts +0 -78
  469. package/src/rehydrate-from-mutationlog.ts +0 -119
  470. package/src/schema/EventId.test.ts +0 -12
  471. package/src/schema/EventId.ts +0 -60
  472. package/src/schema/MutationEvent.ts +0 -185
  473. package/src/schema/mutations.ts +0 -192
  474. package/src/schema/system-tables.ts +0 -105
  475. package/src/schema/table-def.ts +0 -343
  476. package/src/schema-management/validate-mutation-defs.ts +0 -63
  477. package/src/sync/next/test/mutation-fixtures.ts +0 -224
  478. package/tsconfig.json +0 -11
  479. /package/dist/schema/{schema-helpers.d.ts → state/sqlite/schema-helpers.d.ts} +0 -0
@@ -1,188 +1,284 @@
1
+ /// <reference lib="dom" />
1
2
  import { LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
2
- import type { Scope } from '@livestore/utils/effect'
3
- import { Effect, Schema, Stream } from '@livestore/utils/effect'
3
+ import type { Runtime, Scope } from '@livestore/utils/effect'
4
+ import { BucketQueue, Effect, FiberHandle, Queue, Schema, Stream, Subscribable } from '@livestore/utils/effect'
4
5
  import * as otel from '@opentelemetry/api'
5
6
 
6
- import type { ClientSessionLeaderThreadProxy, UnexpectedError } from '../adapter-types.js'
7
- import * as EventId from '../schema/EventId.js'
8
- import { type LiveStoreSchema } from '../schema/mod.js'
9
- import * as MutationEvent from '../schema/MutationEvent.js'
10
- import { SyncState, updateSyncState } from './syncstate.js'
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'
11
13
 
12
14
  /**
13
15
  * Rebase behaviour:
14
- * - We continously pull mutations from the leader and apply them to the local store.
16
+ * - We continously pull events from the leader and apply them to the local store.
15
17
  * - If there was a race condition (i.e. the leader and client session have both advacned),
16
- * we'll need to rebase the local pending mutations on top of the leader's head.
17
- * - The goal is to never block the UI, so we'll interrupt rebasing if a new mutations is pushed by the client session.
18
- * - We also want to avoid "backwards-jumping" in the UI, so we'll transactionally apply a read model changes during a rebase.
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.
19
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.
20
24
  */
21
25
  export const makeClientSessionSyncProcessor = ({
22
26
  schema,
23
- initialLeaderHead,
24
- pushToLeader,
25
- pullFromLeader,
26
- applyMutation,
27
+ clientSession,
28
+ runtime,
29
+ materializeEvent,
27
30
  rollback,
28
31
  refreshTables,
29
32
  span,
33
+ params,
34
+ confirmUnsavedChanges,
30
35
  }: {
31
36
  schema: LiveStoreSchema
32
- initialLeaderHead: EventId.EventId
33
- pushToLeader: (batch: ReadonlyArray<MutationEvent.AnyEncoded>) => void
34
- pullFromLeader: ClientSessionLeaderThreadProxy['mutations']['pull']
35
- applyMutation: (
36
- mutationEventDecoded: MutationEvent.PartialAnyDecoded,
37
+ clientSession: ClientSession
38
+ runtime: Runtime.Runtime<Scope.Scope>
39
+ materializeEvent: (
40
+ eventDecoded: LiveStoreEvent.PartialAnyDecoded,
37
41
  options: { otelContext: otel.Context; withChangeset: boolean },
38
42
  ) => {
39
43
  writeTables: Set<string>
40
- sessionChangeset: Uint8Array | undefined
44
+ sessionChangeset: { _tag: 'sessionChangeset'; data: Uint8Array; debug: any } | { _tag: 'no-op' } | { _tag: 'unset' }
41
45
  }
42
46
  rollback: (changeset: Uint8Array) => void
43
47
  refreshTables: (tables: Set<string>) => void
44
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
45
57
  }): ClientSessionSyncProcessor => {
46
- const mutationEventSchema = MutationEvent.makeMutationEventSchemaMemo(schema)
58
+ const eventSchema = LiveStoreEvent.makeEventDefSchemaMemo(schema)
47
59
 
48
60
  const syncStateRef = {
49
- current: new SyncState({
50
- localHead: initialLeaderHead,
51
- upstreamHead: initialLeaderHead,
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
52
66
  pending: [],
53
- // TODO init rollbackTail from leader to be ready for backend rebasing
54
- rollbackTail: [],
55
67
  }),
56
68
  }
57
69
 
58
- const isLocalEvent = (mutationEventEncoded: MutationEvent.EncodedWithMeta) => {
59
- const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
60
- return mutationDef.options.localOnly
61
- }
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)
62
76
 
63
77
  const push: ClientSessionSyncProcessor['push'] = (batch, { otelContext }) => {
64
78
  // TODO validate batch
65
79
 
66
- let baseEventId = syncStateRef.current.localHead
67
- const encodedMutationEvents = batch.map((mutationEvent) => {
68
- const mutationDef = schema.mutations.get(mutationEvent.mutation)!
69
- const nextIdPair = EventId.nextPair(baseEventId, mutationDef.options.localOnly)
70
- baseEventId = nextIdPair.id
71
- return new MutationEvent.EncodedWithMeta(
72
- Schema.encodeUnknownSync(mutationEventSchema)({ ...mutationEvent, ...nextIdPair }),
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
+ }),
73
93
  )
74
94
  })
75
95
 
76
- const updateResult = updateSyncState({
96
+ const mergeResult = SyncState.merge({
77
97
  syncState: syncStateRef.current,
78
- payload: { _tag: 'local-push', newEvents: encodedMutationEvents },
79
- isLocalEvent,
80
- isEqualEvent: MutationEvent.isEqualEncoded,
98
+ payload: { _tag: 'local-push', newEvents: encodedEventDefs },
99
+ isClientEvent,
100
+ isEqualEvent: LiveStoreEvent.isEqualEncoded,
81
101
  })
82
102
 
103
+ if (mergeResult._tag === 'unexpected-error') {
104
+ return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.cause)
105
+ }
106
+
83
107
  span.addEvent('local-push', {
84
- batchSize: encodedMutationEvents.length,
85
- updateResult: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
108
+ batchSize: encodedEventDefs.length,
109
+ mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
86
110
  })
87
111
 
88
- if (updateResult._tag !== 'advance') {
89
- return shouldNeverHappen(`Expected advance, got ${updateResult._tag}`)
112
+ if (mergeResult._tag !== 'advance') {
113
+ return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
90
114
  }
91
115
 
92
- syncStateRef.current = updateResult.newSyncState
116
+ syncStateRef.current = mergeResult.newSyncState
117
+ syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
93
118
 
94
119
  const writeTables = new Set<string>()
95
- for (const mutationEvent of updateResult.newEvents) {
120
+ for (const event of mergeResult.newEvents) {
96
121
  // TODO avoid encoding and decoding here again
97
- const decodedMutationEvent = Schema.decodeSync(mutationEventSchema)(mutationEvent)
98
- const res = applyMutation(decodedMutationEvent, { otelContext, withChangeset: true })
122
+ const decodedEventDef = Schema.decodeSync(eventSchema)(event)
123
+ const res = materializeEvent(decodedEventDef, { otelContext, withChangeset: true })
99
124
  for (const table of res.writeTables) {
100
125
  writeTables.add(table)
101
126
  }
102
- mutationEvent.meta.sessionChangeset = res.sessionChangeset
127
+ event.meta.sessionChangeset = res.sessionChangeset
103
128
  }
104
129
 
105
- // console.debug('pushToLeader', encodedMutationEvents.length, ...encodedMutationEvents.map((_) => _.toJSON()))
106
- pushToLeader(encodedMutationEvents)
130
+ // console.debug('pushToLeader', encodedEventDefs.length, ...encodedEventDefs.map((_) => _.toJSON()))
131
+ BucketQueue.offerAll(leaderPushQueue, encodedEventDefs).pipe(Effect.runSync)
107
132
 
108
133
  return { writeTables }
109
134
  }
110
135
 
136
+ const debugInfo = {
137
+ rebaseCount: 0,
138
+ advanceCount: 0,
139
+ rejectCount: 0,
140
+ }
141
+
111
142
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
112
143
 
113
144
  const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
114
- yield* pullFromLeader.pipe(
115
- Stream.tap(({ payload, remaining }) =>
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 }) =>
116
186
  Effect.gen(function* () {
117
- // console.log('pulled payload from leader', { payload, remaining })
187
+ // yield* Effect.logDebug('ClientSessionSyncProcessor:pull', payload)
188
+
189
+ if (clientSession.devtools.enabled) {
190
+ yield* clientSession.devtools.pullLatch.await
191
+ }
118
192
 
119
- const updateResult = updateSyncState({
193
+ const mergeResult = SyncState.merge({
120
194
  syncState: syncStateRef.current,
121
195
  payload,
122
- isLocalEvent,
123
- isEqualEvent: MutationEvent.isEqualEncoded,
196
+ isClientEvent,
197
+ isEqualEvent: LiveStoreEvent.isEqualEncoded,
124
198
  })
125
199
 
126
- if (updateResult._tag === 'reject') {
127
- debugger
128
- throw new Error('TODO: implement reject in client-session-sync-queue for pull')
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)
129
204
  }
130
205
 
131
- syncStateRef.current = updateResult.newSyncState
206
+ syncStateRef.current = mergeResult.newSyncState
207
+ syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
132
208
 
133
- if (updateResult._tag === 'rebase') {
134
- span.addEvent('pull:rebase', {
209
+ if (mergeResult._tag === 'rebase') {
210
+ span.addEvent('merge:pull:rebase', {
135
211
  payloadTag: payload._tag,
136
212
  payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
137
- newEventsCount: updateResult.newEvents.length,
138
- rollbackCount: updateResult.eventsToRollback.length,
139
- res: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
140
- remaining,
213
+ newEventsCount: mergeResult.newEvents.length,
214
+ rollbackCount: mergeResult.rollbackEvents.length,
215
+ res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
216
+ leaderMergeCounter,
141
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
+
142
228
  if (LS_DEV) {
143
- console.debug(
144
- 'pull:rebase: rollback',
145
- updateResult.eventsToRollback.length,
146
- ...updateResult.eventsToRollback.map((_) => _.toJSON()),
147
- )
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)
148
235
  }
149
236
 
150
- for (let i = updateResult.eventsToRollback.length - 1; i >= 0; i--) {
151
- const event = updateResult.eventsToRollback[i]!
152
- if (event.meta.sessionChangeset) {
153
- rollback(event.meta.sessionChangeset)
154
- event.meta.sessionChangeset = undefined
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' }
155
242
  }
156
243
  }
157
244
 
158
- pushToLeader(updateResult.newSyncState.pending)
245
+ yield* BucketQueue.offerAll(leaderPushQueue, mergeResult.newSyncState.pending)
159
246
  } else {
160
- span.addEvent('pull:advance', {
247
+ span.addEvent('merge:pull:advance', {
161
248
  payloadTag: payload._tag,
162
249
  payload: TRACE_VERBOSE ? JSON.stringify(payload) : undefined,
163
- newEventsCount: updateResult.newEvents.length,
164
- res: TRACE_VERBOSE ? JSON.stringify(updateResult) : undefined,
165
- remaining,
250
+ newEventsCount: mergeResult.newEvents.length,
251
+ res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
252
+ leaderMergeCounter,
166
253
  })
254
+
255
+ debugInfo.advanceCount++
167
256
  }
168
257
 
169
- if (updateResult.newEvents.length === 0) return
258
+ if (mergeResult.newEvents.length === 0) return
170
259
 
171
260
  const writeTables = new Set<string>()
172
- for (const mutationEvent of updateResult.newEvents) {
173
- const decodedMutationEvent = Schema.decodeSync(mutationEventSchema)(mutationEvent)
174
- const res = applyMutation(decodedMutationEvent, { otelContext, withChangeset: true })
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 })
175
265
  for (const table of res.writeTables) {
176
266
  writeTables.add(table)
177
267
  }
178
268
 
179
- mutationEvent.meta.sessionChangeset = res.sessionChangeset
269
+ event.meta.sessionChangeset = res.sessionChangeset
180
270
  }
181
271
 
182
272
  refreshTables(writeTables)
183
- }),
273
+ }).pipe(
274
+ Effect.tapCauseLogPretty,
275
+ Effect.catchAllCause((cause) => clientSession.shutdown(cause)),
276
+ ),
184
277
  ),
185
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'),
186
282
  Effect.tapCauseLogPretty,
187
283
  Effect.forkScoped,
188
284
  )
@@ -191,18 +287,46 @@ export const makeClientSessionSyncProcessor = ({
191
287
  return {
192
288
  push,
193
289
  boot,
194
- syncStateRef,
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
+ },
195
313
  } satisfies ClientSessionSyncProcessor
196
314
  }
197
315
 
198
316
  export interface ClientSessionSyncProcessor {
199
317
  push: (
200
- batch: ReadonlyArray<MutationEvent.PartialAnyDecoded>,
318
+ batch: ReadonlyArray<LiveStoreEvent.PartialAnyDecoded>,
201
319
  options: { otelContext: otel.Context },
202
320
  ) => {
203
321
  writeTables: Set<string>
204
322
  }
205
323
  boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
206
-
207
- syncStateRef: { current: SyncState }
324
+ syncState: Subscribable.Subscribable<SyncState.SyncState>
325
+ debug: {
326
+ print: () => void
327
+ debugInfo: () => {
328
+ rebaseCount: number
329
+ advanceCount: number
330
+ }
331
+ }
208
332
  }
@@ -1,6 +1,6 @@
1
+ import { EventSequenceNumber } from '../../schema/mod.js'
1
2
  import { replacesFacts } from './facts.js'
2
3
  import { graphologyDag } from './graphology_.js'
3
- import { eventIdToString } from './history-dag.js'
4
4
  import type { HistoryDag } from './history-dag-common.js'
5
5
  import { emptyHistoryDag } from './history-dag-common.js'
6
6
 
@@ -17,25 +17,25 @@ export const compactEvents = (inputDag: HistoryDag): { dag: HistoryDag; compacte
17
17
  const dag = inputDag.copy()
18
18
  const compactedEventCount = 0
19
19
 
20
- const orderedEventIdStrs = graphologyDag.topologicalSort(dag).reverse()
20
+ const orderedEventSequenceNumberStrs = graphologyDag.topologicalSort(dag).reverse()
21
21
 
22
22
  // drop root
23
- orderedEventIdStrs.pop()
23
+ orderedEventSequenceNumberStrs.pop()
24
24
 
25
- for (const eventIdStr of orderedEventIdStrs) {
26
- if (dag.hasNode(eventIdStr) === false) {
25
+ for (const eventNumStr of orderedEventSequenceNumberStrs) {
26
+ if (dag.hasNode(eventNumStr) === false) {
27
27
  continue
28
28
  }
29
29
 
30
- const subDagsForEvent = Array.from(makeSubDagsForEvent(dag, eventIdStr))
30
+ const subDagsForEvent = Array.from(makeSubDagsForEvent(dag, eventNumStr))
31
31
  for (const subDag of subDagsForEvent) {
32
32
  let shouldRetry = true
33
33
  while (shouldRetry) {
34
- const subDagsInHistory = findSubDagsInHistory(dag, subDag, eventIdStr)
34
+ const subDagsInHistory = findSubDagsInHistory(dag, subDag, eventNumStr)
35
35
 
36
36
  // console.debug(
37
37
  // 'subDagsInHistory',
38
- // eventIdStr,
38
+ // eventNumStr,
39
39
  // 'target',
40
40
  // subDag.nodes(),
41
41
  // 'found',
@@ -65,9 +65,9 @@ export const compactEvents = (inputDag: HistoryDag): { dag: HistoryDag; compacte
65
65
  return { dag, compactedEventCount }
66
66
  }
67
67
 
68
- function* makeSubDagsForEvent(inputDag: HistoryDag, eventIdStr: string): Generator<HistoryDag> {
69
- /** Map from eventIdStr to array of eventIdStrs that are dependencies */
70
- let nextIterationEls: Map<string, string[]> = new Map([[eventIdStr, []]])
68
+ function* makeSubDagsForEvent(inputDag: HistoryDag, eventNumStr: string): Generator<HistoryDag> {
69
+ /** Map from eventNumStr to array of eventNumStrs that are dependencies */
70
+ let nextIterationEls: Map<string, string[]> = new Map([[eventNumStr, []]])
71
71
  let previousDag: HistoryDag | undefined
72
72
 
73
73
  while (nextIterationEls.size > 0) {
@@ -77,19 +77,22 @@ function* makeSubDagsForEvent(inputDag: HistoryDag, eventIdStr: string): Generat
77
77
  const currentIterationEls = new Map(nextIterationEls)
78
78
  nextIterationEls = new Map()
79
79
 
80
- for (const [currentEventIdStr, edgeTargetIdStrs] of currentIterationEls) {
81
- const node = inputDag.getNodeAttributes(currentEventIdStr)
82
- if (subDag.hasNode(currentEventIdStr) === false) {
83
- subDag.addNode(currentEventIdStr, { ...node })
80
+ for (const [currentEventSequenceNumberStr, edgeTargetIdStrs] of currentIterationEls) {
81
+ const node = inputDag.getNodeAttributes(currentEventSequenceNumberStr)
82
+ if (subDag.hasNode(currentEventSequenceNumberStr) === false) {
83
+ subDag.addNode(currentEventSequenceNumberStr, { ...node })
84
84
  }
85
85
  for (const edgeTargetIdStr of edgeTargetIdStrs) {
86
- subDag.addEdge(currentEventIdStr, edgeTargetIdStr, { type: 'facts' })
86
+ subDag.addEdge(currentEventSequenceNumberStr, edgeTargetIdStr, { type: 'facts' })
87
87
  }
88
88
 
89
- for (const depEdge of inputDag.outboundEdgeEntries(currentEventIdStr)) {
89
+ for (const depEdge of inputDag.outboundEdgeEntries(currentEventSequenceNumberStr)) {
90
90
  if (depEdge.attributes.type === 'facts') {
91
- const depEventIdStr = depEdge.target
92
- nextIterationEls.set(depEventIdStr, [...(nextIterationEls.get(depEventIdStr) ?? []), currentEventIdStr])
91
+ const depEventSequenceNumberStr = depEdge.target
92
+ nextIterationEls.set(depEventSequenceNumberStr, [
93
+ ...(nextIterationEls.get(depEventSequenceNumberStr) ?? []),
94
+ currentEventSequenceNumberStr,
95
+ ])
93
96
  }
94
97
  }
95
98
  }
@@ -102,23 +105,23 @@ function* makeSubDagsForEvent(inputDag: HistoryDag, eventIdStr: string): Generat
102
105
  }
103
106
 
104
107
  /**
105
- * Iterates over all events from root to `upToExclEventIdStr`
108
+ * Iterates over all events from root to `upToExclEventSequenceNumberStr`
106
109
  * and collects all valid sub dags that are replaced by `targetSubDag`.
107
110
  */
108
111
  const findSubDagsInHistory = (
109
112
  inputDag: HistoryDag,
110
113
  targetSubDag: HistoryDag,
111
- upToExclEventIdStr: string,
114
+ upToExclEventSequenceNumberStr: string,
112
115
  ): { subDags: HistoryDag[]; allOutsideDependencies: string[][] } => {
113
116
  const subDags: HistoryDag[] = []
114
117
  const allOutsideDependencies: string[][] = []
115
118
 
116
- for (const eventIdStr of graphologyDag.topologicalSort(inputDag)) {
117
- if (eventIdStr === upToExclEventIdStr) {
119
+ for (const eventNumStr of graphologyDag.topologicalSort(inputDag)) {
120
+ if (eventNumStr === upToExclEventSequenceNumberStr) {
118
121
  break
119
122
  }
120
123
 
121
- for (const subDag of makeSubDagsForEvent(inputDag, eventIdStr)) {
124
+ for (const subDag of makeSubDagsForEvent(inputDag, eventNumStr)) {
122
125
  // console.debug('findSubDagsInHistory', 'target', targetSubDag.nodes(), 'subDag', subDag.nodes())
123
126
  if (subDag.size < targetSubDag.size) {
124
127
  continue
@@ -152,9 +155,9 @@ const outsideDependenciesForDag = (subDag: HistoryDag, inputDag: HistoryDag) =>
152
155
  for (const nodeIdStr of subDag.nodes()) {
153
156
  for (const edgeEntry of inputDag.outboundEdgeEntries(nodeIdStr)) {
154
157
  if (edgeEntry.attributes.type === 'facts') {
155
- const depEventIdStr = edgeEntry.target
156
- if (subDag.hasNode(depEventIdStr) === false) {
157
- outsideDependencies.push(depEventIdStr)
158
+ const depEventSequenceNumberStr = edgeEntry.target
159
+ if (subDag.hasNode(depEventSequenceNumberStr) === false) {
160
+ outsideDependencies.push(depEventSequenceNumberStr)
158
161
  }
159
162
  }
160
163
  }
@@ -201,19 +204,19 @@ const dagReplacesDag = (dagA: HistoryDag, dagB: HistoryDag): boolean => {
201
204
  return true
202
205
  }
203
206
 
204
- const removeEvent = (dag: HistoryDag, eventIdStr: string) => {
205
- // console.debug('removing event', eventIdStr)
206
- const event = dag.getNodeAttributes(eventIdStr)
207
- const parentIdStr = eventIdToString(event.parentId)
208
- const childEdges = dag.outboundEdgeEntries(eventIdStr)
207
+ const removeEvent = (dag: HistoryDag, eventNumStr: string) => {
208
+ // console.debug('removing event', eventNumStr)
209
+ const event = dag.getNodeAttributes(eventNumStr)
210
+ const parentSeqNumStr = EventSequenceNumber.toString(event.parentSeqNum)
211
+ const childEdges = dag.outboundEdgeEntries(eventNumStr)
209
212
 
210
213
  for (const childEdge of childEdges) {
211
214
  if (childEdge.attributes.type === 'parent') {
212
215
  const childEvent = dag.getNodeAttributes(childEdge.target)
213
- childEvent.parentId = { ...event.parentId }
214
- dag.addEdge(parentIdStr, eventIdToString(childEvent.id), { type: 'parent' })
216
+ childEvent.parentSeqNum = { ...event.parentSeqNum }
217
+ dag.addEdge(parentSeqNumStr, EventSequenceNumber.toString(childEvent.seqNum), { type: 'parent' })
215
218
  }
216
219
  }
217
220
 
218
- dag.dropNode(eventIdStr)
221
+ dag.dropNode(eventNumStr)
219
222
  }