@livestore/common 0.4.0-dev.0 → 0.4.0-dev.10

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 (255) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +7 -2
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  4. package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
  5. package/dist/adapter-types.d.ts +9 -3
  6. package/dist/adapter-types.d.ts.map +1 -1
  7. package/dist/adapter-types.js.map +1 -1
  8. package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
  9. package/dist/devtools/devtools-messages-common.d.ts +7 -14
  10. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  11. package/dist/devtools/devtools-messages-common.js +1 -6
  12. package/dist/devtools/devtools-messages-common.js.map +1 -1
  13. package/dist/devtools/devtools-messages-leader.d.ts +27 -25
  14. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  15. package/dist/errors.d.ts +47 -5
  16. package/dist/errors.d.ts.map +1 -1
  17. package/dist/errors.js +22 -3
  18. package/dist/errors.js.map +1 -1
  19. package/dist/leader-thread/LeaderSyncProcessor.d.ts +7 -3
  20. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  21. package/dist/leader-thread/LeaderSyncProcessor.js +122 -49
  22. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  23. package/dist/leader-thread/eventlog.d.ts +4 -10
  24. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  25. package/dist/leader-thread/eventlog.js +4 -6
  26. package/dist/leader-thread/eventlog.js.map +1 -1
  27. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  28. package/dist/leader-thread/leader-worker-devtools.js +6 -2
  29. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  30. package/dist/leader-thread/make-leader-thread-layer.d.ts +1 -2
  31. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  32. package/dist/leader-thread/make-leader-thread-layer.js +68 -19
  33. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  34. package/dist/leader-thread/make-leader-thread-layer.test.d.ts +2 -0
  35. package/dist/leader-thread/make-leader-thread-layer.test.d.ts.map +1 -0
  36. package/dist/leader-thread/make-leader-thread-layer.test.js +32 -0
  37. package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -0
  38. package/dist/leader-thread/materialize-event.d.ts +2 -2
  39. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  40. package/dist/leader-thread/materialize-event.js +23 -9
  41. package/dist/leader-thread/materialize-event.js.map +1 -1
  42. package/dist/leader-thread/recreate-db.d.ts +2 -3
  43. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  44. package/dist/leader-thread/recreate-db.js +1 -1
  45. package/dist/leader-thread/recreate-db.js.map +1 -1
  46. package/dist/leader-thread/shutdown-channel.d.ts +2 -2
  47. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  48. package/dist/leader-thread/shutdown-channel.js +2 -2
  49. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  50. package/dist/leader-thread/types.d.ts +7 -5
  51. package/dist/leader-thread/types.d.ts.map +1 -1
  52. package/dist/leader-thread/types.js.map +1 -1
  53. package/dist/materializer-helper.d.ts +1 -1
  54. package/dist/materializer-helper.d.ts.map +1 -1
  55. package/dist/materializer-helper.js +20 -4
  56. package/dist/materializer-helper.js.map +1 -1
  57. package/dist/rematerialize-from-eventlog.d.ts +1 -1
  58. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  59. package/dist/rematerialize-from-eventlog.js +25 -16
  60. package/dist/rematerialize-from-eventlog.js.map +1 -1
  61. package/dist/schema/EventDef.d.ts +3 -0
  62. package/dist/schema/EventDef.d.ts.map +1 -1
  63. package/dist/schema/EventDef.js.map +1 -1
  64. package/dist/schema/LiveStoreEvent.d.ts +1 -1
  65. package/dist/schema/LiveStoreEvent.d.ts.map +1 -1
  66. package/dist/schema/LiveStoreEvent.js +1 -2
  67. package/dist/schema/LiveStoreEvent.js.map +1 -1
  68. package/dist/schema/mod.d.ts +2 -0
  69. package/dist/schema/mod.d.ts.map +1 -1
  70. package/dist/schema/mod.js +1 -0
  71. package/dist/schema/mod.js.map +1 -1
  72. package/dist/schema/schema.d.ts +15 -0
  73. package/dist/schema/schema.d.ts.map +1 -1
  74. package/dist/schema/schema.js +26 -1
  75. package/dist/schema/schema.js.map +1 -1
  76. package/dist/schema/state/sqlite/client-document-def.d.ts +35 -5
  77. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  78. package/dist/schema/state/sqlite/client-document-def.js +95 -4
  79. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  80. package/dist/schema/state/sqlite/client-document-def.test.js +16 -0
  81. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  82. package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
  83. package/dist/schema/state/sqlite/column-annotations.js +14 -6
  84. package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
  85. package/dist/schema/state/sqlite/column-def.d.ts +19 -0
  86. package/dist/schema/state/sqlite/column-def.d.ts.map +1 -0
  87. package/dist/schema/state/sqlite/column-def.js +179 -0
  88. package/dist/schema/state/sqlite/column-def.js.map +1 -0
  89. package/dist/schema/state/sqlite/column-def.test.d.ts +2 -0
  90. package/dist/schema/state/sqlite/column-def.test.d.ts.map +1 -0
  91. package/dist/schema/state/sqlite/column-def.test.js +572 -0
  92. package/dist/schema/state/sqlite/column-def.test.js.map +1 -0
  93. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +2 -1
  94. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
  95. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +23 -6
  96. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
  97. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
  98. package/dist/schema/state/sqlite/db-schema/dsl/mod.js +2 -1
  99. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
  100. package/dist/schema/state/sqlite/mod.d.ts +1 -1
  101. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  102. package/dist/schema/state/sqlite/mod.js +1 -1
  103. package/dist/schema/state/sqlite/mod.js.map +1 -1
  104. package/dist/schema/state/sqlite/query-builder/api.d.ts +5 -2
  105. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  106. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  107. package/dist/schema/state/sqlite/query-builder/impl.js +6 -2
  108. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  109. package/dist/schema/state/sqlite/query-builder/impl.test.js +137 -2
  110. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  111. package/dist/schema/state/sqlite/system-tables.d.ts +42 -6
  112. package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -1
  113. package/dist/schema/state/sqlite/system-tables.js +2 -0
  114. package/dist/schema/state/sqlite/system-tables.js.map +1 -1
  115. package/dist/schema/state/sqlite/table-def.d.ts +6 -8
  116. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  117. package/dist/schema/state/sqlite/table-def.js +4 -211
  118. package/dist/schema/state/sqlite/table-def.js.map +1 -1
  119. package/dist/schema/state/sqlite/table-def.test.js +59 -453
  120. package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
  121. package/dist/schema/unknown-events.d.ts +47 -0
  122. package/dist/schema/unknown-events.d.ts.map +1 -0
  123. package/dist/schema/unknown-events.js +69 -0
  124. package/dist/schema/unknown-events.js.map +1 -0
  125. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  126. package/dist/sql-queries/sql-query-builder.js +2 -1
  127. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  128. package/dist/sync/ClientSessionSyncProcessor.d.ts +9 -11
  129. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  130. package/dist/sync/ClientSessionSyncProcessor.js +35 -33
  131. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  132. package/dist/sync/errors.d.ts +61 -0
  133. package/dist/sync/errors.d.ts.map +1 -0
  134. package/dist/sync/errors.js +36 -0
  135. package/dist/sync/errors.js.map +1 -0
  136. package/dist/sync/index.d.ts +3 -0
  137. package/dist/sync/index.d.ts.map +1 -1
  138. package/dist/sync/index.js +3 -0
  139. package/dist/sync/index.js.map +1 -1
  140. package/dist/sync/mock-sync-backend.d.ts +23 -0
  141. package/dist/sync/mock-sync-backend.d.ts.map +1 -0
  142. package/dist/sync/mock-sync-backend.js +114 -0
  143. package/dist/sync/mock-sync-backend.js.map +1 -0
  144. package/dist/sync/next/compact-events.d.ts.map +1 -1
  145. package/dist/sync/next/compact-events.js +4 -5
  146. package/dist/sync/next/compact-events.js.map +1 -1
  147. package/dist/sync/next/facts.d.ts.map +1 -1
  148. package/dist/sync/next/facts.js +1 -2
  149. package/dist/sync/next/facts.js.map +1 -1
  150. package/dist/sync/next/history-dag-common.d.ts +50 -11
  151. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  152. package/dist/sync/next/history-dag-common.js +193 -4
  153. package/dist/sync/next/history-dag-common.js.map +1 -1
  154. package/dist/sync/next/history-dag.d.ts.map +1 -1
  155. package/dist/sync/next/history-dag.js +3 -1
  156. package/dist/sync/next/history-dag.js.map +1 -1
  157. package/dist/sync/sync-backend-kv.d.ts +7 -0
  158. package/dist/sync/sync-backend-kv.d.ts.map +1 -0
  159. package/dist/sync/sync-backend-kv.js +18 -0
  160. package/dist/sync/sync-backend-kv.js.map +1 -0
  161. package/dist/sync/sync-backend.d.ts +105 -0
  162. package/dist/sync/sync-backend.d.ts.map +1 -0
  163. package/dist/sync/sync-backend.js +61 -0
  164. package/dist/sync/sync-backend.js.map +1 -0
  165. package/dist/sync/sync.d.ts +6 -84
  166. package/dist/sync/sync.d.ts.map +1 -1
  167. package/dist/sync/sync.js +2 -27
  168. package/dist/sync/sync.js.map +1 -1
  169. package/dist/sync/transport-chunking.d.ts +36 -0
  170. package/dist/sync/transport-chunking.d.ts.map +1 -0
  171. package/dist/sync/transport-chunking.js +56 -0
  172. package/dist/sync/transport-chunking.js.map +1 -0
  173. package/dist/sync/validate-push-payload.d.ts +1 -1
  174. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  175. package/dist/sync/validate-push-payload.js +6 -6
  176. package/dist/sync/validate-push-payload.js.map +1 -1
  177. package/dist/testing/event-factory.d.ts +68 -0
  178. package/dist/testing/event-factory.d.ts.map +1 -0
  179. package/dist/testing/event-factory.js +80 -0
  180. package/dist/testing/event-factory.js.map +1 -0
  181. package/dist/testing/mod.d.ts +2 -0
  182. package/dist/testing/mod.d.ts.map +1 -0
  183. package/dist/testing/mod.js +2 -0
  184. package/dist/testing/mod.js.map +1 -0
  185. package/dist/version.d.ts +2 -2
  186. package/dist/version.d.ts.map +1 -1
  187. package/dist/version.js +2 -2
  188. package/dist/version.js.map +1 -1
  189. package/package.json +7 -8
  190. package/src/ClientSessionLeaderThreadProxy.ts +7 -2
  191. package/src/adapter-types.ts +13 -3
  192. package/src/devtools/devtools-messages-common.ts +1 -8
  193. package/src/errors.ts +33 -4
  194. package/src/leader-thread/LeaderSyncProcessor.ts +179 -57
  195. package/src/leader-thread/eventlog.ts +10 -6
  196. package/src/leader-thread/leader-worker-devtools.ts +6 -2
  197. package/src/leader-thread/make-leader-thread-layer.test.ts +44 -0
  198. package/src/leader-thread/make-leader-thread-layer.ts +137 -26
  199. package/src/leader-thread/materialize-event.ts +34 -9
  200. package/src/leader-thread/recreate-db.ts +11 -3
  201. package/src/leader-thread/shutdown-channel.ts +16 -2
  202. package/src/leader-thread/types.ts +7 -5
  203. package/src/materializer-helper.ts +22 -5
  204. package/src/rematerialize-from-eventlog.ts +33 -23
  205. package/src/schema/EventDef.ts +3 -0
  206. package/src/schema/LiveStoreEvent.ts +1 -2
  207. package/src/schema/mod.ts +2 -0
  208. package/src/schema/schema.ts +37 -1
  209. package/src/schema/state/sqlite/client-document-def.test.ts +17 -0
  210. package/src/schema/state/sqlite/client-document-def.ts +117 -5
  211. package/src/schema/state/sqlite/column-annotations.ts +16 -6
  212. package/src/schema/state/sqlite/column-def.test.ts +722 -0
  213. package/src/schema/state/sqlite/column-def.ts +215 -0
  214. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +26 -6
  215. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +2 -1
  216. package/src/schema/state/sqlite/mod.ts +1 -0
  217. package/src/schema/state/sqlite/query-builder/api.ts +7 -2
  218. package/src/schema/state/sqlite/query-builder/impl.test.ts +187 -6
  219. package/src/schema/state/sqlite/query-builder/impl.ts +8 -2
  220. package/src/schema/state/sqlite/system-tables.ts +2 -0
  221. package/src/schema/state/sqlite/table-def.test.ts +74 -569
  222. package/src/schema/state/sqlite/table-def.ts +13 -262
  223. package/src/schema/unknown-events.ts +131 -0
  224. package/src/sql-queries/sql-query-builder.ts +2 -1
  225. package/src/sync/ClientSessionSyncProcessor.ts +55 -49
  226. package/src/sync/errors.ts +38 -0
  227. package/src/sync/index.ts +3 -0
  228. package/src/sync/mock-sync-backend.ts +184 -0
  229. package/src/sync/next/compact-events.ts +4 -5
  230. package/src/sync/next/facts.ts +1 -3
  231. package/src/sync/next/history-dag-common.ts +272 -21
  232. package/src/sync/next/history-dag.ts +3 -1
  233. package/src/sync/sync-backend-kv.ts +22 -0
  234. package/src/sync/sync-backend.ts +185 -0
  235. package/src/sync/sync.ts +6 -89
  236. package/src/sync/transport-chunking.ts +90 -0
  237. package/src/sync/validate-push-payload.ts +6 -7
  238. package/src/testing/event-factory.ts +133 -0
  239. package/src/testing/mod.ts +1 -0
  240. package/src/version.ts +2 -2
  241. package/dist/schema-management/migrations.test.d.ts +0 -2
  242. package/dist/schema-management/migrations.test.d.ts.map +0 -1
  243. package/dist/schema-management/migrations.test.js +0 -52
  244. package/dist/schema-management/migrations.test.js.map +0 -1
  245. package/dist/sync/next/graphology.d.ts +0 -8
  246. package/dist/sync/next/graphology.d.ts.map +0 -1
  247. package/dist/sync/next/graphology.js +0 -30
  248. package/dist/sync/next/graphology.js.map +0 -1
  249. package/dist/sync/next/graphology_.d.ts +0 -3
  250. package/dist/sync/next/graphology_.d.ts.map +0 -1
  251. package/dist/sync/next/graphology_.js +0 -3
  252. package/dist/sync/next/graphology_.js.map +0 -1
  253. package/src/sync/next/ambient.d.ts +0 -3
  254. package/src/sync/next/graphology.ts +0 -41
  255. package/src/sync/next/graphology_.ts +0 -2
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Effect, Schema, Stream } from '@livestore/utils/effect';
1
+ import { Effect, Layer, Schema, Stream } from '@livestore/utils/effect';
2
2
  declare const UnexpectedError_base: Schema.TaggedErrorClass<UnexpectedError, "LiveStore.UnexpectedError", {
3
3
  readonly _tag: Schema.tag<"LiveStore.UnexpectedError">;
4
4
  } & {
@@ -8,14 +8,18 @@ declare const UnexpectedError_base: Schema.TaggedErrorClass<UnexpectedError, "Li
8
8
  }>;
