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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (491) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +83 -221
  3. package/dist/__tests__/fixture.d.ts.map +1 -1
  4. package/dist/__tests__/fixture.js +33 -11
  5. package/dist/__tests__/fixture.js.map +1 -1
  6. package/dist/adapter-types.d.ts +120 -64
  7. package/dist/adapter-types.d.ts.map +1 -1
  8. package/dist/adapter-types.js +39 -8
  9. package/dist/adapter-types.js.map +1 -1
  10. package/dist/bounded-collections.d.ts.map +1 -1
  11. package/dist/debug-info.d.ts +1 -1
  12. package/dist/debug-info.d.ts.map +1 -1
  13. package/dist/debug-info.js +1 -0
  14. package/dist/debug-info.js.map +1 -1
  15. package/dist/devtools/devtools-messages-client-session.d.ts +390 -0
  16. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -0
  17. package/dist/devtools/devtools-messages-client-session.js +97 -0
  18. package/dist/devtools/devtools-messages-client-session.js.map +1 -0
  19. package/dist/devtools/devtools-messages-common.d.ts +68 -0
  20. package/dist/devtools/devtools-messages-common.d.ts.map +1 -0
  21. package/dist/devtools/devtools-messages-common.js +60 -0
  22. package/dist/devtools/devtools-messages-common.js.map +1 -0
  23. package/dist/devtools/devtools-messages-leader.d.ts +394 -0
  24. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -0
  25. package/dist/devtools/devtools-messages-leader.js +147 -0
  26. package/dist/devtools/devtools-messages-leader.js.map +1 -0
  27. package/dist/devtools/devtools-messages.d.ts +3 -580
  28. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  29. package/dist/devtools/devtools-messages.js +3 -174
  30. package/dist/devtools/devtools-messages.js.map +1 -1
  31. package/dist/devtools/devtools-sessioninfo.d.ts +32 -0
  32. package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -0
  33. package/dist/devtools/devtools-sessioninfo.js +36 -0
  34. package/dist/devtools/devtools-sessioninfo.js.map +1 -0
  35. package/dist/devtools/mod.d.ts +55 -0
  36. package/dist/devtools/mod.d.ts.map +1 -0
  37. package/dist/devtools/mod.js +33 -0
  38. package/dist/devtools/mod.js.map +1 -0
  39. package/dist/index.d.ts +7 -13
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +7 -9
  42. package/dist/index.js.map +1 -1
  43. package/dist/leader-thread/LeaderSyncProcessor.d.ts +62 -0
  44. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -0
  45. package/dist/leader-thread/LeaderSyncProcessor.js +595 -0
  46. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -0
  47. package/dist/leader-thread/connection.d.ts +34 -6
  48. package/dist/leader-thread/connection.d.ts.map +1 -1
  49. package/dist/leader-thread/connection.js +22 -7
  50. package/dist/leader-thread/connection.js.map +1 -1
  51. package/dist/leader-thread/eventlog.d.ts +27 -0
  52. package/dist/leader-thread/eventlog.d.ts.map +1 -0
  53. package/dist/leader-thread/eventlog.js +119 -0
  54. package/dist/leader-thread/eventlog.js.map +1 -0
  55. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  56. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  57. package/dist/leader-thread/leader-worker-devtools.js +155 -80
  58. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  59. package/dist/leader-thread/make-leader-thread-layer.d.ts +23 -11
  60. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  61. package/dist/leader-thread/make-leader-thread-layer.js +72 -47
  62. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  63. package/dist/leader-thread/materialize-event.d.ts +16 -0
  64. package/dist/leader-thread/materialize-event.d.ts.map +1 -0
  65. package/dist/leader-thread/materialize-event.js +109 -0
  66. package/dist/leader-thread/materialize-event.js.map +1 -0
  67. package/dist/leader-thread/mod.d.ts +1 -1
  68. package/dist/leader-thread/mod.d.ts.map +1 -1
  69. package/dist/leader-thread/mod.js +1 -1
  70. package/dist/leader-thread/mod.js.map +1 -1
  71. package/dist/leader-thread/recreate-db.d.ts +4 -2
  72. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  73. package/dist/leader-thread/recreate-db.js +33 -31
  74. package/dist/leader-thread/recreate-db.js.map +1 -1
  75. package/dist/leader-thread/shutdown-channel.d.ts +2 -5
  76. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  77. package/dist/leader-thread/shutdown-channel.js +2 -4
  78. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  79. package/dist/leader-thread/types.d.ts +87 -40
  80. package/dist/leader-thread/types.d.ts.map +1 -1
  81. package/dist/leader-thread/types.js +1 -3
  82. package/dist/leader-thread/types.js.map +1 -1
  83. package/dist/make-client-session.d.ts +23 -0
  84. package/dist/make-client-session.d.ts.map +1 -0
  85. package/dist/make-client-session.js +57 -0
  86. package/dist/make-client-session.js.map +1 -0
  87. package/dist/materializer-helper.d.ts +23 -0
  88. package/dist/materializer-helper.d.ts.map +1 -0
  89. package/dist/materializer-helper.js +86 -0
  90. package/dist/materializer-helper.js.map +1 -0
  91. package/dist/otel.d.ts +2 -0
  92. package/dist/otel.d.ts.map +1 -1
  93. package/dist/otel.js +5 -0
  94. package/dist/otel.js.map +1 -1
  95. package/dist/rematerialize-from-eventlog.d.ts +14 -0
  96. package/dist/rematerialize-from-eventlog.d.ts.map +1 -0
  97. package/dist/rematerialize-from-eventlog.js +64 -0
  98. package/dist/rematerialize-from-eventlog.js.map +1 -0
  99. package/dist/schema/EventDef.d.ts +146 -0
  100. package/dist/schema/EventDef.d.ts.map +1 -0
  101. package/dist/schema/EventDef.js +58 -0
  102. package/dist/schema/EventDef.js.map +1 -0
  103. package/dist/schema/EventId.d.ts +43 -25
  104. package/dist/schema/EventId.d.ts.map +1 -1
  105. package/dist/schema/EventId.js +56 -18
  106. package/dist/schema/EventId.js.map +1 -1
  107. package/dist/schema/EventId.test.d.ts +2 -0
  108. package/dist/schema/EventId.test.d.ts.map +1 -0
  109. package/dist/schema/EventId.test.js +11 -0
  110. package/dist/schema/EventId.test.js.map +1 -0
  111. package/dist/schema/EventNumber.d.ts +57 -0
  112. package/dist/schema/EventNumber.d.ts.map +1 -0
  113. package/dist/schema/EventNumber.js +82 -0
  114. package/dist/schema/EventNumber.js.map +1 -0
  115. package/dist/schema/EventNumber.test.d.ts +2 -0
  116. package/dist/schema/EventNumber.test.d.ts.map +1 -0
  117. package/dist/schema/EventNumber.test.js +11 -0
  118. package/dist/schema/EventNumber.test.js.map +1 -0
  119. package/dist/schema/EventSequenceNumber.d.ts +57 -0
  120. package/dist/schema/EventSequenceNumber.d.ts.map +1 -0
  121. package/dist/schema/EventSequenceNumber.js +82 -0
  122. package/dist/schema/EventSequenceNumber.js.map +1 -0
  123. package/dist/schema/EventSequenceNumber.test.d.ts +2 -0
  124. package/dist/schema/EventSequenceNumber.test.d.ts.map +1 -0
  125. package/dist/schema/EventSequenceNumber.test.js +11 -0
  126. package/dist/schema/EventSequenceNumber.test.js.map +1 -0
  127. package/dist/schema/LiveStoreEvent.d.ts +257 -0
  128. package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
  129. package/dist/schema/LiveStoreEvent.js +117 -0
  130. package/dist/schema/LiveStoreEvent.js.map +1 -0
  131. package/dist/schema/events.d.ts +2 -0
  132. package/dist/schema/events.d.ts.map +1 -0
  133. package/dist/schema/events.js +2 -0
  134. package/dist/schema/events.js.map +1 -0
  135. package/dist/schema/mod.d.ts +8 -6
  136. package/dist/schema/mod.d.ts.map +1 -1
  137. package/dist/schema/mod.js +8 -6
  138. package/dist/schema/mod.js.map +1 -1
  139. package/dist/schema/schema.d.ts +50 -32
  140. package/dist/schema/schema.d.ts.map +1 -1
  141. package/dist/schema/schema.js +36 -43
  142. package/dist/schema/schema.js.map +1 -1
  143. package/dist/schema/state/mod.d.ts +3 -0
  144. package/dist/schema/state/mod.d.ts.map +1 -0
  145. package/dist/schema/state/mod.js +3 -0
  146. package/dist/schema/state/mod.js.map +1 -0
  147. package/dist/schema/state/sqlite/client-document-def.d.ts +223 -0
  148. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -0
  149. package/dist/schema/state/sqlite/client-document-def.js +170 -0
  150. package/dist/schema/state/sqlite/client-document-def.js.map +1 -0
  151. package/dist/schema/state/sqlite/client-document-def.test.d.ts +2 -0
  152. package/dist/schema/state/sqlite/client-document-def.test.d.ts.map +1 -0
  153. package/dist/schema/state/sqlite/client-document-def.test.js +201 -0
  154. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -0
  155. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +69 -0
  156. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -0
  157. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +71 -0
  158. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -0
  159. package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts +3 -0
  160. package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts.map +1 -0
  161. package/dist/schema/state/sqlite/db-schema/ast/validate.js +12 -0
  162. package/dist/schema/state/sqlite/db-schema/ast/validate.js.map +1 -0
  163. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +90 -0
  164. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -0
  165. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +87 -0
  166. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -0
  167. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.d.ts +2 -0
  168. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.d.ts.map +1 -0
  169. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +29 -0
  170. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -0
  171. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +90 -0
  172. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -0
  173. package/dist/schema/state/sqlite/db-schema/dsl/mod.js +41 -0
  174. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -0
  175. package/dist/schema/state/sqlite/db-schema/hash.d.ts +2 -0
  176. package/dist/schema/state/sqlite/db-schema/hash.d.ts.map +1 -0
  177. package/dist/schema/state/sqlite/db-schema/hash.js +14 -0
  178. package/dist/schema/state/sqlite/db-schema/hash.js.map +1 -0
  179. package/dist/schema/state/sqlite/db-schema/mod.d.ts +3 -0
  180. package/dist/schema/state/sqlite/db-schema/mod.d.ts.map +1 -0
  181. package/dist/schema/state/sqlite/db-schema/mod.js +3 -0
  182. package/dist/schema/state/sqlite/db-schema/mod.js.map +1 -0
  183. package/dist/schema/state/sqlite/mod.d.ts +17 -0
  184. package/dist/schema/state/sqlite/mod.d.ts.map +1 -0
  185. package/dist/schema/state/sqlite/mod.js +41 -0
  186. package/dist/schema/state/sqlite/mod.js.map +1 -0
  187. package/dist/schema/state/sqlite/query-builder/api.d.ts +294 -0
  188. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -0
  189. package/dist/schema/state/sqlite/query-builder/api.js +6 -0
  190. package/dist/schema/state/sqlite/query-builder/api.js.map +1 -0
  191. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts +7 -0
  192. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -0
  193. package/dist/schema/state/sqlite/query-builder/astToSql.js +190 -0
  194. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -0
  195. package/dist/schema/state/sqlite/query-builder/impl.d.ts +7 -0
  196. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -0
  197. package/dist/schema/state/sqlite/query-builder/impl.js +286 -0
  198. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -0
  199. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts +87 -0
  200. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts.map +1 -0
  201. package/dist/schema/state/sqlite/query-builder/impl.test.js +563 -0
  202. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -0
  203. package/dist/{query-builder → schema/state/sqlite/query-builder}/mod.d.ts +7 -0
  204. package/dist/schema/state/sqlite/query-builder/mod.d.ts.map +1 -0
  205. package/dist/{query-builder → schema/state/sqlite/query-builder}/mod.js +7 -0
  206. package/dist/schema/state/sqlite/query-builder/mod.js.map +1 -0
  207. package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -0
  208. package/dist/schema/{schema-helpers.js → state/sqlite/schema-helpers.js} +1 -1
  209. package/dist/schema/state/sqlite/schema-helpers.js.map +1 -0
  210. package/dist/schema/state/sqlite/system-tables.d.ts +574 -0
  211. package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -0
  212. package/dist/schema/state/sqlite/system-tables.js +88 -0
  213. package/dist/schema/state/sqlite/system-tables.js.map +1 -0
  214. package/dist/schema/state/sqlite/table-def.d.ts +84 -0
  215. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -0
  216. package/dist/schema/state/sqlite/table-def.js +36 -0
  217. package/dist/schema/state/sqlite/table-def.js.map +1 -0
  218. package/dist/schema-management/common.d.ts +7 -7
  219. package/dist/schema-management/common.d.ts.map +1 -1
  220. package/dist/schema-management/common.js.map +1 -1
  221. package/dist/schema-management/migrations.d.ts +6 -6
  222. package/dist/schema-management/migrations.d.ts.map +1 -1
  223. package/dist/schema-management/migrations.js +27 -18
  224. package/dist/schema-management/migrations.js.map +1 -1
  225. package/dist/schema-management/validate-schema.d.ts +8 -0
  226. package/dist/schema-management/validate-schema.d.ts.map +1 -0
  227. package/dist/schema-management/validate-schema.js +39 -0
  228. package/dist/schema-management/validate-schema.js.map +1 -0
  229. package/dist/sql-queries/misc.d.ts.map +1 -1
  230. package/dist/sql-queries/sql-queries.d.ts +1 -1
  231. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  232. package/dist/sql-queries/sql-queries.js.map +1 -1
  233. package/dist/sql-queries/sql-query-builder.d.ts +1 -1
  234. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  235. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  236. package/dist/sql-queries/types.d.ts +2 -1
  237. package/dist/sql-queries/types.d.ts.map +1 -1
  238. package/dist/sql-queries/types.js.map +1 -1
  239. package/dist/sync/ClientSessionSyncProcessor.d.ts +66 -0
  240. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -0
  241. package/dist/sync/ClientSessionSyncProcessor.js +209 -0
  242. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -0
  243. package/dist/sync/index.d.ts +1 -1
  244. package/dist/sync/index.d.ts.map +1 -1
  245. package/dist/sync/index.js +1 -1
  246. package/dist/sync/index.js.map +1 -1
  247. package/dist/sync/next/compact-events.d.ts.map +1 -1
  248. package/dist/sync/next/compact-events.js +38 -35
  249. package/dist/sync/next/compact-events.js.map +1 -1
  250. package/dist/sync/next/facts.d.ts +21 -21
  251. package/dist/sync/next/facts.d.ts.map +1 -1
  252. package/dist/sync/next/facts.js +11 -11
  253. package/dist/sync/next/facts.js.map +1 -1
  254. package/dist/sync/next/history-dag-common.d.ts +9 -7
  255. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  256. package/dist/sync/next/history-dag-common.js +10 -5
  257. package/dist/sync/next/history-dag-common.js.map +1 -1
  258. package/dist/sync/next/history-dag.d.ts +0 -2
  259. package/dist/sync/next/history-dag.d.ts.map +1 -1
  260. package/dist/sync/next/history-dag.js +16 -14
  261. package/dist/sync/next/history-dag.js.map +1 -1
  262. package/dist/sync/next/rebase-events.d.ts +10 -8
  263. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  264. package/dist/sync/next/rebase-events.js +18 -10
  265. package/dist/sync/next/rebase-events.js.map +1 -1
  266. package/dist/sync/next/test/compact-events.calculator.test.js +39 -34
  267. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  268. package/dist/sync/next/test/compact-events.test.js +77 -77
  269. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  270. package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +38 -28
  271. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
  272. package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +81 -38
  273. package/dist/sync/next/test/event-fixtures.js.map +1 -0
  274. package/dist/sync/next/test/mod.d.ts +1 -1
  275. package/dist/sync/next/test/mod.d.ts.map +1 -1
  276. package/dist/sync/next/test/mod.js +1 -1
  277. package/dist/sync/next/test/mod.js.map +1 -1
  278. package/dist/sync/sync.d.ts +60 -25
  279. package/dist/sync/sync.d.ts.map +1 -1
  280. package/dist/sync/sync.js +10 -6
  281. package/dist/sync/sync.js.map +1 -1
  282. package/dist/sync/syncstate.d.ts +213 -82
  283. package/dist/sync/syncstate.d.ts.map +1 -1
  284. package/dist/sync/syncstate.js +337 -139
  285. package/dist/sync/syncstate.js.map +1 -1
  286. package/dist/sync/syncstate.test.js +310 -286
  287. package/dist/sync/syncstate.test.js.map +1 -1
  288. package/dist/sync/validate-push-payload.d.ts +2 -2
  289. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  290. package/dist/sync/validate-push-payload.js +4 -4
  291. package/dist/sync/validate-push-payload.js.map +1 -1
  292. package/dist/util.d.ts +2 -2
  293. package/dist/util.d.ts.map +1 -1
  294. package/dist/version.d.ts +2 -2
  295. package/dist/version.d.ts.map +1 -1
  296. package/dist/version.js +2 -2
  297. package/dist/version.js.map +1 -1
  298. package/package.json +13 -6
  299. package/src/__tests__/fixture.ts +36 -15
  300. package/src/adapter-types.ts +107 -68
  301. package/src/debug-info.ts +1 -0
  302. package/src/devtools/devtools-messages-client-session.ts +142 -0
  303. package/src/devtools/devtools-messages-common.ts +115 -0
  304. package/src/devtools/devtools-messages-leader.ts +191 -0
  305. package/src/devtools/devtools-messages.ts +3 -246
  306. package/src/devtools/devtools-sessioninfo.ts +101 -0
  307. package/src/devtools/mod.ts +59 -0
  308. package/src/index.ts +7 -15
  309. package/src/leader-thread/LeaderSyncProcessor.ts +940 -0
  310. package/src/leader-thread/connection.ts +54 -9
  311. package/src/leader-thread/eventlog.ts +199 -0
  312. package/src/leader-thread/leader-worker-devtools.ts +227 -104
  313. package/src/leader-thread/make-leader-thread-layer.ts +128 -78
  314. package/src/leader-thread/materialize-event.ts +173 -0
  315. package/src/leader-thread/mod.ts +1 -1
  316. package/src/leader-thread/recreate-db.ts +38 -39
  317. package/src/leader-thread/shutdown-channel.ts +2 -4
  318. package/src/leader-thread/types.ts +96 -50
  319. package/src/make-client-session.ts +136 -0
  320. package/src/materializer-helper.ts +138 -0
  321. package/src/otel.ts +8 -0
  322. package/src/rematerialize-from-eventlog.ts +117 -0
  323. package/src/schema/EventDef.ts +227 -0
  324. package/src/schema/EventSequenceNumber.test.ts +12 -0
  325. package/src/schema/EventSequenceNumber.ts +121 -0
  326. package/src/schema/LiveStoreEvent.ts +240 -0
  327. package/src/schema/events.ts +1 -0
  328. package/src/schema/mod.ts +8 -6
  329. package/src/schema/schema.ts +88 -84
  330. package/src/schema/state/mod.ts +2 -0
  331. package/src/schema/state/sqlite/client-document-def.test.ts +238 -0
  332. package/src/schema/state/sqlite/client-document-def.ts +444 -0
  333. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +142 -0
  334. package/src/schema/state/sqlite/db-schema/ast/validate.ts +13 -0
  335. package/src/schema/state/sqlite/db-schema/dsl/__snapshots__/field-defs.test.ts.snap +206 -0
  336. package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +35 -0
  337. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +242 -0
  338. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +222 -0
  339. package/src/schema/state/sqlite/db-schema/hash.ts +14 -0
  340. package/src/schema/state/sqlite/db-schema/mod.ts +2 -0
  341. package/src/schema/state/sqlite/mod.ts +73 -0
  342. package/src/schema/state/sqlite/query-builder/api.ts +440 -0
  343. package/src/schema/state/sqlite/query-builder/astToSql.ts +232 -0
  344. package/src/schema/state/sqlite/query-builder/impl.test.ts +617 -0
  345. package/src/schema/state/sqlite/query-builder/impl.ts +351 -0
  346. package/src/{query-builder → schema/state/sqlite/query-builder}/mod.ts +7 -0
  347. package/src/schema/{schema-helpers.ts → state/sqlite/schema-helpers.ts} +1 -1
  348. package/src/schema/state/sqlite/system-tables.ts +117 -0
  349. package/src/schema/state/sqlite/table-def.ts +197 -0
  350. package/src/schema-management/common.ts +7 -7
  351. package/src/schema-management/migrations.ts +37 -31
  352. package/src/schema-management/validate-schema.ts +61 -0
  353. package/src/sql-queries/sql-queries.ts +1 -1
  354. package/src/sql-queries/sql-query-builder.ts +1 -2
  355. package/src/sql-queries/types.ts +3 -1
  356. package/src/sync/ClientSessionSyncProcessor.ts +332 -0
  357. package/src/sync/index.ts +1 -1
  358. package/src/sync/next/compact-events.ts +38 -35
  359. package/src/sync/next/facts.ts +43 -41
  360. package/src/sync/next/history-dag-common.ts +17 -10
  361. package/src/sync/next/history-dag.ts +16 -17
  362. package/src/sync/next/rebase-events.ts +29 -17
  363. package/src/sync/next/test/compact-events.calculator.test.ts +46 -46
  364. package/src/sync/next/test/compact-events.test.ts +79 -79
  365. package/src/sync/next/test/event-fixtures.ts +226 -0
  366. package/src/sync/next/test/mod.ts +1 -1
  367. package/src/sync/sync.ts +60 -24
  368. package/src/sync/syncstate.test.ts +347 -320
  369. package/src/sync/syncstate.ts +422 -230
  370. package/src/sync/validate-push-payload.ts +6 -6
  371. package/src/version.ts +2 -2
  372. package/dist/derived-mutations.d.ts +0 -109
  373. package/dist/derived-mutations.d.ts.map +0 -1
  374. package/dist/derived-mutations.js +0 -54
  375. package/dist/derived-mutations.js.map +0 -1
  376. package/dist/derived-mutations.test.d.ts +0 -2
  377. package/dist/derived-mutations.test.d.ts.map +0 -1
  378. package/dist/derived-mutations.test.js +0 -93
  379. package/dist/derived-mutations.test.js.map +0 -1
  380. package/dist/devtools/devtools-bridge.d.ts +0 -13
  381. package/dist/devtools/devtools-bridge.d.ts.map +0 -1
  382. package/dist/devtools/devtools-bridge.js +0 -2
  383. package/dist/devtools/devtools-bridge.js.map +0 -1
  384. package/dist/devtools/devtools-window-message.d.ts +0 -29
  385. package/dist/devtools/devtools-window-message.d.ts.map +0 -1
  386. package/dist/devtools/devtools-window-message.js +0 -33
  387. package/dist/devtools/devtools-window-message.js.map +0 -1
  388. package/dist/devtools/index.d.ts +0 -42
  389. package/dist/devtools/index.d.ts.map +0 -1
  390. package/dist/devtools/index.js +0 -48
  391. package/dist/devtools/index.js.map +0 -1
  392. package/dist/init-singleton-tables.d.ts +0 -4
  393. package/dist/init-singleton-tables.d.ts.map +0 -1
  394. package/dist/init-singleton-tables.js +0 -16
  395. package/dist/init-singleton-tables.js.map +0 -1
  396. package/dist/leader-thread/apply-mutation.d.ts +0 -8
  397. package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
  398. package/dist/leader-thread/apply-mutation.js +0 -95
  399. package/dist/leader-thread/apply-mutation.js.map +0 -1
  400. package/dist/leader-thread/leader-sync-processor.d.ts +0 -47
  401. package/dist/leader-thread/leader-sync-processor.d.ts.map +0 -1
  402. package/dist/leader-thread/leader-sync-processor.js +0 -425
  403. package/dist/leader-thread/leader-sync-processor.js.map +0 -1
  404. package/dist/leader-thread/mutationlog.d.ts +0 -10
  405. package/dist/leader-thread/mutationlog.d.ts.map +0 -1
  406. package/dist/leader-thread/mutationlog.js +0 -28
  407. package/dist/leader-thread/mutationlog.js.map +0 -1
  408. package/dist/leader-thread/pull-queue-set.d.ts +0 -7
  409. package/dist/leader-thread/pull-queue-set.d.ts.map +0 -1
  410. package/dist/leader-thread/pull-queue-set.js +0 -39
  411. package/dist/leader-thread/pull-queue-set.js.map +0 -1
  412. package/dist/mutation.d.ts +0 -13
  413. package/dist/mutation.d.ts.map +0 -1
  414. package/dist/mutation.js +0 -57
  415. package/dist/mutation.js.map +0 -1
  416. package/dist/query-builder/api.d.ts +0 -190
  417. package/dist/query-builder/api.d.ts.map +0 -1
  418. package/dist/query-builder/api.js +0 -8
  419. package/dist/query-builder/api.js.map +0 -1
  420. package/dist/query-builder/impl.d.ts +0 -12
  421. package/dist/query-builder/impl.d.ts.map +0 -1
  422. package/dist/query-builder/impl.js +0 -244
  423. package/dist/query-builder/impl.js.map +0 -1
  424. package/dist/query-builder/impl.test.d.ts +0 -2
  425. package/dist/query-builder/impl.test.d.ts.map +0 -1
  426. package/dist/query-builder/impl.test.js +0 -212
  427. package/dist/query-builder/impl.test.js.map +0 -1
  428. package/dist/query-builder/mod.d.ts.map +0 -1
  429. package/dist/query-builder/mod.js.map +0 -1
  430. package/dist/query-info.d.ts +0 -38
  431. package/dist/query-info.d.ts.map +0 -1
  432. package/dist/query-info.js +0 -7
  433. package/dist/query-info.js.map +0 -1
  434. package/dist/rehydrate-from-mutationlog.d.ts +0 -14
  435. package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
  436. package/dist/rehydrate-from-mutationlog.js +0 -72
  437. package/dist/rehydrate-from-mutationlog.js.map +0 -1
  438. package/dist/schema/MutationEvent.d.ts +0 -166
  439. package/dist/schema/MutationEvent.d.ts.map +0 -1
  440. package/dist/schema/MutationEvent.js +0 -72
  441. package/dist/schema/MutationEvent.js.map +0 -1
  442. package/dist/schema/mutations.d.ts +0 -107
  443. package/dist/schema/mutations.d.ts.map +0 -1
  444. package/dist/schema/mutations.js +0 -42
  445. package/dist/schema/mutations.js.map +0 -1
  446. package/dist/schema/schema-helpers.d.ts.map +0 -1
  447. package/dist/schema/schema-helpers.js.map +0 -1
  448. package/dist/schema/system-tables.d.ts +0 -399
  449. package/dist/schema/system-tables.d.ts.map +0 -1
  450. package/dist/schema/system-tables.js +0 -58
  451. package/dist/schema/system-tables.js.map +0 -1
  452. package/dist/schema/table-def.d.ts +0 -156
  453. package/dist/schema/table-def.d.ts.map +0 -1
  454. package/dist/schema/table-def.js +0 -79
  455. package/dist/schema/table-def.js.map +0 -1
  456. package/dist/schema-management/validate-mutation-defs.d.ts +0 -8
  457. package/dist/schema-management/validate-mutation-defs.d.ts.map +0 -1
  458. package/dist/schema-management/validate-mutation-defs.js +0 -39
  459. package/dist/schema-management/validate-mutation-defs.js.map +0 -1
  460. package/dist/sync/client-session-sync-processor.d.ts +0 -45
  461. package/dist/sync/client-session-sync-processor.d.ts.map +0 -1
  462. package/dist/sync/client-session-sync-processor.js +0 -131
  463. package/dist/sync/client-session-sync-processor.js.map +0 -1
  464. package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
  465. package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
  466. package/src/derived-mutations.test.ts +0 -101
  467. package/src/derived-mutations.ts +0 -166
  468. package/src/devtools/devtools-bridge.ts +0 -14
  469. package/src/devtools/devtools-window-message.ts +0 -27
  470. package/src/devtools/index.ts +0 -48
  471. package/src/init-singleton-tables.ts +0 -24
  472. package/src/leader-thread/apply-mutation.ts +0 -143
  473. package/src/leader-thread/leader-sync-processor.ts +0 -670
  474. package/src/leader-thread/mutationlog.ts +0 -46
  475. package/src/leader-thread/pull-queue-set.ts +0 -58
  476. package/src/mutation.ts +0 -81
  477. package/src/query-builder/api.ts +0 -289
  478. package/src/query-builder/impl.test.ts +0 -239
  479. package/src/query-builder/impl.ts +0 -285
  480. package/src/query-info.ts +0 -78
  481. package/src/rehydrate-from-mutationlog.ts +0 -127
  482. package/src/schema/EventId.ts +0 -60
  483. package/src/schema/MutationEvent.ts +0 -180
  484. package/src/schema/mutations.ts +0 -192
  485. package/src/schema/system-tables.ts +0 -104
  486. package/src/schema/table-def.ts +0 -343
  487. package/src/schema-management/validate-mutation-defs.ts +0 -63
  488. package/src/sync/client-session-sync-processor.ts +0 -207
  489. package/src/sync/next/test/mutation-fixtures.ts +0 -224
  490. package/tsconfig.json +0 -11
  491. /package/dist/schema/{schema-helpers.d.ts → state/sqlite/schema-helpers.d.ts} +0 -0
