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