9
9
  export declare class UnexpectedError extends UnexpectedError_base {
10
10
  static mapToUnexpectedError: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, UnexpectedError, R>;
11
+ static mapToUnexpectedErrorLayer: <A, E, R>(layer: Layer.Layer<A, E, R>) => Layer.Layer<A, UnexpectedError, R>;
11
12
  static mapToUnexpectedErrorStream: <A, E, R>(stream: Stream.Stream<A, E, R>) => Stream.Stream<A, UnexpectedError, R>;
12
13
  }
13
- declare const SyncError_base: Schema.TaggedErrorClass<SyncError, "LiveStore.SyncError", {
14
- readonly _tag: Schema.tag<"LiveStore.SyncError">;
14
+ declare const MaterializerHashMismatchError_base: Schema.TaggedErrorClass<MaterializerHashMismatchError, "LiveStore.MaterializerHashMismatchError", {
15
+ readonly _tag: Schema.tag<"LiveStore.MaterializerHashMismatchError">;
15
16
  } & {
16
- cause: typeof Schema.Defect;
17
+ eventName: typeof Schema.String;
18
+ note: Schema.optionalWith<typeof Schema.String, {
19
+ default: () => string;
20
+ }>;
17
21
  }>;
18
- export declare class SyncError extends SyncError_base {
22
+ export declare class MaterializerHashMismatchError extends MaterializerHashMismatchError_base {
19
23
  }
20
24
  declare const IntentionalShutdownCause_base: Schema.TaggedErrorClass<IntentionalShutdownCause, "LiveStore.IntentionalShutdownCause", {
21
25
  readonly _tag: Schema.tag<"LiveStore.IntentionalShutdownCause">;
@@ -46,5 +50,43 @@ declare const SqliteError_base: Schema.TaggedErrorClass<SqliteError, "LiveStore.
46
50
  }>;
47
51
  export declare class SqliteError extends SqliteError_base {
48
52
  }
53
+ declare const UnknownEventError_base: Schema.TaggedErrorClass<UnknownEventError, "LiveStore.UnknownEventError", {
54
+ readonly _tag: Schema.tag<"LiveStore.UnknownEventError">;
55
+ } & {
56
+ event: Schema.SchemaClass<{
57
+ readonly clientId: string;
58
+ readonly sessionId: string;
59
+ readonly name: string;
60
+ readonly seqNum: {
61
+ readonly global: number & import("effect/Brand").Brand<"GlobalEventSequenceNumber">;
62
+ readonly client: number & import("effect/Brand").Brand<"ClientEventSequenceNumber">;
63
+ readonly rebaseGeneration: number;
64
+ };
65
+ readonly args: any;
66
+ }, {
67
+ readonly clientId: string;
68
+ readonly sessionId: string;
69
+ readonly name: string;
70
+ readonly seqNum: {
71
+ readonly global: number;
72
+ readonly client: number;
73
+ readonly rebaseGeneration: number;
74
+ };
75
+ readonly args: any;
76
+ }, never>;
77
+ reason: Schema.Literal<["event-definition-missing", "materializer-missing"]>;
78
+ operation: typeof Schema.String;
79
+ note: Schema.optional<typeof Schema.String>;
80
+ }>;
81
+ export declare class UnknownEventError extends UnknownEventError_base {
82
+ }
83
+ declare const MaterializeError_base: Schema.TaggedErrorClass<MaterializeError, "LiveStore.MaterializeError", {
84
+ readonly _tag: Schema.tag<"LiveStore.MaterializeError">;
85
+ } & {
86
+ cause: Schema.Union<[typeof MaterializerHashMismatchError, typeof SqliteError, typeof UnknownEventError]>;
87
+ note: Schema.optional<typeof Schema.String>;
88
+ }>;
89
+ export declare class MaterializeError extends MaterializeError_base {
90
+ }
49
91
  export {};
50
92
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;;;;;;;;AAEhE,qBAAa,eAAgB,SAAQ,oBAInC;IACA,MAAM,CAAC,oBAAoB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0CAInE;IAEH,MAAM,CAAC,0BAA0B,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0CAGzE;CACJ;;;;;;AAED,qBAAa,SAAU,SAAQ,cAE7B;CAAG;;;;;;AAEL,qBAAa,wBAAyB,SAAQ,6BAK7C;CAAG;;;;;;AAEJ,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;;;;IASH,6BAA6B;;IAI7B,iCAAiC;;;;AAXnC,qBAAa,WAAY,SAAQ,gBAc/B;CAAG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;;;;;;;;AAI9E,qBAAa,eAAgB,SAAQ,oBAInC;IACA,MAAM,CAAC,oBAAoB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0CAInE;IAEH,MAAM,CAAC,yBAAyB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,wCAOrE;IAEH,MAAM,CAAC,0BAA0B,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0CAGzE;CACJ;;;;;;;;;AAED,qBAAa,6BAA8B,SAAQ,kCAQlD;CAAG;;;;;;AAEJ,qBAAa,wBAAyB,SAAQ,6BAK7C;CAAG;;;;;;AAEJ,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;;;;IASH,6BAA6B;;IAI7B,iCAAiC;;;;AAXnC,qBAAa,WAAY,SAAQ,gBAc/B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL,qBAAa,iBAAkB,SAAQ,sBAKrC;CAAG;;;;;;;AAEL,qBAAa,gBAAiB,SAAQ,qBAGpC;CAAG"}
package/dist/errors.js CHANGED
@@ -1,14 +1,21 @@
1
- import { Effect, Schema, Stream } from '@livestore/utils/effect';
1
+ import { Cause, Effect, Layer, Schema, Stream } from '@livestore/utils/effect';
2
+ import * as LiveStoreEvent from "./schema/LiveStoreEvent.js";
2
3
  export class UnexpectedError extends Schema.TaggedError()('LiveStore.UnexpectedError', {
3
4
  cause: Schema.Defect,
4
5
  note: Schema.optional(Schema.String),
5
6
  payload: Schema.optional(Schema.Any),
6
7
  }) {
7
8
  static mapToUnexpectedError = (effect) => effect.pipe(Effect.mapError((cause) => (Schema.is(UnexpectedError)(cause) ? cause : new UnexpectedError({ cause }))), Effect.catchAllDefect((cause) => new UnexpectedError({ cause })));
9
+ static mapToUnexpectedErrorLayer = (layer) => layer.pipe(Layer.catchAllCause((cause) => Cause.isFailType(cause) && Schema.is(UnexpectedError)(cause.error)
10
+ ? Layer.fail(cause.error)
11
+ : Layer.fail(new UnexpectedError({ cause: cause }))));
8
12
  static mapToUnexpectedErrorStream = (stream) => stream.pipe(Stream.mapError((cause) => (Schema.is(UnexpectedError)(cause) ? cause : new UnexpectedError({ cause }))));
9
13
  }
10
- export class SyncError extends Schema.TaggedError()('LiveStore.SyncError', {
11
- cause: Schema.Defect,
14
+ export class MaterializerHashMismatchError extends Schema.TaggedError()('LiveStore.MaterializerHashMismatchError', {
15
+ eventName: Schema.String,
16
+ note: Schema.optionalWith(Schema.String, {
17
+ default: () => 'Please make sure your event materializer is a pure function without side effects.',
18
+ }),
12
19
  }) {
13
20
  }
14
21
  export class IntentionalShutdownCause extends Schema.TaggedError()('LiveStore.IntentionalShutdownCause', {
@@ -33,4 +40,16 @@ export class SqliteError extends Schema.TaggedError()('LiveStore.SqliteError', {
33
40
  note: Schema.optional(Schema.String),
34
41
  }) {
35
42
  }
43
+ export class UnknownEventError extends Schema.TaggedError()('LiveStore.UnknownEventError', {
44
+ event: LiveStoreEvent.AnyEncoded.pipe(Schema.pick('name', 'args', 'seqNum', 'clientId', 'sessionId')),
45
+ reason: Schema.Literal('event-definition-missing', 'materializer-missing'),
46
+ operation: Schema.String,
47
+ note: Schema.optional(Schema.String),
48
+ }) {
49
+ }
50
+ export class MaterializeError extends Schema.TaggedError()('LiveStore.MaterializeError', {
51
+ cause: Schema.Union(MaterializerHashMismatchError, SqliteError, UnknownEventError),
52
+ note: Schema.optional(Schema.String),
53
+ }) {
54
+ }
36
55
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhE,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,WAAW,EAAmB,CAAC,2BAA2B,EAAE;IACtG,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;CACrC,CAAC;IACA,MAAM,CAAC,oBAAoB,GAAG,CAAU,MAA8B,EAAE,EAAE,CACxE,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EACxG,MAAM,CAAC,cAAc,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CACjE,CAAA;IAEH,MAAM,CAAC,0BAA0B,GAAG,CAAU,MAA8B,EAAE,EAAE,CAC9E,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CACzG,CAAA;;AAGL,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,WAAW,EAAa,CAAC,qBAAqB,EAAE;IACpF,KAAK,EAAE,MAAM,CAAC,MAAM;CACrB,CAAC;CAAG;AAEL,MAAM,OAAO,wBAAyB,SAAQ,MAAM,CAAC,WAAW,EAA4B,CAC1F,oCAAoC,EACpC;IACE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,QAAQ,CAAC;CACvF,CACF;CAAG;AAEJ,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,EAAoB,CAAC,4BAA4B,EAAE;IACzG,MAAM,EAAE,MAAM,CAAC,MAAM;CACtB,CAAC;CAAG;AAEL,MAAM,OAAO,WAAY,SAAQ,MAAM,CAAC,WAAW,EAAe,CAAC,uBAAuB,EAAE;IAC1F,KAAK,EAAE,MAAM,CAAC,QAAQ,CACpB,MAAM,CAAC,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC,MAAM;QAClB,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;KAC7G,CAAC,CACH;IACD,6BAA6B;IAC7B,wCAAwC;IACxC,8FAA8F;IAC9F,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACjE,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;CACrC,CAAC;CAAG"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import type { Scope } from '@livestore/utils/effect';
2
2
  import { Effect } from '@livestore/utils/effect';
3
- import type { SqliteDb } from '../adapter-types.ts';
4
- import { UnexpectedError } from '../adapter-types.ts';
3
+ import { type SqliteDb, UnexpectedError } from '../adapter-types.ts';
5
4
  import type { LiveStoreSchema } from '../schema/mod.ts';
6
5
  import * as SyncState from '../sync/syncstate.ts';
7
6
  import type { InitialBlockingSyncContext, LeaderSyncProcessor } from './types.ts';
@@ -35,7 +34,7 @@ import type { InitialBlockingSyncContext, LeaderSyncProcessor } from './types.ts
35
34
  *
36
35
  * See ClientSessionSyncProcessor for how the leader and session sync processors are similar/different.
37
36
  */
38
- export declare const makeLeaderSyncProcessor: ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, params, testing, }: {
37
+ export declare const makeLeaderSyncProcessor: ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, livePull, params, testing, }: {
39
38
  schema: LiveStoreSchema;
40
39
  dbState: SqliteDb;
41
40
  initialBlockingSyncContext: InitialBlockingSyncContext;
@@ -52,6 +51,11 @@ export declare const makeLeaderSyncProcessor: ({ schema, dbState, initialBlockin
52
51
  */
53
52
  backendPushBatchSize?: number;
54
53
  };
54
+ /**
55
+ * Whether the sync backend should reactively pull new events from the sync backend
56
+ * When `false`, the sync processor will only do an initial pull
57
+ */
58
+ livePull: boolean;
55
59
  testing: {
56
60
  delays?: {
57
61
  localPushProcessing?: Effect.Effect<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"LeaderSyncProcessor.d.ts","sourceRoot":"","sources":["../../src/leader-thread/LeaderSyncProcessor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,KAAK,EAAU,MAAM,yBAAyB,CAAA;AACjF,OAAO,EAGL,MAAM,EAWP,MAAM,yBAAyB,CAAA;AAGhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAa,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAEhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAGvD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAIjD,OAAO,KAAK,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAQjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,uBAAuB,GAAI,8FAQrC;IACD,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,QAAQ,CAAA;IACjB,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,sFAAsF;IACtF,gBAAgB,EAAE,SAAS,CAAC,SAAS,CAAA;IACrC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC9B,MAAM,EAAE;QACN;;WAEG;QACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;QAC3B;;WAEG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAC9B,CAAA;IACD,OAAO,EAAE;QACP,MAAM,CAAC,EAAE;YACP,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;SAC1C,CAAA;KACF,CAAA;CACF,KAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,eAAe,EAAE,KAAK,CAAC,KAAK,CA0O/D,CAAA"}
1
+ {"version":3,"file":"LeaderSyncProcessor.d.ts","sourceRoot":"","sources":["../../src/leader-thread/LeaderSyncProcessor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,KAAK,EAAU,MAAM,yBAAyB,CAAA;AACjF,OAAO,EAKL,MAAM,EAaP,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAGL,KAAK,QAAQ,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AASvD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAIjD,OAAO,KAAK,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAQjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,uBAAuB,GAAI,wGASrC;IACD,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,QAAQ,CAAA;IACjB,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,sFAAsF;IACtF,gBAAgB,EAAE,SAAS,CAAC,SAAS,CAAA;IACrC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC9B,MAAM,EAAE;QACN;;WAEG;QACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;QAC3B;;WAEG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAC9B,CAAA;IACD;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE;YACP,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;SAC1C,CAAA;KACF,CAAA;CACF,KAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,eAAe,EAAE,KAAK,CAAC,KAAK,CAuR/D,CAAA"}
@@ -1,9 +1,9 @@
1
1
  import { casesHandled, isNotUndefined, LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils';
2
- import { BucketQueue, Deferred, Effect, Exit, FiberHandle, Option, OtelTracer, pipe, Queue, ReadonlyArray, Stream, Subscribable, SubscriptionRef, } from '@livestore/utils/effect';
3
- import { SyncError, UnexpectedError } from "../adapter-types.js";
2
+ import { BucketQueue, Cause, Deferred, Duration, Effect, Exit, FiberHandle, Layer, Option, OtelTracer, pipe, Queue, ReadonlyArray, Schedule, Stream, Subscribable, SubscriptionRef, } from '@livestore/utils/effect';
3
+ import { UnexpectedError, } from "../adapter-types.js";
4
4
  import { makeMaterializerHash } from "../materializer-helper.js";
5
- import { EventSequenceNumber, getEventDef, LiveStoreEvent, SystemTables } from "../schema/mod.js";
6
- import { LeaderAheadError } from "../sync/sync.js";
5
+ import { EventSequenceNumber, LiveStoreEvent, resolveEventDef, SystemTables } from "../schema/mod.js";
6
+ import { LeaderAheadError, } from "../sync/sync.js";
7
7
  import * as SyncState from "../sync/syncstate.js";
8
8
  import { sql } from "../util.js";
9
9
  import * as Eventlog from "./eventlog.js";
@@ -39,15 +39,12 @@ import { LeaderThreadCtx } from "./types.js";
39
39
  *
40
40
  * See ClientSessionSyncProcessor for how the leader and session sync processors are similar/different.
41
41
  */
42
- export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, params, testing, }) => Effect.gen(function* () {
42
+ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, livePull, params, testing, }) => Effect.gen(function* () {
43
43
  const syncBackendPushQueue = yield* BucketQueue.make();
44
44
  const localPushBatchSize = params.localPushBatchSize ?? 1;
45
45
  const backendPushBatchSize = params.backendPushBatchSize ?? 2;
46
46
  const syncStateSref = yield* SubscriptionRef.make(undefined);
47
- const isClientEvent = (eventEncoded) => {
48
- const { eventDef } = getEventDef(schema, eventEncoded.name);
49
- return eventDef.options.clientOnly;
50
- };
47
+ const isClientEvent = (eventEncoded) => schema.eventsDefsMap.get(eventEncoded.name)?.options.clientOnly ?? false;
51
48
  const connectedClientSessionPullQueues = yield* makePullQueueSet;
52
49
  // This context depends on data from `boot`, we should find a better implementation to avoid this ref indirection.
53
50
  const ctxRef = {
@@ -98,13 +95,30 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
98
95
  const syncState = yield* syncStateSref;
99
96
  if (syncState === undefined)
100
97
  return shouldNeverHappen('Not initialized');
101
- const { eventDef } = getEventDef(schema, name);
98
+ const resolution = yield* resolveEventDef(schema, {
99
+ operation: '@livestore/common:LeaderSyncProcessor:pushPartial',
100
+ event: {
101
+ name,
102
+ args,
103
+ clientId,
104
+ sessionId,
105
+ seqNum: syncState.localHead,
106
+ },
107
+ }).pipe(UnexpectedError.mapToUnexpectedError);
108
+ if (resolution._tag === 'unknown') {
109
+ // Ignore partial pushes for unrecognised events – they are still
110
+ // persisted server-side once a schema update ships.
111
+ return;
112
+ }
102
113
  const eventEncoded = new LiveStoreEvent.EncodedWithMeta({
103
114
  name,
104
115
  args,
105
116
  clientId,
106
117
  sessionId,
107
- ...EventSequenceNumber.nextPair({ seqNum: syncState.localHead, isClient: eventDef.options.clientOnly }),
118
+ ...EventSequenceNumber.nextPair({
119
+ seqNum: syncState.localHead,
120
+ isClient: resolution.eventDef.options.clientOnly,
121
+ }),
108
122
  });
109
123
  yield* push([eventEncoded]);
110
124
  }).pipe(Effect.catchTag('LeaderAheadError', Effect.orDie));
@@ -127,18 +141,23 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
127
141
  const globalPendingEvents = initialSyncState.pending
128
142
  // Don't sync clientOnly events
129
143
  .filter((eventEncoded) => {
130
- const { eventDef } = getEventDef(schema, eventEncoded.name);
131
- return eventDef.options.clientOnly === false;
144
+ const eventDef = schema.eventsDefsMap.get(eventEncoded.name);
145
+ return eventDef === undefined ? true : eventDef.options.clientOnly === false;
132
146
  });
133
147
  if (globalPendingEvents.length > 0) {
134
148
  yield* BucketQueue.offerAll(syncBackendPushQueue, globalPendingEvents);
135
149
  }
136
150
  }
137
- const shutdownOnError = (cause) => Effect.gen(function* () {
138
- if (onError === 'shutdown') {
139
- yield* shutdownChannel.send(UnexpectedError.make({ cause }));
140
- yield* Effect.die(cause);
151
+ const maybeShutdownOnError = (cause) => Effect.gen(function* () {
152
+ if (onError === 'ignore') {
153
+ if (LS_DEV) {
154
+ yield* Effect.logDebug(`Ignoring sync error (${cause._tag === 'Fail' ? cause.error._tag : cause._tag})`, Cause.pretty(cause));
155
+ }
156
+ return;
141
157
  }
158
+ const errorToSend = Cause.isFailType(cause) ? cause.error : UnexpectedError.make({ cause });
159
+ yield* shutdownChannel.send(errorToSend).pipe(Effect.orDie);
160
+ return yield* Effect.die(cause);
142
161
  });
143
162
  yield* backgroundApplyLocalPushes({
144
163
  localPushesLatch,
@@ -154,17 +173,16 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
154
173
  testing: {
155
174
  delay: testing?.delays?.localPushProcessing,
156
175
  },
157
- }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError), Effect.forkScoped);
176
+ }).pipe(Effect.catchAllCause(maybeShutdownOnError), Effect.forkScoped);
158
177
  const backendPushingFiberHandle = yield* FiberHandle.make();
159
178
  const backendPushingEffect = backgroundBackendPushing({
160
179
  syncBackendPushQueue,
161
180
  otelSpan,
162
181
  devtoolsLatch: ctxRef.current?.devtoolsLatch,
163
182
  backendPushBatchSize,
164
- }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError));
183
+ }).pipe(Effect.catchAllCause(maybeShutdownOnError));
165
184
  yield* FiberHandle.run(backendPushingFiberHandle, backendPushingEffect);
166
185
  yield* backgroundBackendPulling({
167
- initialBackendHead: initialSyncState.upstreamHead.global,
168
186
  isClientEvent,
169
187
  restartBackendPushing: (filteredRebasedPending) => Effect.gen(function* () {
170
188
  // Stop current pushing fiber
@@ -178,13 +196,20 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
178
196
  syncStateSref,
179
197
  localPushesLatch,
180
198
  pullLatch,
199
+ livePull,
181
200
  dbState,
182
201
  otelSpan,
183
202
  initialBlockingSyncContext,
184
203
  devtoolsLatch: ctxRef.current?.devtoolsLatch,
185
204
  connectedClientSessionPullQueues,
186
205
  advancePushHead,
187
- }).pipe(Effect.tapCauseLogPretty, Effect.catchAllCause(shutdownOnError), Effect.forkScoped);
206
+ }).pipe(Effect.retry({
207
+ // We want to retry pulling if we've lost connection to the sync backend
208
+ while: (cause) => cause._tag === 'IsOfflineError',
209
+ }), Effect.catchAllCause(maybeShutdownOnError),
210
+ // Needed to avoid `Fiber terminated with an unhandled error` logs which seem to happen because of the `Effect.retry` above.
211
+ // This might be a bug in Effect. Only seems to happen in the browser.
212
+ Effect.provide(Layer.setUnhandledErrorLogLevel(Option.none())), Effect.forkScoped);
188
213
  return { initialLeaderHead: initialSyncState.localHead };
189
214
  }).pipe(Effect.withSpanScoped('@livestore/common:LeaderSyncProcessor:boot'));
190
215
  const pull = ({ cursor }) => Effect.gen(function* () {
@@ -243,7 +268,10 @@ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLa
243
268
  const currentRebaseGeneration = syncState.localHead.rebaseGeneration;
244
269
  // Since the rebase generation might have changed since enqueuing, we need to filter out items with older generation
245
270
  // It's important that we filter after we got localPushesLatch, otherwise we might filter with the old generation
246
- const [newEvents, deferreds] = pipe(batchItems, ReadonlyArray.filter(([eventEncoded]) => eventEncoded.seqNum.rebaseGeneration === currentRebaseGeneration), ReadonlyArray.unzip);
271
+ const [newEvents, deferreds] = pipe(batchItems, ReadonlyArray.filter(([eventEncoded]) =>
272
+ // Keep events that match the current generation or newer. Older generations will
273
+ // be rejected below when their sequence numbers no longer advance the local head.
274
+ eventEncoded.seqNum.rebaseGeneration >= currentRebaseGeneration), ReadonlyArray.unzip);
247
275
  if (newEvents.length === 0) {
248
276
  // console.log('dropping old-gen batch', currentLocalPushGenerationRef.current)
249
277
  // Allow the backend pulling to start
@@ -262,7 +290,7 @@ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLa
262
290
  batchSize: newEvents.length,
263
291
  newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
264
292
  });
265
- return yield* new SyncError({ cause: mergeResult.message });
293
+ return yield* new UnexpectedError({ cause: mergeResult.message });
266
294
  }
267
295
  case 'rebase': {
268
296
  return shouldNeverHappen('The leader thread should never have to rebase due to a local push');
@@ -314,8 +342,8 @@ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLa
314
342
  });
315
343
  // Don't sync clientOnly events
316
344
  const filteredBatch = mergeResult.newEvents.filter((eventEncoded) => {
317
- const { eventDef } = getEventDef(schema, eventEncoded.name);
318
- return eventDef.options.clientOnly === false;
345
+ const eventDef = schema.eventsDefsMap.get(eventEncoded.name);
346
+ return eventDef === undefined ? true : eventDef.options.clientOnly === false;
319
347
  });
320
348
  yield* BucketQueue.offerAll(syncBackendPushQueue, filteredBatch);
321
349
  yield* materializeEventsBatch({ batchItems: mergeResult.newEvents, deferreds });
@@ -348,12 +376,12 @@ const materializeEventsBatch = ({ batchItems, deferreds }) => Effect.gen(functio
348
376
  dbEventlog.execute('COMMIT', undefined); // Commit the transaction
349
377
  }).pipe(Effect.uninterruptible, Effect.scoped, Effect.withSpan('@livestore/common:LeaderSyncProcessor:materializeEventItems', {
350
378
  attributes: { batchSize: batchItems.length },
351
- }), Effect.tapCauseLogPretty, UnexpectedError.mapToUnexpectedError);
352
- const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBackendPushing, otelSpan, dbState, syncStateSref, localPushesLatch, pullLatch, devtoolsLatch, initialBlockingSyncContext, connectedClientSessionPullQueues, advancePushHead, }) => Effect.gen(function* () {
379
+ }), Effect.tapCauseLogPretty);
380
+ const backgroundBackendPulling = ({ isClientEvent, restartBackendPushing, otelSpan, dbState, syncStateSref, localPushesLatch, livePull, pullLatch, devtoolsLatch, initialBlockingSyncContext, connectedClientSessionPullQueues, advancePushHead, }) => Effect.gen(function* () {
353
381
  const { syncBackend, dbState: db, dbEventlog, schema } = yield* LeaderThreadCtx;
354
382
  if (syncBackend === undefined)
355
383
  return;
356
- const onNewPullChunk = (newEvents, remaining) => Effect.gen(function* () {
384
+ const onNewPullChunk = (newEvents, pageInfo) => Effect.gen(function* () {
357
385
  if (newEvents.length === 0)
358
386
  return;
359
387
  if (devtoolsLatch !== undefined) {
@@ -381,7 +409,7 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
381
409
  newEventsCount: newEvents.length,
382
410
  newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
383
411
  });
384
- return yield* new SyncError({ cause: mergeResult.message });
412
+ return yield* new UnexpectedError({ cause: mergeResult.message });
385
413
  }
386
414
  const newBackendHead = newEvents.at(-1).seqNum;
387
415
  Eventlog.updateBackendHead(dbEventlog, newBackendHead);
@@ -393,8 +421,8 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
393
421
  mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
394
422
  });
395
423
  const globalRebasedPendingEvents = mergeResult.newSyncState.pending.filter((event) => {
396
- const { eventDef } = getEventDef(schema, event.name);
397
- return eventDef.options.clientOnly === false;
424
+ const eventDef = schema.eventsDefsMap.get(event.name);
425
+ return eventDef === undefined ? true : eventDef.options.clientOnly === false;
398
426
  });
399
427
  yield* restartBackendPushing(globalRebasedPendingEvents);
400
428
  if (mergeResult.rollbackEvents.length > 0) {
@@ -414,6 +442,12 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
414
442
  newEventsCount: newEvents.length,
415
443
  mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
416
444
  });
445
+ // Ensure push fiber is active after advance by restarting with current pending (non-client) events
446
+ const globalPendingEvents = mergeResult.newSyncState.pending.filter((event) => {
447
+ const eventDef = schema.eventsDefsMap.get(event.name);
448
+ return eventDef === undefined ? true : eventDef.options.clientOnly === false;
449
+ });
450
+ yield* restartBackendPushing(globalPendingEvents);
417
451
  yield* connectedClientSessionPullQueues.offer({
418
452
  payload: SyncState.payloadFromMergeResult(mergeResult),
419
453
  leaderHead: mergeResult.newSyncState.localHead,
@@ -422,7 +456,7 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
422
456
  // `mergeResult.confirmedEvents` don't contain the correct sync metadata, so we need to use
423
457
  // `newEvents` instead which we filter via `mergeResult.confirmedEvents`
424
458
  const confirmedNewEvents = newEvents.filter((event) => mergeResult.confirmedEvents.some((confirmedEvent) => EventSequenceNumber.isEqual(event.seqNum, confirmedEvent.seqNum)));
425
- yield* Eventlog.updateSyncMetadata(confirmedNewEvents);
459
+ yield* Eventlog.updateSyncMetadata(confirmedNewEvents).pipe(UnexpectedError.mapToUnexpectedError);
426
460
  }
427
461
  }
428
462
  // Removes the changeset rows which are no longer needed as we'll never have to rollback beyond this point
@@ -431,15 +465,18 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
431
465
  yield* materializeEventsBatch({ batchItems: mergeResult.newEvents, deferreds: undefined });
432
466
  yield* SubscriptionRef.set(syncStateSref, mergeResult.newSyncState);
433
467
  // Allow local pushes to be processed again
434
- if (remaining === 0) {
468
+ if (pageInfo._tag === 'NoMore') {
435
469
  yield* localPushesLatch.open;
436
470
  }
437
471
  });