@@ -1,221 +1,314 @@
1
- import { shouldNeverHappen } from '@livestore/utils'
2
- import { ReadonlyArray, Schema } from '@livestore/utils/effect'
1
+ import { casesHandled, LS_DEV, shouldNeverHappen } from '@livestore/utils'
2
+ import { Match, ReadonlyArray, Schema } from '@livestore/utils/effect'
3
3
 
4
- import * as EventId from '../schema/EventId.js'
5
- import * as MutationEvent from '../schema/MutationEvent.js'
4
+ import { UnexpectedError } from '../adapter-types.js'
5
+ import * as EventSequenceNumber from '../schema/EventSequenceNumber.js'
6
+ import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
6
7
 
7
8
  /**
8
9
  * SyncState represents the current sync state of a sync node relative to an upstream node.
9
10
  * Events flow from local to upstream, with each state maintaining its own event head.
10
11
  *
11
- * Event Chain Structure:
12
+ * Example:
12
13
  * ```
13
- * +-------------------------+------------------------+
14
- * | ROLLBACK TAIL | PENDING EVENTS |
15
- * +-------------------------+------------------------+
16
- * ▼ ▼
17
- * Upstream Head Local Head
18
- * Example: (0,0), (0,1), (1,0) (1,1), (1,2), (2,0)
14
+ * +------------------------+
15
+ * | PENDING EVENTS |
16
+ * +------------------------+
17
+ * ▼ ▼
18
+ * Upstream Head Local Head
19
+ * (1,0) (1,1), (1,2), (2,0)
19
20
  * ```
20
21
  *
21
- * State:
22
- * - **Pending Events**: Events awaiting acknowledgment from the upstream.
23
- * - Can be confirmed or rejected by the upstream.
24
- * - Subject to rebase if rejected.
25
- * - **Rollback Tail**: Events that are kept around temporarily for potential rollback until confirmed by upstream.
22
+ * **Pending Events**: Events awaiting acknowledgment from the upstream.
23
+ * - Can be confirmed or rejected by the upstream.
24
+ * - Subject to rebase if rejected.
26
25
  *
27
26
  * Payloads:
28
27
  * - `PayloadUpstreamRebase`: Upstream has performed a rebase, so downstream must roll back to the specified event
29
28
  * and rebase the pending events on top of the new events.
30
29
  * - `PayloadUpstreamAdvance`: Upstream has advanced, so downstream must rebase the pending events on top of the new events.
31
- * - `PayloadUpstreamTrimRollbackTail`: Upstream has advanced, so downstream can trim the rollback tail.
32
30
  * - `PayloadLocalPush`: Local push payload
33
31
  *
34
32
  * Invariants:
35
33
  * 1. **Chain Continuity**: Each event must reference its immediate parent.
36
34
  * 2. **Head Ordering**: Upstream Head ≤ Local Head.
37
- * 3. **ID Sequence**: Must follow the pattern (1,0)→(1,1)→(1,2)→(2,0).
35
+ * 3. **Event number sequence**: Must follow the pattern (1,0)→(1,1)→(1,2)→(2,0).
38
36
  *
39
- * The `updateSyncState` function processes updates to the sync state based on incoming payloads,
40
- * handling cases such as upstream rebase, advance, local push, and rollback tail trimming.
37
+ * A few further notes to help form an intuition:
38
+ * - The goal is to keep the pending events as small as possible (i.e. to have synced with the next upstream node)
39
+ * - There are 2 cases for rebasing:
40
+ * - The conflicting event only conflicts with the pending events -> only (some of) the pending events need to be rolled back
41
+ *
42
+ * The `merge` function processes updates to the sync state based on incoming payloads,
43
+ * handling cases such as upstream rebase, advance and local push.
41
44
  */
