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

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 (344) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +16 -9
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  4. package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
  5. package/dist/WorkerTransportError.d.ts +11 -0
  6. package/dist/WorkerTransportError.d.ts.map +1 -0
  7. package/dist/WorkerTransportError.js +11 -0
  8. package/dist/WorkerTransportError.js.map +1 -0
  9. package/dist/adapter-types.d.ts +26 -3
  10. package/dist/adapter-types.d.ts.map +1 -1
  11. package/dist/adapter-types.js +27 -1
  12. package/dist/adapter-types.js.map +1 -1
  13. package/dist/bounded-collections.d.ts.map +1 -1
  14. package/dist/bounded-collections.js +6 -4
  15. package/dist/bounded-collections.js.map +1 -1
  16. package/dist/debug-info.js +4 -4
  17. package/dist/debug-info.js.map +1 -1
  18. package/dist/devtools/devtools-messages-client-session.d.ts +42 -22
  19. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  20. package/dist/devtools/devtools-messages-client-session.js +12 -1
  21. package/dist/devtools/devtools-messages-client-session.js.map +1 -1
  22. package/dist/devtools/devtools-messages-common.d.ts +12 -6
  23. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  24. package/dist/devtools/devtools-messages-common.js +8 -3
  25. package/dist/devtools/devtools-messages-common.js.map +1 -1
  26. package/dist/devtools/devtools-messages-leader.d.ts +45 -25
  27. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  28. package/dist/devtools/devtools-messages-leader.js +12 -1
  29. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  30. package/dist/devtools/mod.js +1 -1
  31. package/dist/devtools/mod.js.map +1 -1
  32. package/dist/errors.d.ts +15 -15
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js +11 -11
  35. package/dist/errors.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/leader-thread/LeaderSyncProcessor.d.ts +20 -6
  41. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  42. package/dist/leader-thread/LeaderSyncProcessor.js +283 -253
  43. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  44. package/dist/leader-thread/RejectedPushError.d.ts +107 -0
  45. package/dist/leader-thread/RejectedPushError.d.ts.map +1 -0
  46. package/dist/leader-thread/RejectedPushError.js +78 -0
  47. package/dist/leader-thread/RejectedPushError.js.map +1 -0
  48. package/dist/leader-thread/connection.js +1 -1
  49. package/dist/leader-thread/connection.js.map +1 -1
  50. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  51. package/dist/leader-thread/eventlog.js +12 -11
  52. package/dist/leader-thread/eventlog.js.map +1 -1
  53. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -2
  54. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  55. package/dist/leader-thread/leader-worker-devtools.js +34 -14
  56. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  57. package/dist/leader-thread/make-leader-thread-layer.d.ts +12 -5
  58. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  59. package/dist/leader-thread/make-leader-thread-layer.js +12 -11
  60. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  61. package/dist/leader-thread/make-leader-thread-layer.test.js +1 -1
  62. package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -1
  63. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  64. package/dist/leader-thread/materialize-event.js +7 -4
  65. package/dist/leader-thread/materialize-event.js.map +1 -1
  66. package/dist/leader-thread/recreate-db.js +1 -1
  67. package/dist/leader-thread/recreate-db.js.map +1 -1
  68. package/dist/leader-thread/shutdown-channel.d.ts +2 -2
  69. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  70. package/dist/leader-thread/shutdown-channel.js +2 -2
  71. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  72. package/dist/leader-thread/stream-events.d.ts.map +1 -1
  73. package/dist/leader-thread/stream-events.js +4 -3
  74. package/dist/leader-thread/stream-events.js.map +1 -1
  75. package/dist/leader-thread/types.d.ts +7 -6
  76. package/dist/leader-thread/types.d.ts.map +1 -1
  77. package/dist/leader-thread/types.js.map +1 -1
  78. package/dist/logging.js +4 -4
  79. package/dist/logging.js.map +1 -1
  80. package/dist/make-client-session.js +2 -2
  81. package/dist/make-client-session.js.map +1 -1
  82. package/dist/materializer-helper.js +6 -6
  83. package/dist/materializer-helper.js.map +1 -1
  84. package/dist/otel.d.ts +1 -1
  85. package/dist/otel.d.ts.map +1 -1
  86. package/dist/otel.js +2 -2
  87. package/dist/otel.js.map +1 -1
  88. package/dist/rematerialize-from-eventlog.d.ts +1 -1
  89. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  90. package/dist/rematerialize-from-eventlog.js +11 -9
  91. package/dist/rematerialize-from-eventlog.js.map +1 -1
  92. package/dist/schema/EventDef/define.d.ts +16 -2
  93. package/dist/schema/EventDef/define.d.ts.map +1 -1
  94. package/dist/schema/EventDef/define.js +5 -4
  95. package/dist/schema/EventDef/define.js.map +1 -1
  96. package/dist/schema/EventDef/deprecated.d.ts +99 -0
  97. package/dist/schema/EventDef/deprecated.d.ts.map +1 -0
  98. package/dist/schema/EventDef/deprecated.js +144 -0
  99. package/dist/schema/EventDef/deprecated.js.map +1 -0
  100. package/dist/schema/EventDef/deprecated.test.d.ts +2 -0
  101. package/dist/schema/EventDef/deprecated.test.d.ts.map +1 -0
  102. package/dist/schema/EventDef/deprecated.test.js +95 -0
  103. package/dist/schema/EventDef/deprecated.test.js.map +1 -0
  104. package/dist/schema/EventDef/event-def.d.ts +4 -0
  105. package/dist/schema/EventDef/event-def.d.ts.map +1 -1
  106. package/dist/schema/EventDef/mod.d.ts +1 -0
  107. package/dist/schema/EventDef/mod.d.ts.map +1 -1
  108. package/dist/schema/EventDef/mod.js +1 -0
  109. package/dist/schema/EventDef/mod.js.map +1 -1
  110. package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -1
  111. package/dist/schema/EventSequenceNumber/client.js +11 -11
  112. package/dist/schema/EventSequenceNumber/client.js.map +1 -1
  113. package/dist/schema/EventSequenceNumber.test.js +1 -1
  114. package/dist/schema/EventSequenceNumber.test.js.map +1 -1
  115. package/dist/schema/LiveStoreEvent/client.d.ts +6 -6
  116. package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -1
  117. package/dist/schema/LiveStoreEvent/client.js +6 -3
  118. package/dist/schema/LiveStoreEvent/client.js.map +1 -1
  119. package/dist/schema/LiveStoreEvent/client.test.d.ts +2 -0
  120. package/dist/schema/LiveStoreEvent/client.test.d.ts.map +1 -0
  121. package/dist/schema/LiveStoreEvent/client.test.js +83 -0
  122. package/dist/schema/LiveStoreEvent/client.test.js.map +1 -0
  123. package/dist/schema/schema.d.ts.map +1 -1
  124. package/dist/schema/schema.js +7 -4
  125. package/dist/schema/schema.js.map +1 -1
  126. package/dist/schema/state/sqlite/client-document-def.d.ts +1 -0
  127. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  128. package/dist/schema/state/sqlite/client-document-def.js +34 -13
  129. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  130. package/dist/schema/state/sqlite/client-document-def.test.js +121 -2
  131. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  132. package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
  133. package/dist/schema/state/sqlite/column-annotations.js +1 -1
  134. package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
  135. package/dist/schema/state/sqlite/column-annotations.test.js +1 -1
  136. package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -1
  137. package/dist/schema/state/sqlite/column-def.d.ts.map +1 -1
  138. package/dist/schema/state/sqlite/column-def.js +36 -34
  139. package/dist/schema/state/sqlite/column-def.js.map +1 -1
  140. package/dist/schema/state/sqlite/column-def.test.js +7 -6
  141. package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
  142. package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
  143. package/dist/schema/state/sqlite/column-spec.js +8 -8
  144. package/dist/schema/state/sqlite/column-spec.js.map +1 -1
  145. package/dist/schema/state/sqlite/column-spec.test.js +1 -1
  146. package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
  147. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +2 -2
  148. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
  149. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +2 -2
  150. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
  151. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +11 -2
  152. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
  153. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +1 -1
  154. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -1
  155. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +1 -1
  156. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
  157. package/dist/schema/state/sqlite/db-schema/dsl/mod.js +1 -1
  158. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
  159. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  160. package/dist/schema/state/sqlite/mod.js +3 -5
  161. package/dist/schema/state/sqlite/mod.js.map +1 -1
  162. package/dist/schema/state/sqlite/query-builder/api.d.ts +37 -13
  163. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  164. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
  165. package/dist/schema/state/sqlite/query-builder/astToSql.js +77 -7
  166. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
  167. package/dist/schema/state/sqlite/query-builder/impl.d.ts +1 -1
  168. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  169. package/dist/schema/state/sqlite/query-builder/impl.js +28 -14
  170. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  171. package/dist/schema/state/sqlite/query-builder/impl.test.js +112 -3
  172. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  173. package/dist/schema/state/sqlite/schema-helpers.js +2 -2
  174. package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
  175. package/dist/schema/state/sqlite/table-def.d.ts +5 -3
  176. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  177. package/dist/schema/state/sqlite/table-def.js +1 -1
  178. package/dist/schema/state/sqlite/table-def.js.map +1 -1
  179. package/dist/schema/state/sqlite/table-def.test.js +57 -4
  180. package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
  181. package/dist/schema/unknown-events.d.ts +1 -1
  182. package/dist/schema/unknown-events.d.ts.map +1 -1
  183. package/dist/schema/unknown-events.js +1 -1
  184. package/dist/schema/unknown-events.js.map +1 -1
  185. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +1 -1
  186. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -1
  187. package/dist/schema-management/common.js +2 -2
  188. package/dist/schema-management/common.js.map +1 -1
  189. package/dist/schema-management/migrations.js +1 -1
  190. package/dist/schema-management/migrations.js.map +1 -1
  191. package/dist/sql-queries/sql-queries.js +8 -6
  192. package/dist/sql-queries/sql-queries.js.map +1 -1
  193. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  194. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  195. package/dist/sqlite-db-helper.js +3 -3
  196. package/dist/sqlite-db-helper.js.map +1 -1
  197. package/dist/sqlite-types.d.ts +2 -2
  198. package/dist/sqlite-types.d.ts.map +1 -1
  199. package/dist/sqlite-types.js.map +1 -1
  200. package/dist/sync/ClientSessionSyncProcessor.d.ts +8 -9
  201. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  202. package/dist/sync/ClientSessionSyncProcessor.js +93 -107
  203. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  204. package/dist/sync/errors.d.ts +0 -38
  205. package/dist/sync/errors.d.ts.map +1 -1
  206. package/dist/sync/errors.js +3 -20
  207. package/dist/sync/errors.js.map +1 -1
  208. package/dist/sync/mock-sync-backend.d.ts +5 -3
  209. package/dist/sync/mock-sync-backend.d.ts.map +1 -1
  210. package/dist/sync/mock-sync-backend.js +70 -68
  211. package/dist/sync/mock-sync-backend.js.map +1 -1
  212. package/dist/sync/next/compact-events.js +6 -6
  213. package/dist/sync/next/compact-events.js.map +1 -1
  214. package/dist/sync/next/facts.d.ts.map +1 -1
  215. package/dist/sync/next/facts.js +6 -6
  216. package/dist/sync/next/facts.js.map +1 -1
  217. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  218. package/dist/sync/next/history-dag-common.js +6 -6
  219. package/dist/sync/next/history-dag-common.js.map +1 -1
  220. package/dist/sync/next/history-dag.js +3 -3
  221. package/dist/sync/next/history-dag.js.map +1 -1
  222. package/dist/sync/next/rebase-events.js +1 -1
  223. package/dist/sync/next/rebase-events.js.map +1 -1
  224. package/dist/sync/next/test/compact-events.calculator.test.js +2 -2
  225. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  226. package/dist/sync/next/test/compact-events.test.d.ts.map +1 -1
  227. package/dist/sync/next/test/compact-events.test.js +2 -2
  228. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  229. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  230. package/dist/sync/next/test/event-fixtures.js +2 -2
  231. package/dist/sync/next/test/event-fixtures.js.map +1 -1
  232. package/dist/sync/sync-backend-kv.d.ts.map +1 -1
  233. package/dist/sync/sync-backend-kv.js.map +1 -1
  234. package/dist/sync/sync-backend.d.ts +3 -3
  235. package/dist/sync/sync-backend.d.ts.map +1 -1
  236. package/dist/sync/sync-backend.js +1 -1
  237. package/dist/sync/sync-backend.js.map +1 -1
  238. package/dist/sync/sync.d.ts +20 -0
  239. package/dist/sync/sync.d.ts.map +1 -1
  240. package/dist/sync/syncstate.d.ts +4 -17
  241. package/dist/sync/syncstate.d.ts.map +1 -1
  242. package/dist/sync/syncstate.js +51 -74
  243. package/dist/sync/syncstate.js.map +1 -1
  244. package/dist/sync/syncstate.test.js +112 -96
  245. package/dist/sync/syncstate.test.js.map +1 -1
  246. package/dist/sync/transport-chunking.js +3 -3
  247. package/dist/sync/transport-chunking.js.map +1 -1
  248. package/dist/sync/validate-push-payload.d.ts +2 -2
  249. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  250. package/dist/sync/validate-push-payload.js +4 -6
  251. package/dist/sync/validate-push-payload.js.map +1 -1
  252. package/dist/util.js +2 -2
  253. package/dist/util.js.map +1 -1
  254. package/dist/version.d.ts +7 -1
  255. package/dist/version.d.ts.map +1 -1
  256. package/dist/version.js +8 -4
  257. package/dist/version.js.map +1 -1
  258. package/package.json +66 -12
  259. package/src/ClientSessionLeaderThreadProxy.ts +16 -9
  260. package/src/WorkerTransportError.ts +12 -0
  261. package/src/adapter-types.ts +39 -3
  262. package/src/bounded-collections.ts +6 -5
  263. package/src/debug-info.ts +4 -4
  264. package/src/devtools/devtools-messages-client-session.ts +12 -0
  265. package/src/devtools/devtools-messages-common.ts +8 -4
  266. package/src/devtools/devtools-messages-leader.ts +12 -0
  267. package/src/devtools/mod.ts +1 -1
  268. package/src/errors.ts +18 -17
  269. package/src/index.ts +2 -0
  270. package/src/leader-thread/LeaderSyncProcessor.ts +417 -347
  271. package/src/leader-thread/RejectedPushError.ts +106 -0
  272. package/src/leader-thread/connection.ts +1 -1
  273. package/src/leader-thread/eventlog.ts +16 -14
  274. package/src/leader-thread/leader-worker-devtools.ts +107 -66
  275. package/src/leader-thread/make-leader-thread-layer.test.ts +1 -1
  276. package/src/leader-thread/make-leader-thread-layer.ts +41 -31
  277. package/src/leader-thread/materialize-event.ts +8 -4
  278. package/src/leader-thread/recreate-db.ts +1 -1
  279. package/src/leader-thread/shutdown-channel.ts +2 -6
  280. package/src/leader-thread/stream-events.ts +10 -5
  281. package/src/leader-thread/types.ts +7 -6
  282. package/src/logging.ts +4 -4
  283. package/src/make-client-session.ts +2 -2
  284. package/src/materializer-helper.ts +9 -9
  285. package/src/otel.ts +3 -2
  286. package/src/rematerialize-from-eventlog.ts +60 -60
  287. package/src/schema/EventDef/define.ts +22 -6
  288. package/src/schema/EventDef/deprecated.test.ts +129 -0
  289. package/src/schema/EventDef/deprecated.ts +175 -0
  290. package/src/schema/EventDef/event-def.ts +5 -0
  291. package/src/schema/EventDef/mod.ts +1 -0
  292. package/src/schema/EventSequenceNumber/client.ts +11 -11
  293. package/src/schema/EventSequenceNumber.test.ts +2 -1
  294. package/src/schema/LiveStoreEvent/client.test.ts +97 -0
  295. package/src/schema/LiveStoreEvent/client.ts +6 -3
  296. package/src/schema/schema.ts +9 -4
  297. package/src/schema/state/sqlite/client-document-def.test.ts +142 -3
  298. package/src/schema/state/sqlite/client-document-def.ts +37 -14
  299. package/src/schema/state/sqlite/column-annotations.test.ts +2 -1
  300. package/src/schema/state/sqlite/column-annotations.ts +2 -1
  301. package/src/schema/state/sqlite/column-def.test.ts +8 -6
  302. package/src/schema/state/sqlite/column-def.ts +41 -36
  303. package/src/schema/state/sqlite/column-spec.test.ts +3 -1
  304. package/src/schema/state/sqlite/column-spec.ts +9 -8
  305. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +2 -2
  306. package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +2 -1
  307. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +13 -4
  308. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +3 -3
  309. package/src/schema/state/sqlite/mod.ts +4 -5
  310. package/src/schema/state/sqlite/query-builder/api.ts +37 -8
  311. package/src/schema/state/sqlite/query-builder/astToSql.ts +87 -7
  312. package/src/schema/state/sqlite/query-builder/impl.test.ts +145 -3
  313. package/src/schema/state/sqlite/query-builder/impl.ts +26 -12
  314. package/src/schema/state/sqlite/schema-helpers.ts +2 -2
  315. package/src/schema/state/sqlite/table-def.test.ts +67 -4
  316. package/src/schema/state/sqlite/table-def.ts +8 -15
  317. package/src/schema/unknown-events.ts +2 -2
  318. package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +3 -1
  319. package/src/schema-management/common.ts +2 -2
  320. package/src/schema-management/migrations.ts +1 -1
  321. package/src/sql-queries/sql-queries.ts +10 -6
  322. package/src/sql-queries/sql-query-builder.ts +1 -0
  323. package/src/sqlite-db-helper.ts +3 -3
  324. package/src/sqlite-types.ts +3 -2
  325. package/src/sync/ClientSessionSyncProcessor.ts +142 -133
  326. package/src/sync/errors.ts +10 -22
  327. package/src/sync/mock-sync-backend.ts +139 -97
  328. package/src/sync/next/compact-events.ts +5 -5
  329. package/src/sync/next/facts.ts +7 -6
  330. package/src/sync/next/history-dag-common.ts +9 -6
  331. package/src/sync/next/history-dag.ts +3 -3
  332. package/src/sync/next/rebase-events.ts +1 -1
  333. package/src/sync/next/test/compact-events.calculator.test.ts +3 -2
  334. package/src/sync/next/test/compact-events.test.ts +4 -3
  335. package/src/sync/next/test/event-fixtures.ts +2 -2
  336. package/src/sync/sync-backend-kv.ts +1 -0
  337. package/src/sync/sync-backend.ts +5 -4
  338. package/src/sync/sync.ts +21 -0
  339. package/src/sync/syncstate.test.ts +513 -435
  340. package/src/sync/syncstate.ts +80 -86
  341. package/src/sync/transport-chunking.ts +3 -3
  342. package/src/sync/validate-push-payload.ts +4 -6
  343. package/src/util.ts +2 -2
  344. package/src/version.ts +8 -4