438
- const cursorInfo = yield* Eventlog.getSyncBackendCursorInfo({ remoteHead: initialBackendHead });
472
+ const syncState = yield* syncStateSref;
473
+ if (syncState === undefined)
474
+ return shouldNeverHappen('Not initialized');
475
+ const cursorInfo = yield* Eventlog.getSyncBackendCursorInfo({ remoteHead: syncState.upstreamHead.global });
439
476
  const hashMaterializerResult = makeMaterializerHash({ schema, dbState });
440
- yield* syncBackend.pull(cursorInfo).pipe(
477
+ yield* syncBackend.pull(cursorInfo, { live: livePull }).pipe(
441
478
  // TODO only take from queue while connected
442
- Stream.tap(({ batch, remaining }) => Effect.gen(function* () {
479
+ Stream.tap(({ batch, pageInfo }) => Effect.gen(function* () {
443
480
  // yield* Effect.spanEvent('batch', {
444
481
  // attributes: {
445
482
  // batchSize: batch.length,
@@ -456,9 +493,11 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
456
493
  // This is a bug and needs to be fixed https://github.com/livestorejs/livestore/issues/503#issuecomment-3114533165
457
494
  materializerHashLeader: hashMaterializerResult(LiveStoreEvent.encodedFromGlobal(_.eventEncoded)),
458
495
  materializerHashSession: Option.none(),
459
- })), remaining);
460
- yield* initialBlockingSyncContext.update({ processed: batch.length, remaining });
496
+ })), pageInfo);
497
+ yield* initialBlockingSyncContext.update({ processed: batch.length, pageInfo });
461
498
  })), Stream.runDrain, Effect.interruptible);