42
- export interface SyncState {
43
- pending: ReadonlyArray<MutationEvent.EncodedWithMeta>
44
- rollbackTail: ReadonlyArray<MutationEvent.EncodedWithMeta>
45
- upstreamHead: EventId.EventId
46
- localHead: EventId.EventId
45
+ export class SyncState extends Schema.Class<SyncState>('SyncState')({
46
+ pending: Schema.Array(LiveStoreEvent.EncodedWithMeta),
47
+ /** What this node expects the next upstream node to have as its own local head */
48
+ upstreamHead: EventSequenceNumber.EventSequenceNumber,
49
+ /** Equivalent to `pending.at(-1)?.id` if there are pending events */
50
+ localHead: EventSequenceNumber.EventSequenceNumber,
51
+ }) {
52
+ toJSON = (): any => ({
53
+ pending: this.pending.map((e) => e.toJSON()),
54
+ upstreamHead: EventSequenceNumber.toString(this.upstreamHead),
55
+ localHead: EventSequenceNumber.toString(this.localHead),
56
+ })
47
57
  }
48
58
 
49
- export const SyncState = Schema.Struct({
50
- pending: Schema.Array(MutationEvent.EncodedWithMeta),
51
- rollbackTail: Schema.Array(MutationEvent.EncodedWithMeta),
52
- upstreamHead: EventId.EventId,
53
- localHead: EventId.EventId,
54
- }).annotations({ title: 'SyncState' })
55
-
59
+ /**
60
+ * This payload propagates a rebase from the upstream node
61
+ */
56
62
  export class PayloadUpstreamRebase extends Schema.TaggedStruct('upstream-rebase', {
57
- /** Rollback until this event in the rollback tail (inclusive). Starting from the end of the rollback tail. */
58
- rollbackUntil: EventId.EventId,
59
- newEvents: Schema.Array(MutationEvent.EncodedWithMeta),
60
- /** Trim rollback tail up to this event (inclusive). */
61
- trimRollbackUntil: Schema.optional(EventId.EventId),
63
+ /** Events which need to be rolled back */
64
+ rollbackEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
65
+ /** Events which need to be applied after the rollback (already rebased by the upstream node) */
66
+ newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
62
67
  }) {}
63
68
 
64
69
  export class PayloadUpstreamAdvance extends Schema.TaggedStruct('upstream-advance', {
65
- newEvents: Schema.Array(MutationEvent.EncodedWithMeta),
66
- /** Trim rollback tail up to this event (inclusive). */
67
- trimRollbackUntil: Schema.optional(EventId.EventId),
70
+ newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
68
71
  }) {}
69
72
 
70
73
  export class PayloadLocalPush extends Schema.TaggedStruct('local-push', {
71
- newEvents: Schema.Array(MutationEvent.EncodedWithMeta),
74
+ newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
72
75
  }) {}
73
76
 
74
77
  export class Payload extends Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance, PayloadLocalPush) {}