@@ -1,8 +1,9 @@
1
1
  import type { Schema, Scope } from '@livestore/utils/effect'
2
- import { Effect, Mailbox, Option, Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
2
+ import { Effect, Mailbox, Option, Queue, Ref, Stream, SubscriptionRef } from '@livestore/utils/effect'
3
+
3
4
  import { UnknownError } from '../errors.ts'
4
5
  import { EventSequenceNumber, type LiveStoreEvent } from '../schema/mod.ts'
5
- import { InvalidPushError } from './errors.ts'
6
+ import { type BackendIdMismatchError, type ServerAheadError } from './errors.ts'
6
7
  import * as SyncBackend from './sync-backend.ts'
7
8
  import { validatePushPayload } from './validate-push-payload.ts'
8
9
 
@@ -12,10 +13,17 @@ export interface MockSyncBackend {
12
13
  disconnect: Effect.Effect<void>
13
14
  makeSyncBackend: Effect.Effect<SyncBackend.SyncBackend, UnknownError, Scope.Scope>
14
15
  advance: (...batch: LiveStoreEvent.Global.Encoded[]) => Effect.Effect<void>
15
- /** Fail the next N push calls with an InvalidPushError (or custom error) */
16
+ /** Fail the next N push calls with an UnknownError, ServerAheadError, BackendIdMismatchError, or custom error */
16
17
  failNextPushes: (
17
18
  count: number,
18
- error?: (batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>,
19
+ error?: (
20
+ batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>,
21
+ ) => Effect.Effect<never, UnknownError | ServerAheadError | BackendIdMismatchError>,
22
+ ) => Effect.Effect<void>
23
+ /** Fail the next N pull calls with an UnknownError, BackendIdMismatchError, or custom error */
24
+ failNextPulls: (
25
+ count: number,
26
+ error?: () => Effect.Effect<never, UnknownError | BackendIdMismatchError>,
19
27
  ) => Effect.Effect<void>
20
28
  }
21
29
 
@@ -31,107 +39,136 @@ export const makeMockSyncBackend = (
31
39
  options?: MockSyncBackendOptions,
32
40
  ): Effect.Effect<MockSyncBackend, UnknownError, Scope.Scope> =>
33
41
  Effect.gen(function* () {
34
- const syncEventSequenceNumberRef = { current: EventSequenceNumber.Client.ROOT.global }
42
+ const span = yield* Effect.currentSpan.pipe(Effect.orDie)
43
+ const semaphore = yield* Effect.makeSemaphore(1)
44
+
45
+ // State refs
46
+ const syncHeadRef = yield* Ref.make(EventSequenceNumber.Client.ROOT.global)
47
+ const allEventsRef = yield* Ref.make<LiveStoreEvent.Global.Encoded[]>([])
48
+ const syncIsConnectedRef = yield* SubscriptionRef.make(options?.startConnected ?? false)
49
+
50
+ // Queues for streaming
35
51
  const syncPullQueue = yield* Queue.unbounded<LiveStoreEvent.Global.Encoded>()
36
52
  const pushedEventsQueue = yield* Mailbox.make<LiveStoreEvent.Global.Encoded>()
37
- const syncIsConnectedRef = yield* SubscriptionRef.make(options?.startConnected ?? false)
38
- const allEventsRef: { current: LiveStoreEvent.Global.Encoded[] } = { current: [] }
39
53
 
40
- const span = yield* Effect.currentSpan.pipe(Effect.orDie)
54
+ // Failure simulation state
55
+ const failPushRef = yield* Ref.make<
56
+ FailureState<UnknownError | ServerAheadError | BackendIdMismatchError, [ReadonlyArray<LiveStoreEvent.Global.Encoded>]>
57
+ >({ remaining: 0, error: undefined })
58
+ const failPullRef = yield* Ref.make<FailureState<UnknownError | BackendIdMismatchError, []>>({
59
+ remaining: 0,
60
+ error: undefined,
61
+ })
41
62
 
42
- const semaphore = yield* Effect.makeSemaphore(1)
63
+ const nonLiveChunkSize = Math.max(1, options?.nonLiveChunkSize ?? 100)
64
+
65
+ /** Check and consume a simulated failure, returning the error effect if one should fire */
66
+ const checkFailure = <E, Args extends unknown[]>(
67
+ ref: Ref.Ref<FailureState<E, Args>>,
68
+ defaultError: E,
69
+ ...args: Args
70
+ ): Effect.Effect<void, E> =>
71
+ Ref.modify(ref, (state) => {
72
+ if (state.remaining <= 0) {
73
+ return [Option.none(), state] as const
74
+ }
75
+ const error = state.error?.(...args) ?? Effect.fail(defaultError)
76
+ return [Option.some(error), { ...state, remaining: state.remaining - 1 }] as const
77
+ }).pipe(
78
+ Effect.flatMap(
79
+ Option.match({
80
+ onNone: () => Effect.void,
81
+ onSome: (errorEffect) => errorEffect,
82
+ }),
83
+ ),
84
+ )
85
+
86
+ const pullNonLive = (cursor: Option.Option<{ eventSequenceNumber: EventSequenceNumber.Global.Type }>) =>
87
+ Effect.gen(function* () {
88
+ const lastSeen = Option.match(cursor, {
89
+ onNone: () => EventSequenceNumber.Client.ROOT.global,
90
+ onSome: (_) => _.eventSequenceNumber,
91
+ })
92
+ const allEvents = yield* Ref.get(allEventsRef)
93
+ const slice = allEvents.filter((e) => e.seqNum > lastSeen)
94
+
95
+ // Split into chunks with remaining count for pageInfo
96
+ const chunks: Array<{ events: LiveStoreEvent.Global.Encoded[]; remaining: number }> = []
97
+ for (let i = 0; i < slice.length; i += nonLiveChunkSize) {
98
+ const end = Math.min(i + nonLiveChunkSize, slice.length)
99
+ chunks.push({
100
+ events: slice.slice(i, end),
101
+ remaining: Math.max(slice.length - end, 0),
102
+ })
103
+ }
104
+ // Always return at least one empty chunk
105
+ if (chunks.length === 0) {
106
+ chunks.push({ events: [], remaining: 0 })
107
+ }
108
+ return chunks
109
+ }).pipe(
110
+ Effect.map((chunks) =>
111
+ Stream.fromIterable(chunks).pipe(
112
+ Stream.map(({ events, remaining }) => ({
113
+ batch: events.map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
114
+ pageInfo: remaining > 0 ? SyncBackend.pageInfoMoreKnown(remaining) : SyncBackend.pageInfoNoMore,
115
+ })),
116
+ ),
117
+ ),
118
+ Stream.fromEffect,
119
+ Stream.flatten(),
120
+ )
43
121
 
44
- // TODO improve the API and implementation of simulating errors
45
- const failCounterRef = yield* SubscriptionRef.make(0)
46
- const failEffectRef = yield* SubscriptionRef.make<
47
- ((batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>) | undefined
48
- >(undefined)
122
+ const pullLive = Stream.concat(
123
+ Stream.make(SyncBackend.pullResItemEmpty()),
124
+ Stream.fromQueue(syncPullQueue).pipe(
125
+ Stream.chunks,
126
+ Stream.map((chunk) => ({
127
+ batch: [...chunk].map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
128
+ pageInfo: SyncBackend.pageInfoNoMore,
129
+ })),
130
+ ),
131
+ )
49
132
 
50
133
  const makeSyncBackend = Effect.gen(function* () {
51
- const nonLiveChunkSize = Math.max(1, options?.nonLiveChunkSize ?? 100)
52
-
53
134
  // TODO consider making offline state actively error pull/push.
54
135
  // Currently, offline only reflects in `isConnected`, while operations still succeed,
55
136
  // mirroring how some real providers behave during transient disconnects.
56
- return SyncBackend.of<Schema.JsonValue>({
137
+ return SyncBackend.of({
57
138
  isConnected: syncIsConnectedRef,
58
139
  connect: SubscriptionRef.set(syncIsConnectedRef, true),
59
140
  ping: Effect.void,
60
- pull: (cursor, options) =>
61
- (options?.live
62
- ? Stream.concat(
63
- Stream.make(SyncBackend.pullResItemEmpty()),
64
- Stream.fromQueue(syncPullQueue).pipe(
65
- Stream.chunks,
66
- Stream.map((chunk) => ({
67
- batch: [...chunk].map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
68
- pageInfo: SyncBackend.pageInfoNoMore,
69
- })),
70
- ),
71
- )
72
- : Stream.fromEffect(
73
- Effect.sync(() => {
74
- const lastSeen = cursor.pipe(
75
- Option.match({
76
- onNone: () => EventSequenceNumber.Client.ROOT.global,
77
- onSome: (_) => _.eventSequenceNumber,
78
- }),
79
- )
80
- // All events with seqNum greater than lastSeen
81
- const slice = allEventsRef.current.filter((e) => e.seqNum > lastSeen)
82
- // Split into configured chunk size
83
- const chunks: { events: LiveStoreEvent.Global.Encoded[]; remaining: number }[] = []
84
- for (let i = 0; i < slice.length; i += nonLiveChunkSize) {
85
- const end = Math.min(i + nonLiveChunkSize, slice.length)
86
- const remaining = Math.max(slice.length - end, 0)
87
- chunks.push({ events: slice.slice(i, end), remaining })
88
- }
89
- if (chunks.length === 0) {
90
- chunks.push({ events: [], remaining: 0 })
91
- }
92
- return chunks
93
- }),
94
- ).pipe(
95
- Stream.flatMap((chunks) =>
96
- Stream.fromIterable(chunks).pipe(
97
- Stream.map(({ events, remaining }) => ({
98
- batch: events.map((eventEncoded) => ({ eventEncoded, metadata: Option.none() })),
99
- pageInfo: remaining > 0 ? SyncBackend.pageInfoMoreKnown(remaining) : SyncBackend.pageInfoNoMore,
100
- })),
101
- ),
102
- ),
103
- )
104
- ).pipe(Stream.withSpan('MockSyncBackend:pull', { parent: span })),
141
+ pull: (cursor, pullOptions) =>
142
+ Stream.fromEffect(
143
+ checkFailure(
144
+ failPullRef,
145
+ new UnknownError({ cause: new Error('MockSyncBackend: simulated pull failure') }),
146
+ ),
147
+ ).pipe(
148
+ Stream.flatMap(() => (pullOptions?.live === true ? pullLive : pullNonLive(cursor))),
149
+ Stream.withSpan('MockSyncBackend:pull', { parent: span }),
150
+ ),
105
151
  push: (batch) =>
106
152
  Effect.gen(function* () {
107
- yield* validatePushPayload(batch, syncEventSequenceNumberRef.current)
108
-
109
- const remaining = yield* SubscriptionRef.get(failCounterRef)
110
- if (remaining > 0) {
111
- const maybeFail = yield* SubscriptionRef.get(failEffectRef)
112
- // decrement counter first
113
- yield* SubscriptionRef.set(failCounterRef, remaining - 1)
114
- if (maybeFail) {
115
- return yield* maybeFail(batch)
116
- }
117
- return yield* new InvalidPushError({
118
- cause: new UnknownError({ cause: new Error('MockSyncBackend: simulated push failure') }),
119
- })
120
- }
153
+ const currentHead = yield* Ref.get(syncHeadRef)
154
+ yield* validatePushPayload(batch, currentHead)
155
+
156
+ yield* checkFailure(
157
+ failPushRef,
158
+ new UnknownError({ cause: new Error('MockSyncBackend: simulated push failure') }),
159
+ batch,
160
+ )
121
161
 
122
162
  yield* Effect.sleep(10).pipe(Effect.withSpan('MockSyncBackend:push:sleep')) // Simulate network latency
123
163
 
124
164
  yield* pushedEventsQueue.offerAll(batch)
125
165
  yield* syncPullQueue.offerAll(batch)
126
- allEventsRef.current = allEventsRef.current.concat(batch)
127
-
128
- syncEventSequenceNumberRef.current = batch.at(-1)!.seqNum
166
+ yield* Ref.update(allEventsRef, (events) => events.concat(batch))
167
+ yield* Ref.set(syncHeadRef, batch.at(-1)!.seqNum)
129
168
  }).pipe(
130
169
  Effect.withSpan('MockSyncBackend:push', {
131
170
  parent: span,
132
- attributes: {
133
- nums: batch.map((_) => _.seqNum),
134
- },
171
+ attributes: { nums: batch.map((_) => _.seqNum) },
135
172
  }),
136
173
  semaphore.withPermits(1),
137
174
  ),
@@ -148,8 +185,8 @@ export const makeMockSyncBackend = (
148
185
 
149
186
  const advance = (...batch: LiveStoreEvent.Global.Encoded[]) =>
150
187
  Effect.gen(function* () {
151
- syncEventSequenceNumberRef.current = batch.at(-1)!.seqNum
152
- allEventsRef.current = allEventsRef.current.concat(batch)
188
+ yield* Ref.set(syncHeadRef, batch.at(-1)!.seqNum)
189
+ yield* Ref.update(allEventsRef, (events) => events.concat(batch))
153
190
  yield* syncPullQueue.offerAll(batch)
154
191
  }).pipe(
155
192
  Effect.withSpan('MockSyncBackend:advance', {
@@ -159,26 +196,31 @@ export const makeMockSyncBackend = (
159
196
  semaphore.withPermits(1),
160
197
  )
161
198
 
162
- const connect = SubscriptionRef.set(syncIsConnectedRef, true)
163
- const disconnect = SubscriptionRef.set(syncIsConnectedRef, false)
164
-
165
199
  const failNextPushes = (
166
200
  count: number,
167
- error?: (batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>) => Effect.Effect<never, InvalidPushError>,
168
- ) =>
169
- Effect.gen(function* () {
170
- yield* SubscriptionRef.set(failCounterRef, count)
171
- yield* SubscriptionRef.set(failEffectRef, error)
172
- })
201
+ error?: (
202
+ batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>,
203
+ ) => Effect.Effect<never, UnknownError | ServerAheadError | BackendIdMismatchError>,
204
+ ) => Ref.set(failPushRef, { remaining: count, error })
205
+
206
+ const failNextPulls = (
207
+ count: number,
208
+ error?: () => Effect.Effect<never, UnknownError | BackendIdMismatchError>,
209
+ ) => Ref.set(failPullRef, { remaining: count, error })
173
210
 
174
211
  return {
175
- syncEventSequenceNumberRef,
176
- syncPullQueue,
177
212
  pushedEvents: Mailbox.toStream(pushedEventsQueue),
178
- connect,
179
- disconnect,
213
+ connect: SubscriptionRef.set(syncIsConnectedRef, true),
214
+ disconnect: SubscriptionRef.set(syncIsConnectedRef, false),
180
215
  makeSyncBackend,
181
216
  advance,
182
217
  failNextPushes,
218
+ failNextPulls,
183
219
  }
184
220
  }).pipe(Effect.withSpanScoped('MockSyncBackend'))
221
+
222
+ /** Internal state for simulating failures */
223
+ interface FailureState<E, Args extends unknown[]> {
224
+ remaining: number
225
+ error: ((...args: Args) => Effect.Effect<never, E>) | undefined
226
+ }
@@ -16,7 +16,7 @@ export const compactEvents = (inputDag: HistoryDag): { dag: HistoryDag; compacte
16
16
  const dag = inputDag.copy()
17
17
  const compactedEventCount = 0
18
18
 
19
- const orderedEventSequenceNumberStrs = dag.topologicalNodeIds().reverse()
19
+ const orderedEventSequenceNumberStrs = dag.topologicalNodeIds().toReversed()
20
20
 
21
21
  // drop root
22
22
  orderedEventSequenceNumberStrs.pop()
@@ -29,7 +29,7 @@ export const compactEvents = (inputDag: HistoryDag): { dag: HistoryDag; compacte
29
29
  const subDagsForEvent = Array.from(makeSubDagsForEvent(dag, eventNumStr))
30
30
  for (const subDag of subDagsForEvent) {
31
31
  let shouldRetry = true
32
- while (shouldRetry) {
32
+ while (shouldRetry === true) {
33
33
  const subDagsInHistory = findSubDagsInHistory(dag, subDag, eventNumStr)
34
34
 
35
35
  // console.debug(
@@ -64,7 +64,7 @@ export const compactEvents = (inputDag: HistoryDag): { dag: HistoryDag; compacte
64
64
  return { dag, compactedEventCount }
65
65
  }
66
66
 
67
- function* makeSubDagsForEvent(inputDag: HistoryDag, eventNumStr: string): Generator<HistoryDag> {
67
+ const makeSubDagsForEvent = function* (inputDag: HistoryDag, eventNumStr: string): Generator<HistoryDag> {
68
68
  /** Map from eventNumStr to array of eventNumStrs that are dependencies */
69
69
  let nextIterationEls: Map<string, string[]> = new Map([[eventNumStr, []]])
70
70
  let previousDag: HistoryDag | undefined
@@ -131,7 +131,7 @@ const findSubDagsInHistory = (
131
131
  allOutsideDependencies.push(outsideDependencies)
132
132
  }
133
133
 
134
- if (outsideDependencies.length === 0 && dagReplacesDag(subDag, targetSubDag)) {
134
+ if (outsideDependencies.length === 0 && dagReplacesDag(subDag, targetSubDag) === true) {
135
135
  subDags.push(subDag)
136
136
  } else {
137
137
  break
@@ -171,7 +171,7 @@ const dagDependsOnDag = (dagA: HistoryDag, dagB: HistoryDag, inputDag: HistoryDa
171
171
  for (const edgeEntryA of inputDag.inboundEdgeEntries(nodeAIdStr)) {
172
172
  if (edgeEntryA.attributes.type === 'facts') {
173
173
  const depNodeIdStr = edgeEntryA.target
174
- if (dagB.hasNode(depNodeIdStr)) {
174
+ if (dagB.hasNode(depNodeIdStr) === true) {
175
175
  return true
176
176
  }
177
177
  }
@@ -1,4 +1,5 @@
1
1
  import { notYetImplemented } from '@livestore/utils'
2
+
2
3
  import type {
3
4
  EventDefFactInput,
4
5
  EventDefFacts,
@@ -129,7 +130,7 @@ const isSubSetMapByValue = (setA: EventDefFacts, setB: EventDefFacts) => {
129
130
  /** Check if setA is a subset of setB */
130
131
  const isSubSetMapByKey = (setA: EventDefFacts, setB: EventDefFacts) => {
131
132
  for (const [key, _value] of setA) {
132
- if (!setB.has(key)) {
133
+ if (setB.has(key) === false) {
133
134
  return false
134
135
  }
135
136
  }
@@ -167,7 +168,7 @@ export const factsToString = (facts: EventDefFacts) => {
167
168
 
168
169
  export const factsIntersect = (setA: EventDefFacts, setB: EventDefFacts): boolean => {
169
170
  for (const [key, _value] of setA) {
170
- if (setB.has(key)) {
171
+ if (setB.has(key) === true) {
171
172
  return true
172
173
  }
173
174
  }
@@ -198,7 +199,7 @@ export const getFactsGroupForEventArgs = ({
198
199
  }
199
200
  }
200
201
 
201
- notYetImplemented(`getFactsGroupForEventArgs: ${prop.toString()} is not yet implemented`)
202
+ return notYetImplemented(`getFactsGroupForEventArgs: ${prop.toString()} is not yet implemented`)
202
203
  },
203
204
  })
204
205
 
@@ -215,9 +216,9 @@ export const getFactsGroupForEventArgs = ({
215
216
  return map
216
217
  }
217
218
  const facts = {
218
- modifySet: factsRes?.modify.set ? iterableToMap(factsRes.modify.set) : new Map(),
219
- modifyUnset: factsRes?.modify.unset ? iterableToMap(factsRes.modify.unset) : new Map(),
220
- depRequire: factsRes?.require ? iterableToMap(factsRes.require) : new Map(),
219
+ modifySet: factsRes?.modify.set !== undefined ? iterableToMap(factsRes.modify.set) : new Map(),
220
+ modifyUnset: factsRes?.modify.unset !== undefined ? iterableToMap(factsRes.modify.unset) : new Map(),
221
+ depRequire: factsRes?.require !== undefined ? iterableToMap(factsRes.require) : new Map(),
221
222
  depRead,
222
223
  }
223
224
 
@@ -1,5 +1,6 @@
1
1
  import { shouldNeverHappen } from '@livestore/utils'
2
2
  import { Graph } from '@livestore/utils/effect'
3
+
3
4
  import type { EventDefFactsGroup } from '../../schema/EventDef/mod.ts'
4
5
  import * as EventSequenceNumber from '../../schema/EventSequenceNumber/mod.ts'
5
6
 
@@ -74,13 +75,13 @@ export class HistoryDag {
74
75
  }) {
75
76
  this.graph = graph
76
77
  this.options = { ...defaultOptions, ...options }
77
- this.idToIndex = idToIndex ? new Map(idToIndex) : new Map()
78
- this.indexToId = indexToId ? new Map(indexToId) : new Map()
78
+ this.idToIndex = idToIndex !== undefined ? new Map(idToIndex) : new Map()
79
+ this.indexToId = indexToId !== undefined ? new Map(indexToId) : new Map()
79
80
  }
80
81
 
81
82
  static create(options?: Partial<HistoryDagOptions>): HistoryDag {
82
83
  const graph = Graph.beginMutation(Graph.directed<HistoryDagNode, HistoryDagEdgeAttributes>())
83
- return options ? new HistoryDag({ graph, options }) : new HistoryDag({ graph })
84
+ return options !== undefined ? new HistoryDag({ graph, options }) : new HistoryDag({ graph })
84
85
  }
85
86
 
86
87
  copy(): HistoryDag {
@@ -103,11 +104,13 @@ export class HistoryDag {
103
104
  topologicalNodeIds(): Array<string> {
104
105
  const walker = Graph.topo(this.graph)
105
106
  const indices = Array.from(Graph.indices(walker))
106
- return indices.map((index) => this.indexToId.get(index) ?? shouldNeverHappen(`Missing node id for index ${index}`))
107
+ return indices.map(
108
+ (index) => this.indexToId.get(index) ?? shouldNeverHappen(`Missing node id for index ${String(index)}`),
109
+ )
107
110
  }
108
111
 
109
112
  addNode(id: string, attributes: HistoryDagNode): void {
110
- if (this.idToIndex.has(id)) {
113
+ if (this.idToIndex.has(id) === true) {
111
114
  shouldNeverHappen(`HistoryDag node ${id} already exists`)
112
115
  }
113
116
 
@@ -183,7 +186,7 @@ export class HistoryDag {
183
186
  return []
184
187
  }
185
188
  const incoming = this.graph.reverseAdjacency.get(index)
186
- return incoming ? [...incoming] : []
189
+ return incoming !== undefined ? [...incoming] : []
187
190
  }
188
191
 
189
192
  outboundEdgeEntries(id: string): Array<HistoryDagEdgeEntry> {
@@ -43,12 +43,12 @@ export const historyDagFromNodes = (dagNodes: HistoryDagNode[], options?: { skip
43
43
 
44
44
  while (currentSeqNumStr !== EventSequenceNumber.Client.toString(rootParentNum)) {
45
45
  const parentEdge = dag.inEdges(currentSeqNumStr).find((e) => dag.getEdgeAttribute(e, 'type') === 'parent')
46
- if (!parentEdge) return null
46
+ if (parentEdge == null) return null
47
47
 
48
48
  const parentSeqNumStr = dag.source(parentEdge)
49
49
  const parentNode = dag.getNodeAttributes(parentSeqNumStr)
50
50
 
51
- if (parentNode.factsGroup.modifySet.has(factKey) || parentNode.factsGroup.modifyUnset.has(factKey)) {
51
+ if (parentNode.factsGroup.modifySet.has(factKey) === true || parentNode.factsGroup.modifyUnset.has(factKey) === true) {
52
52
  return parentNode
53
53
  }
54
54
 
@@ -58,7 +58,7 @@ export const historyDagFromNodes = (dagNodes: HistoryDagNode[], options?: { skip
58
58
  return null
59
59
  })()
60
60
 
61
- if (depNode) {
61
+ if (depNode !== null) {
62
62
  const depNodeIdStr = EventSequenceNumber.Client.toString(depNode.seqNum)
63
63
  const nodeIdStr = EventSequenceNumber.Client.toString(node.seqNum)
64
64
  if (dag.edges(depNodeIdStr, nodeIdStr).filter((e) => dag.getEdgeAttributes(e).type === 'facts').length === 0) {
@@ -31,7 +31,7 @@ export type RebaseOutput = {
31
31
  export type RebaseFn = (input: RebaseInput) => RebaseOutput
32
32
 
33
33
  export const defaultRebaseFn: RebaseFn = ({ pendingLocalEvents }) => {
34
- if (pendingLocalEvents.some((_) => _.conflictType === 'missing-requirement')) {
34
+ if (pendingLocalEvents.some((_) => _.conflictType === 'missing-requirement') === true) {
35
35
  throw new Error('missing-requirement conflicts must be resolved before rebasing')
36
36
  }
37
37
 
@@ -1,6 +1,7 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
1
3
  import { defineEvent } from '@livestore/common/schema'
2
4
  import { Schema } from '@livestore/utils/effect'
3
- import { describe, expect, it } from 'vitest'
4
5
 
5
6
  import { compactEvents } from '../compact-events.ts'
6
7
  import { historyDagFromNodes } from '../history-dag.ts'
@@ -33,7 +34,7 @@ const eventDefs = {
33
34
  schema: Schema.Struct({ value: Schema.Number }),
34
35
  facts: ({ value }, currentFacts) => ({
35
36
  modify: {
36
- set: value === 0 || currentFacts.has(facts.multiplyByZero) ? [facts.multiplyByZero] : [],
37
+ set: value === 0 || currentFacts.has(facts.multiplyByZero) === true ? [facts.multiplyByZero] : [],
37
38
  unset: value === 0 ? [] : [facts.multiplyByZero],
38
39
  },
39
40
  }),
@@ -1,10 +1,11 @@
1
- import type { EventDefFacts } from '@livestore/common/schema'
2
1
  import { describe, expect, it } from 'vitest'
3
2
 
3
+ import type { EventDefFacts } from '@livestore/common/schema'
4
+
4
5
  import { compactEvents } from '../compact-events.ts'
5
- import { historyDagFromNodes } from '../history-dag.ts'
6
6
  import type { HistoryDagNode } from '../history-dag-common.ts'
7
7
  import { EMPTY_FACT_VALUE } from '../history-dag-common.ts'
8
+ import { historyDagFromNodes } from '../history-dag.ts'
8
9
  import { events as eventDefs, printEvent, toEventNodes } from './event-fixtures.ts'
9
10
 
10
11
  const customStringify = (value: any): string => {
@@ -19,7 +20,7 @@ const customStringify = (value: any): string => {
19
20
  if (type === 'number' || type === 'boolean') {
20
21
  return String(value)
21
22
  }
22
- if (Array.isArray(value)) {
23
+ if (Array.isArray(value) === true) {
23
24
  const elements = value.map((el) => customStringify(el))
24
25
  return `[${elements.join(', ')}]`
25
26
  }
@@ -4,9 +4,9 @@ import type { EventDef } from '../../../schema/EventDef/mod.ts'
4
4
  import { defineEvent, defineFacts } from '../../../schema/EventDef/mod.ts'
5
5
  import * as EventSequenceNumber from '../../../schema/EventSequenceNumber/mod.ts'
6
6
  import { factsSnapshotForDag, getFactsGroupForEventArgs } from '../facts.ts'
7
- import { historyDagFromNodes } from '../history-dag.ts'
8
7
  import type { HistoryDagNode } from '../history-dag-common.ts'
9
8
  import { rootEventNode } from '../history-dag-common.ts'
9
+ import { historyDagFromNodes } from '../history-dag.ts'
10
10
 
11
11
  export const printEvent = ({ seqNum, parentSeqNum, factsGroup, ...rest }: HistoryDagNode) => ({
12
12
  seqNum: EventSequenceNumber.Client.toString(seqNum),
@@ -45,7 +45,7 @@ export const events = {
45
45
  // {
46
46
  facts: ({ id }, currentFacts) =>
47
47
  // TODO enable an API along the lines of `map.has(key, value)`
48
- currentFacts.has(facts.todoExists(id)) && currentFacts.get(facts.todoIsWriteable(id, true)[0]) === false
48
+ currentFacts.has(facts.todoExists(id)) === true && currentFacts.get(facts.todoIsWriteable(id, true)[0]) === false
49
49
  ? { require: [facts.todoExists(id), facts.todoIsWriteable(id, true)] }
50
50
  : { modify: { set: [facts.todoExists(id), facts.todoIsWriteable(id, true), facts.todoTextUpdated(id)] } },
51
51
  }),
@@ -1,4 +1,5 @@
1
1
  import { Effect, KeyValueStore, Option } from '@livestore/utils/effect'
2
+
2
3
  import { UnknownError } from '../errors.ts'
3
4
 
4
5
  export const makeBackendIdHelper = Effect.gen(function* () {
@@ -9,10 +9,11 @@ import {
9
9
  type Stream,
10
10
  type SubscriptionRef,
11
11
  } from '@livestore/utils/effect'
12
+
12
13
  import type { UnknownError } from '../adapter-types.ts'
13
14
  import type * as LiveStoreEvent from '../schema/LiveStoreEvent/mod.ts'
14
15
  import type { EventSequenceNumber } from '../schema/mod.ts'
15
- import type { InvalidPullError, InvalidPushError, IsOfflineError } from './errors.ts'
16
+ import type { BackendIdMismatchError, IsOfflineError, ServerAheadError } from './errors.ts'
16
17
 
17
18
  export * from './sync-backend-kv.ts'
18
19
 
@@ -60,7 +61,7 @@ export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
60
61
  */
61
62
  live?: boolean
62
63
  },
63
- ) => Stream.Stream<PullResItem<TSyncMetadata>, IsOfflineError | InvalidPullError>
64
+ ) => Stream.Stream<PullResItem<TSyncMetadata>, IsOfflineError | BackendIdMismatchError | UnknownError >
64
65
  // TODO support transactions (i.e. group of mutation events which need to be applied together)
65
66
  push: (
66
67
  /**
@@ -69,7 +70,7 @@ export type SyncBackend<TSyncMetadata = Schema.JsonValue> = {
69
70
  * - sequence numbers must be in ascending order
70
71
  * */
71
72
  batch: ReadonlyArray<LiveStoreEvent.Global.Encoded>,
72
- ) => Effect.Effect<void, IsOfflineError | InvalidPushError>
73
+ ) => Effect.Effect<void, IsOfflineError | BackendIdMismatchError | UnknownError | ServerAheadError>
73
74
  ping: Effect.Effect<void, IsOfflineError | UnknownError | Cause.TimeoutException>
74
75
  // TODO also expose latency information additionally to whether the backend is connected
75
76
  isConnected: SubscriptionRef.SubscriptionRef<boolean>
@@ -178,7 +179,7 @@ export const cursorFromPullResItem = <TSyncMetadata = Schema.JsonValue>(
178
179
  metadata: Option.Option<TSyncMetadata>
179
180
  }> => {
180
181
  const lastEvent = item.batch.at(-1)
181
- if (!lastEvent) {
182
+ if (lastEvent == null) {
182
183
  return Option.none()
183
184
  }
184
185
  return Option.some({ eventSequenceNumber: lastEvent.eventEncoded.seqNum, metadata: lastEvent.metadata })
package/src/sync/sync.ts CHANGED
@@ -2,6 +2,7 @@ export * from './errors.ts'
2
2
  export * as SyncBackend from './sync-backend.ts'
3
3
 
4
4
  import type { Schema } from '@livestore/utils/effect'
5
+
5
6
  import type { InitialSyncOptions } from '../leader-thread/types.ts'
6
7
  import type { SyncBackendConstructor } from './sync-backend.ts'
7
8
 
@@ -19,6 +20,26 @@ export type SyncOptions<TPayload = Schema.JsonValue> = {
19
20
  * @default 'ignore'
20
21
  * */
21
22
  onSyncError?: 'shutdown' | 'ignore'
23
+ /**
24
+ * What to do when the sync backend identity has changed (i.e. the backend was reset).
25
+ *
26
+ * This commonly happens during development when:
27
+ * - The sync backend state is deleted (e.g. `.wrangler/state` for Cloudflare)
28
+ * - Running with a `--reset` flag
29
+ * - Schema changes require re-backfilling data
30
+ *
31
+ * Options:
32
+ * - `'reset'`: Clear local storage (eventlog and state databases) and shutdown.
33
+ * The app will need to restart and will sync fresh data from the backend.
34
+ * This is the recommended option for development.
35
+ * - `'shutdown'`: Shutdown without clearing local storage.
36
+ * On restart, the client will still have stale data and hit the same error.
37
+ * - `'ignore'`: Log the error and continue running.
38
+ * The client will show stale data but keep running (effectively offline mode).
39
+ *
40
+ * @default 'reset'
41
+ */
42
+ onBackendIdMismatch?: 'reset' | 'shutdown' | 'ignore'
22
43
  /**
23
44
  * Whether the sync backend should reactively pull new events from the sync backend
24
45
  * @default true