499
+ // Should only ever happen when livePull is false
500
+ yield* Effect.logDebug('backend-pulling finished', { livePull });
462
501
  }).pipe(Effect.withSpan('@livestore/common:LeaderSyncProcessor:backend-pulling'));
463
502
  const backgroundBackendPushing = ({ syncBackendPushQueue, otelSpan, devtoolsLatch, backendPushBatchSize, }) => Effect.gen(function* () {
464
503
  const { syncBackend } = yield* LeaderThreadCtx;
@@ -475,16 +514,39 @@ const backgroundBackendPushing = ({ syncBackendPushQueue, otelSpan, devtoolsLatc
475
514
  batchSize: queueItems.length,
476
515
  batch: TRACE_VERBOSE ? JSON.stringify(queueItems) : undefined,
477
516
  });
478
- // TODO handle push errors (should only happen during concurrent pull+push)
479
- const pushResult = yield* syncBackend.push(queueItems.map((_) => _.toGlobal())).pipe(Effect.either);
480
- if (pushResult._tag === 'Left') {
481
- if (LS_DEV) {
482
- yield* Effect.logDebug('handled backend-push-error', { error: pushResult.left.toString() });
517
+ // Push with declarative retry/backoff using Effect schedules
518
+ // - Exponential backoff starting at 1s and doubling (1s, 2s, 4s, 8s, 16s, 30s ...)
519
+ // - Delay clamped at 30s (continues retrying at 30s)
520
+ // - Resets automatically after successful push
521
+ // TODO(metrics): expose counters/gauges for retry attempts and queue health via devtools/metrics
522
+ // Only retry for transient UnexpectedError cases
523
+ const isRetryable = (err) => err._tag === 'InvalidPushError' && err.cause._tag === 'LiveStore.UnexpectedError';
524
+ // Input: InvalidPushError | IsOfflineError, Output: Duration
525
+ const retrySchedule = Schedule.exponential(Duration.seconds(1)).pipe(Schedule.andThenEither(Schedule.spaced(Duration.seconds(30))), // clamp at 30 second intervals
526
+ Schedule.compose(Schedule.elapsed), Schedule.whileInput(isRetryable));
527
+ yield* Effect.gen(function* () {
528
+ const iteration = yield* Schedule.CurrentIterationMetadata;
529
+ const pushResult = yield* syncBackend.push(queueItems.map((_) => _.toGlobal())).pipe(Effect.either);
530
+ const retries = iteration.recurrence;
531
+ if (retries > 0 && pushResult._tag === 'Right') {
532
+ otelSpan?.addEvent('backend-push-retry-success', { retries, batchSize: queueItems.length });
483
533
  }
484
- otelSpan?.addEvent('backend-push-error', { error: pushResult.left.toString() });
485
- // wait for interrupt caused by background pulling which will then restart pushing
486
- return yield* Effect.never;
487
- }
534
+ if (pushResult._tag === 'Left') {
535
+ otelSpan?.addEvent('backend-push-error', {
536
+ error: pushResult.left.toString(),
537
+ retries,
538
+ batchSize: queueItems.length,
539
+ });
540
+ const error = pushResult.left;
541
+ if (error._tag === 'IsOfflineError' ||
542
+ (error._tag === 'InvalidPushError' && error.cause._tag === 'ServerAheadError')) {
543
+ // It's a core part of the sync protocol that the sync backend will emit a new pull chunk alongside the ServerAheadError
544
+ yield* Effect.logDebug('handled backend-push-error (waiting for interupt caused by pull)', { error });
545
+ return yield* Effect.never;
546
+ }
547
+ return yield* error;
548
+ }
549
+ }).pipe(Effect.retry(retrySchedule));
488
550
  }
489
551
  }).pipe(Effect.interruptible, Effect.withSpan('@livestore/common:LeaderSyncProcessor:backend-pushing'));