75
78
 
76
- export const PayloadUpstream = Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance)
77
-
78
- export type PayloadUpstream = typeof PayloadUpstream.Type
79
+ export class PayloadUpstream extends Schema.Union(PayloadUpstreamRebase, PayloadUpstreamAdvance) {}
80
+
81
+ /** Only used for debugging purposes */
82
+ export class MergeContext extends Schema.Class<MergeContext>('MergeContext')({
83
+ payload: Payload,
84
+ syncState: SyncState,
85
+ }) {
86
+ toJSON = (): any => {
87
+ const payload = Match.value(this.payload).pipe(
88
+ Match.tag('local-push', () => ({
89
+ _tag: 'local-push',
90
+ newEvents: this.payload.newEvents.map((e) => e.toJSON()),
91
+ })),
92
+ Match.tag('upstream-advance', () => ({
93
+ _tag: 'upstream-advance',
94
+ newEvents: this.payload.newEvents.map((e) => e.toJSON()),
95
+ })),
96
+ Match.tag('upstream-rebase', (payload) => ({
97
+ _tag: 'upstream-rebase',
98
+ newEvents: payload.newEvents.map((e) => e.toJSON()),
99
+ rollbackEvents: payload.rollbackEvents.map((e) => e.toJSON()),
100
+ })),
101
+ Match.exhaustive,
102
+ )
103
+ return {
104
+ payload,
105
+ syncState: this.syncState.toJSON(),
106
+ }
107
+ }
108
+ }
79
109
 