490
552
  const trimChangesetRows = (db, newHead) => {
@@ -575,14 +637,25 @@ const makePullQueueSet = Effect.gen(function* () {
575
637
  offer,
576
638
  };
577
639
  });
640
+ /**
641
+ * Validate a client-provided batch before it is admitted to the leader queue.
642
+ * Ensures the numbers form a strictly increasing chain and that the first
643
+ * event sits ahead of the current push head.
644
+ */
578
645
  const validatePushBatch = (batch, pushHead) => Effect.gen(function* () {
579
646
  if (batch.length === 0) {
580
647
  return;
581
648
  }
582
- // Make sure batch is monotonically increasing
649
+ // Example: session A already enqueued e1…e6 while session B (same client, different
650
+ // session) still believes the head is e1 and submits [e2, e7, e8]. The numbers look
651
+ // monotonic from B’s perspective, but we must reject and force B to rebase locally
652
+ // so the leader never regresses.
583
653
  for (let i = 1; i < batch.length; i++) {
584
654
  if (EventSequenceNumber.isGreaterThanOrEqual(batch[i - 1].seqNum, batch[i].seqNum)) {
585
- shouldNeverHappen(`Events must be ordered in monotonically ascending order by eventNum. Received: [${batch.map((e) => EventSequenceNumber.toString(e.seqNum)).join(', ')}]`);
655
+ return yield* LeaderAheadError.make({
656
+ minimumExpectedNum: batch[i - 1].seqNum,
657
+ providedNum: batch[i].seqNum,
658
+ });
586
659
  }
587
660
  }
588
661
  // Make sure smallest sequence number is > pushHead