80
- export type UpdateResultAdvance = {
81
- _tag: 'advance'
82
- newSyncState: SyncState
83
- previousSyncState: SyncState
84
- /** Events which weren't pending before the update */
85
- newEvents: ReadonlyArray<MutationEvent.EncodedWithMeta>
110
+ export class MergeResultAdvance extends Schema.Class<MergeResultAdvance>('MergeResultAdvance')({
111
+ _tag: Schema.Literal('advance'),
112
+ newSyncState: SyncState,
113
+ newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
114
+ /** Events which were previously pending but are now confirmed */
115
+ confirmedEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
116
+ mergeContext: MergeContext,
117
+ }) {
118
+ toJSON = (): any => {
119
+ return {
120
+ _tag: this._tag,
121
+ newSyncState: this.newSyncState.toJSON(),
122
+ newEvents: this.newEvents.map((e) => e.toJSON()),
123
+ confirmedEvents: this.confirmedEvents.map((e) => e.toJSON()),
124
+ mergeContext: this.mergeContext.toJSON(),
125
+ }
126
+ }
86
127
  }
87
128
 
88
- export type UpdateResultRebase = {
89
- _tag: 'rebase'
90
- newSyncState: SyncState
91
- previousSyncState: SyncState
92
- /** Events which weren't pending before the update */
93
- newEvents: ReadonlyArray<MutationEvent.EncodedWithMeta>
94
- eventsToRollback: ReadonlyArray<MutationEvent.EncodedWithMeta>
129
+ export class MergeResultRebase extends Schema.Class<MergeResultRebase>('MergeResultRebase')({
130
+ _tag: Schema.Literal('rebase'),
131
+ newSyncState: SyncState,
132
+ newEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
133
+ /** Events which need to be rolled back */
134
+ rollbackEvents: Schema.Array(LiveStoreEvent.EncodedWithMeta),
135
+ mergeContext: MergeContext,
136
+ }) {
137
+ toJSON = (): any => {
138
+ return {
139
+ _tag: this._tag,
140
+ newSyncState: this.newSyncState.toJSON(),
141
+ newEvents: this.newEvents.map((e) => e.toJSON()),
142
+ rollbackEvents: this.rollbackEvents.map((e) => e.toJSON()),
143
+ mergeContext: this.mergeContext.toJSON(),
144
+ }
145
+ }
95
146
  }
96
147
 
97
- export type UpdateResultReject = {
98
- _tag: 'reject'
99
- previousSyncState: SyncState
148
+ export class MergeResultReject extends Schema.Class<MergeResultReject>('MergeResultReject')({
149
+ _tag: Schema.Literal('reject'),
100
150
  /** The minimum id that the new events must have */
101
- expectedMinimumId: EventId.EventId
151
+ expectedMinimumId: EventSequenceNumber.EventSequenceNumber,
152
+ mergeContext: MergeContext,
153
+ }) {
154
+ toJSON = (): any => {
155
+ return {
156
+ _tag: this._tag,
157
+ expectedMinimumId: EventSequenceNumber.toString(this.expectedMinimumId),
158
+ mergeContext: this.mergeContext.toJSON(),
159
+ }
160
+ }
161
+ }
162
+
163
+ export class MergeResultUnexpectedError extends Schema.Class<MergeResultUnexpectedError>('MergeResultUnexpectedError')({
164
+ _tag: Schema.Literal('unexpected-error'),
165
+ cause: UnexpectedError,
166
+ }) {}
167
+
168
+ export class MergeResult extends Schema.Union(
169
+ MergeResultAdvance,
170
+ MergeResultRebase,
171
+ MergeResultReject,
172
+ MergeResultUnexpectedError,
173
+ ) {}
174
+
175
+ const unexpectedError = (cause: unknown): MergeResultUnexpectedError => {
176
+ if (LS_DEV) {
177
+ debugger
178
+ }
179
+
180
+ return MergeResultUnexpectedError.make({
181
+ _tag: 'unexpected-error',
182
+ cause: new UnexpectedError({ cause }),
183
+ })
102
184
  }
103
185
 
104
- export type UpdateResult = UpdateResultAdvance | UpdateResultRebase | UpdateResultReject
186
+ // TODO Idea: call merge recursively through hierarchy levels
187
+ /*
188
+ Idea: have a map that maps from `globalEventSequenceNumber` to Array<ClientEvents>
189
+ The same applies to even further hierarchy levels
105
190
 
106
- export const updateSyncState = ({
191
+ TODO: possibly even keep the client events in a separate table in the client leader
192
+ */
193
+ export const merge = ({
107
194
  syncState,
108
195
  payload,
109
- isLocalEvent,
196
+ isClientEvent,
110
197
  isEqualEvent,
111
- ignoreLocalEvents = false,
198
+ ignoreClientEvents = false,
112
199
  }: {
113
200
  syncState: SyncState
114
201
  payload: typeof Payload.Type
115
- isLocalEvent: (event: MutationEvent.EncodedWithMeta) => boolean
116
- isEqualEvent: (a: MutationEvent.EncodedWithMeta, b: MutationEvent.EncodedWithMeta) => boolean
117
- /** This is used in the leader which should ignore local events when receiving an upstream-advance payload */
118
- ignoreLocalEvents?: boolean
119
- }): UpdateResult => {
120
- const trimRollbackTail = (
121
- rollbackTail: ReadonlyArray<MutationEvent.EncodedWithMeta>,
122
- ): ReadonlyArray<MutationEvent.EncodedWithMeta> => {
123
- const trimRollbackUntil = payload._tag === 'local-push' ? undefined : payload.trimRollbackUntil
124
- if (trimRollbackUntil === undefined) return rollbackTail
125
- const index = rollbackTail.findIndex((event) => EventId.isEqual(event.id, trimRollbackUntil))
126
- if (index === -1) return []
127
- return rollbackTail.slice(index + 1)
128
- }
202
+ isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
203
+ isEqualEvent: (a: LiveStoreEvent.EncodedWithMeta, b: LiveStoreEvent.EncodedWithMeta) => boolean
204
+ /** This is used in the leader which should ignore client events when receiving an upstream-advance payload */
205
+ ignoreClientEvents?: boolean
206
+ }): typeof MergeResult.Type => {
207
+ validateSyncState(syncState)
208
+ validatePayload(payload)
209
+
210
+ const mergeContext = MergeContext.make({ payload, syncState })
129
211
 
130
212
  switch (payload._tag) {
131
213
  case 'upstream-rebase': {
132
- // Find the index of the rollback event in the rollback tail
133
- const rollbackIndex = syncState.rollbackTail.findIndex((event) =>
134
- EventId.isEqual(event.id, payload.rollbackUntil),
135
- )
136
- if (rollbackIndex === -1) {
137
- return shouldNeverHappen(
138
- `Rollback event not found in rollback tail. Rollback until: [${payload.rollbackUntil.global},${payload.rollbackUntil.local}]. Rollback tail: [${syncState.rollbackTail.map((e) => e.toString()).join(', ')}]`,
139
- )
140
- }
141
-
142
- const eventsToRollback = [...syncState.rollbackTail.slice(rollbackIndex), ...syncState.pending]
214
+ const rollbackEvents = [...payload.rollbackEvents, ...syncState.pending]
143
215
 
144
216
  // Get the last new event's ID as the new upstream head
145
- const newUpstreamHead = payload.newEvents.at(-1)?.id ?? syncState.upstreamHead
217
+ const newUpstreamHead = payload.newEvents.at(-1)?.seqNum ?? syncState.upstreamHead
146
218
 
147
219
  // Rebase pending events on top of the new events
148
220
  const rebasedPending = rebaseEvents({
149
221
  events: syncState.pending,
150
- baseEventId: newUpstreamHead,
151
- isLocalEvent,
222
+ baseEventSequenceNumber: newUpstreamHead,
223
+ isClientEvent,
152
224
  })
153
225
 
154
- return {
155
- _tag: 'rebase',
156
- newSyncState: {
157
- pending: rebasedPending,
158
- rollbackTail: trimRollbackTail([...syncState.rollbackTail.slice(0, rollbackIndex), ...payload.newEvents]),
159
- upstreamHead: newUpstreamHead,
160
- localHead: rebasedPending.at(-1)?.id ?? newUpstreamHead,
161
- },
162
- previousSyncState: syncState,
163
- newEvents: payload.newEvents,
164
- eventsToRollback,
165
- }
226
+ return validateMergeResult(
227
+ MergeResultRebase.make({
228
+ _tag: 'rebase',
229
+ newSyncState: new SyncState({
230
+ pending: rebasedPending,
231
+ upstreamHead: newUpstreamHead,
232
+ localHead: rebasedPending.at(-1)?.seqNum ?? newUpstreamHead,
233
+ }),
234
+ newEvents: [...payload.newEvents, ...rebasedPending],
235
+ rollbackEvents,
236
+ mergeContext,
237
+ }),
238
+ )
166
239
  }
167
240
 
241
+ // #region upstream-advance
168
242
  case 'upstream-advance': {
169
243
  if (payload.newEvents.length === 0) {
170
- return {
171
- _tag: 'advance',
172
- newSyncState: {
173
- pending: syncState.pending,
174
- rollbackTail: trimRollbackTail(syncState.rollbackTail),
175
- upstreamHead: syncState.upstreamHead,
176
- localHead: syncState.localHead,
177
- },
178
- previousSyncState: syncState,
179
- newEvents: [],
180
- }
244
+ return validateMergeResult(
245
+ MergeResultAdvance.make({
246
+ _tag: 'advance',
247
+ newSyncState: new SyncState({
248
+ pending: syncState.pending,
249
+ upstreamHead: syncState.upstreamHead,
250
+ localHead: syncState.localHead,
251
+ }),
252
+ newEvents: [],
253
+ confirmedEvents: [],
254
+ mergeContext: mergeContext,
255
+ }),
256
+ )
181
257
  }
182
258
 
183
- // Validate that newEvents are sorted in ascending order by eventId
259
+ // Validate that newEvents are sorted in ascending order by eventNum
184
260
  for (let i = 1; i < payload.newEvents.length; i++) {
185
- if (EventId.isGreaterThan(payload.newEvents[i - 1]!.id, payload.newEvents[i]!.id)) {
186
- return shouldNeverHappen('Events must be sorted in ascending order by eventId')
261
+ if (EventSequenceNumber.isGreaterThan(payload.newEvents[i - 1]!.seqNum, payload.newEvents[i]!.seqNum)) {
262
+ return unexpectedError(
263
+ `Events must be sorted in ascending order by event number. Received: [${payload.newEvents.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
264
+ )
187
265
  }
188
266
  }
189
267
 
190
- const newUpstreamHead = payload.newEvents.at(-1)!.id
268
+ // Validate that incoming events are larger than upstream head
269
+ if (
270
+ EventSequenceNumber.isGreaterThan(syncState.upstreamHead, payload.newEvents[0]!.seqNum) ||
271
+ EventSequenceNumber.isEqual(syncState.upstreamHead, payload.newEvents[0]!.seqNum)
272
+ ) {
273
+ return unexpectedError(
274
+ `Incoming events must be greater than upstream head. Expected greater than: ${EventSequenceNumber.toString(syncState.upstreamHead)}. Received: [${payload.newEvents.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
275
+ )
276
+ }
277
+
278
+ const newUpstreamHead = payload.newEvents.at(-1)!.seqNum
191
279
 
192
280
  const divergentPendingIndex = findDivergencePoint({
193
281
  existingEvents: syncState.pending,
194
282
  incomingEvents: payload.newEvents,
195
283
  isEqualEvent,
196
- isLocalEvent,
197
- ignoreLocalEvents,
284
+ isClientEvent,
285
+ ignoreClientEvents,
198
286
  })
199
287
 
288
+ // No divergent pending events, thus we can just advance (some of) the pending events
200
289
  if (divergentPendingIndex === -1) {
201
- const pendingEventIds = new Set(syncState.pending.map((e) => `${e.id.global},${e.id.local}`))
202
- const newEvents = payload.newEvents.filter((e) => !pendingEventIds.has(`${e.id.global},${e.id.local}`))
290
+ const pendingEventSequenceNumbers = new Set(
291
+ syncState.pending.map((e) => `${e.seqNum.global},${e.seqNum.client}`),
292
+ )
293
+ const newEvents = payload.newEvents.filter(
294
+ (e) => !pendingEventSequenceNumbers.has(`${e.seqNum.global},${e.seqNum.client}`),
295
+ )
203
296
 
204
297
  // In the case where the incoming events are a subset of the pending events,
205
298
  // we need to split the pending events into two groups:
206
299
  // - pendingMatching: The pending events up to point where they match the incoming events
207
300
  // - pendingRemaining: The pending events after the point where they match the incoming events
208
- // The `localIndexOffset` is used to account for the local events that are being ignored
209
- let localIndexOffset = 0
301
+ // The `clientIndexOffset` is used to account for the client events that are being ignored
302
+ let clientIndexOffset = 0
210
303
  const [pendingMatching, pendingRemaining] = ReadonlyArray.splitWhere(
211
304
  syncState.pending,
212
305
  (pendingEvent, index) => {
213
- if (ignoreLocalEvents && isLocalEvent(pendingEvent)) {
214
- localIndexOffset++
306
+ if (ignoreClientEvents && isClientEvent(pendingEvent)) {
307
+ clientIndexOffset++
215
308
  return false
216
309
  }
217
310
 
218
- const newEvent = payload.newEvents.at(index - localIndexOffset)
311
+ const newEvent = payload.newEvents.at(index - clientIndexOffset)
219
312
  if (!newEvent) {
220
313
  return true
221
314
  }
@@ -223,105 +316,100 @@ export const updateSyncState = ({
223
316
  },
224
317
  )
225
318
 
226
- const seenEventIds = new Set<string>()
227
- const pendingAndNewEvents = [...pendingMatching, ...payload.newEvents].filter((event) => {
228
- const eventIdStr = `${event.id.global},${event.id.local}`
229
- if (seenEventIds.has(eventIdStr)) {
230
- return false
231
- }
232
- seenEventIds.add(eventIdStr)
233
- return true
234
- })
235
-
236
- return {
237
- _tag: 'advance',
238
- newSyncState: {
239
- pending: pendingRemaining,
240
- rollbackTail: trimRollbackTail([...syncState.rollbackTail, ...pendingAndNewEvents]),
241
- upstreamHead: newUpstreamHead,
242
- localHead: pendingRemaining.at(-1)?.id ?? newUpstreamHead,
243
- },
244
- previousSyncState: syncState,
245
- newEvents,
246
- }
319
+ return validateMergeResult(
320
+ MergeResultAdvance.make({
321
+ _tag: 'advance',
322
+ newSyncState: new SyncState({
323
+ pending: pendingRemaining,
324
+ upstreamHead: newUpstreamHead,
325
+ localHead:
326
+ pendingRemaining.at(-1)?.seqNum ?? EventSequenceNumber.max(syncState.localHead, newUpstreamHead),
327
+ }),
328
+ newEvents,
329
+ confirmedEvents: pendingMatching,
330
+ mergeContext: mergeContext,
331
+ }),
332
+ )
247
333
  } else {
248
334
  const divergentPending = syncState.pending.slice(divergentPendingIndex)
249
335
  const rebasedPending = rebaseEvents({
250
336
  events: divergentPending,
251
- baseEventId: newUpstreamHead,
252
- isLocalEvent,
337
+ baseEventSequenceNumber: newUpstreamHead,
338
+ isClientEvent,
253
339
  })
254
340
 
255
341
  const divergentNewEventsIndex = findDivergencePoint({
256
342
  existingEvents: payload.newEvents,
257
343
  incomingEvents: syncState.pending,
258
344
  isEqualEvent,
259
- isLocalEvent,
260
- ignoreLocalEvents,
345
+ isClientEvent,
346
+ ignoreClientEvents,
261
347
  })
262
348
 
263
- return {
264
- _tag: 'rebase',
265
- newSyncState: {
266
- pending: rebasedPending,
267
- rollbackTail: trimRollbackTail([...syncState.rollbackTail, ...payload.newEvents]),
268
- upstreamHead: newUpstreamHead,
269
- localHead: rebasedPending.at(-1)!.id,
270
- },
271
- previousSyncState: syncState,
272
- newEvents: [...payload.newEvents.slice(divergentNewEventsIndex), ...rebasedPending],
273
- eventsToRollback: [...syncState.rollbackTail, ...divergentPending],
274
- }
349
+ return validateMergeResult(
350
+ MergeResultRebase.make({
351
+ _tag: 'rebase',
352
+ newSyncState: new SyncState({
353
+ pending: rebasedPending,
354
+ upstreamHead: newUpstreamHead,
355
+ localHead: rebasedPending.at(-1)!.seqNum,
356
+ }),
357
+ newEvents: [...payload.newEvents.slice(divergentNewEventsIndex), ...rebasedPending],
358
+ rollbackEvents: divergentPending,
359
+ mergeContext,
360
+ }),
361
+ )
275
362
  }
276
363
  }
364
+ // #endregion
277
365
 
366
+ // This is the same as what's running in the sync backend
278
367
  case 'local-push': {
279
368
  if (payload.newEvents.length === 0) {
280
- return { _tag: 'advance', newSyncState: syncState, previousSyncState: syncState, newEvents: [] }
369
+ return validateMergeResult(
370
+ MergeResultAdvance.make({
371
+ _tag: 'advance',
372
+ newSyncState: syncState,
373
+ newEvents: [],
374
+ confirmedEvents: [],
375
+ mergeContext: mergeContext,
376
+ }),
377
+ )
281
378
  }
282
379
 
283
380
  const newEventsFirst = payload.newEvents.at(0)!
284
- const invalidEventId = EventId.isGreaterThan(newEventsFirst.id, syncState.localHead) === false
285
-
286
- if (invalidEventId) {
287
- const expectedMinimumId = EventId.nextPair(syncState.localHead, true).id
288
- return { _tag: 'reject', previousSyncState: syncState, expectedMinimumId }
381
+ const invalidEventSequenceNumber =
382
+ EventSequenceNumber.isGreaterThan(newEventsFirst.seqNum, syncState.localHead) === false
383
+
384
+ if (invalidEventSequenceNumber) {
385
+ const expectedMinimumId = EventSequenceNumber.nextPair(syncState.localHead, true).seqNum
386
+ return validateMergeResult(
387
+ MergeResultReject.make({
388
+ _tag: 'reject',
389
+ expectedMinimumId,
390
+ mergeContext,
391
+ }),
392
+ )
289
393
  } else {
290
- return {
291
- _tag: 'advance',
292
- newSyncState: {
293
- pending: [...syncState.pending, ...payload.newEvents],
294
- rollbackTail: syncState.rollbackTail,
295
- upstreamHead: syncState.upstreamHead,
296
- localHead: payload.newEvents.at(-1)!.id,
297
- },
298
- previousSyncState: syncState,
299
- newEvents: payload.newEvents,
300
- }
394
+ return validateMergeResult(
395
+ MergeResultAdvance.make({
396
+ _tag: 'advance',
397
+ newSyncState: new SyncState({
398
+ pending: [...syncState.pending, ...payload.newEvents],
399
+ upstreamHead: syncState.upstreamHead,
400
+ localHead: payload.newEvents.at(-1)!.seqNum,
401
+ }),
402
+ newEvents: payload.newEvents,
403
+ confirmedEvents: [],
404
+ mergeContext: mergeContext,
405
+ }),
406
+ )
301
407
  }
302
408
  }
303
409
 
304
- // case 'upstream-trim-rollback-tail': {
305
- // // Find the index of the new rollback start in the rollback tail
306
- // const startIndex = syncState.rollbackTail.findIndex((event) => eventIdsEqual(event.id, payload.trimRollbackUntil))
307
- // if (startIndex === -1) {
308
- // return shouldNeverHappen('New rollback start event not found in rollback tail')
309
- // }
310
-
311
- // // Keep only the events from the start index onwards
312
- // const newRollbackTail = syncState.rollbackTail.slice(startIndex)
313
-
314
- // return {
315
- // _tag: 'advance',
316
- // syncState: {
317
- // pending: syncState.pending,
318
- // rollbackTail: newRollbackTail,
319
- // upstreamHead: syncState.upstreamHead,
320
- // localHead: syncState.localHead,
321
- // },
322
- // newEvents: [],
323
- // }
324
- // }
410
+ default: {
411
+ casesHandled(payload)
412
+ }
325
413
  }
326
414
  }
327
415
 
@@ -329,34 +417,36 @@ export const updateSyncState = ({
329
417
  * Gets the index relative to `existingEvents` where the divergence point is
330
418
  * by comparing each event in `existingEvents` to the corresponding event in `incomingEvents`
331
419
  */
332
- const findDivergencePoint = ({
420
+ export const findDivergencePoint = ({
333
421
  existingEvents,
334
422
  incomingEvents,
335
423
  isEqualEvent,
336
- isLocalEvent,
337
- ignoreLocalEvents,
424
+ isClientEvent,
425
+ ignoreClientEvents,
338
426
  }: {
339
- existingEvents: ReadonlyArray<MutationEvent.EncodedWithMeta>
340
- incomingEvents: ReadonlyArray<MutationEvent.EncodedWithMeta>
341
- isEqualEvent: (a: MutationEvent.EncodedWithMeta, b: MutationEvent.EncodedWithMeta) => boolean
342
- isLocalEvent: (event: MutationEvent.EncodedWithMeta) => boolean
343
- ignoreLocalEvents: boolean
427
+ existingEvents: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
428
+ incomingEvents: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
429
+ isEqualEvent: (a: LiveStoreEvent.EncodedWithMeta, b: LiveStoreEvent.EncodedWithMeta) => boolean
430
+ isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
431
+ ignoreClientEvents: boolean
344
432
  }): number => {
345
- if (ignoreLocalEvents) {
346
- const filteredExistingEvents = existingEvents.filter((event) => !isLocalEvent(event))
347
- const divergencePointWithoutLocalEvents = findDivergencePoint({
433
+ if (ignoreClientEvents) {
434
+ const filteredExistingEvents = existingEvents.filter((event) => !isClientEvent(event))
435
+ const divergencePointWithoutClientEvents = findDivergencePoint({
348
436
  existingEvents: filteredExistingEvents,
349
437
  incomingEvents,
350
438
  isEqualEvent,
351
- isLocalEvent,
352
- ignoreLocalEvents: false,
439
+ isClientEvent,
440
+ ignoreClientEvents: false,
353
441
  })
354
442
 
355
- if (divergencePointWithoutLocalEvents === -1) return -1
443
+ if (divergencePointWithoutClientEvents === -1) return -1
356
444
 
357
- const divergencePointEventId = existingEvents[divergencePointWithoutLocalEvents]!.id
445
+ const divergencePointEventSequenceNumber = existingEvents[divergencePointWithoutClientEvents]!.seqNum
358
446
  // Now find the divergence point in the original array
359
- return existingEvents.findIndex((event) => EventId.isEqual(event.id, divergencePointEventId))
447
+ return existingEvents.findIndex((event) =>
448
+ EventSequenceNumber.isEqual(event.seqNum, divergencePointEventSequenceNumber),
449
+ )
360
450
  }
361
451
 
362
452
  return existingEvents.findIndex((existingEvent, index) => {
@@ -368,18 +458,120 @@ const findDivergencePoint = ({
368
458
 
369
459
  const rebaseEvents = ({
370
460
  events,
371
- baseEventId,
372
- isLocalEvent,
461
+ baseEventSequenceNumber,
462
+ isClientEvent,
373
463
  }: {
374
- events: ReadonlyArray<MutationEvent.EncodedWithMeta>
375
- baseEventId: EventId.EventId
376
- isLocalEvent: (event: MutationEvent.EncodedWithMeta) => boolean
377
- }): ReadonlyArray<MutationEvent.EncodedWithMeta> => {
378
- let prevEventId = baseEventId
464
+ events: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
465
+ baseEventSequenceNumber: EventSequenceNumber.EventSequenceNumber
466
+ isClientEvent: (event: LiveStoreEvent.EncodedWithMeta) => boolean
467
+ }): ReadonlyArray<LiveStoreEvent.EncodedWithMeta> => {
468
+ let prevEventSequenceNumber = baseEventSequenceNumber
379
469
  return events.map((event) => {
380
- const isLocal = isLocalEvent(event)
381
- const newEvent = event.rebase(prevEventId, isLocal)
382
- prevEventId = newEvent.id
470
+ const isLocal = isClientEvent(event)
471
+ const newEvent = event.rebase(prevEventSequenceNumber, isLocal)
472
+ prevEventSequenceNumber = newEvent.seqNum
383
473
  return newEvent
384
474
  })
385
475
  }
476
+
477
+ /**
478
+ * TODO: Implement this
479
+ *
480
+ * In certain scenarios e.g. when the client session has a queue of upstream update results,
481
+ * it could make sense to "flatten" update results into a single update result which the client session
482
+ * can process more efficiently which avoids push-threshing
483
+ */
484
+ const _flattenMergeResults = (_updateResults: ReadonlyArray<MergeResult>) => {}
485
+
486
+ const validatePayload = (payload: typeof Payload.Type) => {
487
+ for (let i = 1; i < payload.newEvents.length; i++) {
488
+ if (EventSequenceNumber.isGreaterThanOrEqual(payload.newEvents[i - 1]!.seqNum, payload.newEvents[i]!.seqNum)) {
489
+ return unexpectedError(
490
+ `Events must be ordered in monotonically ascending order by eventNum. Received: [${payload.newEvents.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
491
+ )
492
+ }
493
+ }
494
+ }
495
+
496
+ const validateSyncState = (syncState: SyncState) => {
497
+ for (let i = 0; i < syncState.pending.length; i++) {
498
+ const event = syncState.pending[i]!
499
+ const nextEvent = syncState.pending[i + 1]
500
+ if (nextEvent === undefined) break // Reached end of chain
501
+
502
+ if (EventSequenceNumber.isGreaterThanOrEqual(event.seqNum, nextEvent.seqNum)) {
503
+ shouldNeverHappen(
504
+ `Events must be ordered in monotonically ascending order by eventNum. Received: [${syncState.pending.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`,
505
+ {
506
+ event,
507
+ nextEvent,
508
+ },
509
+ )
510
+ }
511
+
512
+ // If the global id has increased, then the client id must be 0
513
+ const globalIdHasIncreased = nextEvent.seqNum.global > event.seqNum.global
514
+ if (globalIdHasIncreased) {
515
+ if (nextEvent.seqNum.client !== 0) {
516
+ shouldNeverHappen(
517
+ `New global events must point to clientId 0 in the parentSeqNum. Received: (${EventSequenceNumber.toString(nextEvent.seqNum)})`,
518
+ syncState.pending,
519
+ {
520
+ event,
521
+ nextEvent,
522
+ },
523
+ )
524
+ }
525
+ } else {
526
+ // Otherwise, the parentSeqNum must be the same as the previous event's id
527
+ if (EventSequenceNumber.isEqual(nextEvent.parentSeqNum, event.seqNum) === false) {
528
+ shouldNeverHappen('Events must be linked in a continuous chain via the parentSeqNum', syncState.pending, {
529
+ event,
530
+ nextEvent,
531
+ })
532
+ }
533
+ }
534
+ }
535
+ }
536
+
537
+ const validateMergeResult = (mergeResult: typeof MergeResult.Type) => {
538
+ if (mergeResult._tag === 'unexpected-error' || mergeResult._tag === 'reject') return mergeResult
539
+
540
+ validateSyncState(mergeResult.newSyncState)
541
+
542
+ // Ensure local head is always greater than or equal to upstream head
543
+ if (EventSequenceNumber.isGreaterThan(mergeResult.newSyncState.upstreamHead, mergeResult.newSyncState.localHead)) {
544
+ shouldNeverHappen('Local head must be greater than or equal to upstream head', {
545
+ localHead: mergeResult.newSyncState.localHead,
546
+ upstreamHead: mergeResult.newSyncState.upstreamHead,
547
+ })
548
+ }
549
+
550
+ // Ensure new local head is greater than or equal to the previous local head
551
+ if (
552
+ EventSequenceNumber.isGreaterThanOrEqual(
553
+ mergeResult.newSyncState.localHead,
554
+ mergeResult.mergeContext.syncState.localHead,
555
+ ) === false
556
+ ) {
557
+ shouldNeverHappen('New local head must be greater than or equal to the previous local head', {
558
+ localHead: mergeResult.newSyncState.localHead,
559
+ previousLocalHead: mergeResult.mergeContext.syncState.localHead,
560
+ })
561
+ }
562
+
563
+ // Ensure new upstream head is greater than or equal to the previous upstream head
564
+ if (
565
+ EventSequenceNumber.isGreaterThanOrEqual(
566
+ mergeResult.newSyncState.upstreamHead,
567
+ mergeResult.mergeContext.syncState.upstreamHead,
568
+ ) === false
569
+ ) {
570
+ shouldNeverHappen('New upstream head must be greater than or equal to the previous upstream head', {
571
+ upstreamHead: mergeResult.newSyncState.upstreamHead,
572
+ previousUpstreamHead: mergeResult.mergeContext.syncState.upstreamHead,
573
+ })
574
+ }
575
+
576
+ return mergeResult
577
+ }