@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
@@ -1,13 +1,16 @@
1
- import { type Nullable, shouldNeverHappen, type Writeable } from '@livestore/utils'
1
+ import { type Nullable, shouldNeverHappen } from '@livestore/utils'
2
2
  import { Option, Schema, SchemaAST, type Types } from '@livestore/utils/effect'
3
3
 
4
- import { AutoIncrement, ColumnType, Default, PrimaryKeyId, Unique } from './column-annotations.ts'
4
+ import { getColumnDefForSchema, schemaFieldsToColumns } from './column-def.ts'
5
5
  import { SqliteDsl } from './db-schema/mod.ts'
6
6
  import type { QueryBuilder } from './query-builder/mod.ts'
7
7
  import { makeQueryBuilder, QueryBuilderAstSymbol, QueryBuilderTypeId } from './query-builder/mod.ts'
8
8
 
9
9
  export const { blob, boolean, column, datetime, integer, isColumnDefinition, json, real, text } = SqliteDsl
10
10
 
11
+ // Re-export the column definition function
12
+ export { getColumnDefForSchema }
13
+
11
14
  export type StateType = 'singleton' | 'dynamic'
12
15
 
13
16
  export type DefaultSqliteTableDef = SqliteDsl.TableDefinition<string, SqliteDsl.Columns>
@@ -218,7 +221,7 @@ export function table<
218
221
  ) as SqliteDsl.Columns
219
222
  additionalIndexes = []
220
223
  } else if ('schema' in args) {
221
- const result = schemaFieldsToColumns(SchemaAST.getPropertySignatures(args.schema.ast))
224
+ const result = schemaFieldsToColumns(Schema.getResolvedPropertySignatures(args.schema))
222
225
  columns = result.columns
223
226
 
224
227
  // We'll set tableName first, then use it for index names
@@ -378,12 +381,13 @@ export declare namespace SchemaToColumns {
378
381
  export type ColumnDefForType<TEncoded, TType> = SqliteDsl.ColumnDefinition<TEncoded, TType>
379
382
 
380
383
  // Create columns type from schema Type and Encoded
381
- export type FromTypes<TType, TEncoded> = TType extends Record<string, any>
382
- ? TEncoded extends Record<string, any>
383
- ? {
384
- [K in keyof TType & keyof TEncoded]: ColumnDefForType<TEncoded[K], TType[K]>
385
- }
386
- : SqliteDsl.Columns
384
+ export type FromTypes<TType, TEncoded> = TEncoded extends Record<string, any>
385
+ ? {
386
+ [K in keyof TEncoded]-?: ColumnDefForType<
387
+ TEncoded[K],
388
+ TType extends Record<string, any> ? (K extends keyof TType ? TType[K] : TEncoded[K]) : TEncoded[K]
389
+ >
390
+ }
387
391
  : SqliteDsl.Columns
388
392
  }
389
393
 
@@ -398,256 +402,3 @@ export declare namespace TableDefInput {
398
402
  SchemaToColumns.FromTypes<TType, TEncoded>
399
403
  >
400
404
  }
401
-
402
- /**
403
- * Checks if a property signature has a specific annotation, checking both
404
- * the property signature itself and its type AST.
405
- */
406
- const hasPropertyAnnotation = <T>(
407
- propertySignature: SchemaAST.PropertySignature,
408
- annotationId: symbol,
409
- ): Option.Option<T> => {
410
- // When using Schema.optional(Schema.String).pipe(withPrimaryKey) in a struct,
411
- // the annotation ends up on a PropertySignatureDeclaration, not the Union type
412
- // Check if this is a PropertySignatureDeclaration with annotations
413
- if ('annotations' in propertySignature && propertySignature.annotations) {
414
- const annotation = SchemaAST.getAnnotation<T>(annotationId)(propertySignature as any)
415
- if (Option.isSome(annotation)) {
416
- return annotation
417
- }
418
- }
419
-
420
- // Otherwise check the type AST
421
- return SchemaAST.getAnnotation<T>(annotationId)(propertySignature.type)
422
- }
423
-
424
- /**
425
- * Maps schema property signatures to SQLite column definitions.
426
- * Returns both columns and unique column names for index creation.
427
- */
428
- const schemaFieldsToColumns = (
429
- propertySignatures: ReadonlyArray<SchemaAST.PropertySignature>,
430
- ): { columns: SqliteDsl.Columns; uniqueColumns: string[] } => {
431
- const columns: SqliteDsl.Columns = {}
432
- const uniqueColumns: string[] = []
433
-
434
- for (const prop of propertySignatures) {
435
- if (typeof prop.name === 'string') {
436
- // Create a schema from the AST
437
- const fieldSchema = Schema.make(prop.type)
438
- // Check if property has primary key annotation
439
- const hasPrimaryKey = hasPropertyAnnotation<boolean>(prop, PrimaryKeyId).pipe(Option.getOrElse(() => false))
440
- // Check if property has unique annotation
441
- const hasUnique = hasPropertyAnnotation<boolean>(prop, Unique).pipe(Option.getOrElse(() => false))
442
-
443
- columns[prop.name] = schemaFieldToColumn(fieldSchema, prop, hasPrimaryKey)
444
-
445
- if (hasUnique) {
446
- uniqueColumns.push(prop.name)
447
- }
448
- }
449
- }
450
-
451
- return { columns, uniqueColumns }
452
- }
453
-
454
- /**
455
- * Converts a schema field and its property signature to a SQLite column definition.
456
- */
457
- const schemaFieldToColumn = (
458
- fieldSchema: Schema.Schema.AnyNoContext,
459
- propertySignature: SchemaAST.PropertySignature,
460
- forceHasPrimaryKey?: boolean,
461
- ): SqliteDsl.ColumnDefinition.Any => {
462
- // Determine column type based on schema type
463
- const columnDef = getColumnDefForSchema(fieldSchema, propertySignature)
464
-
465
- // Create a new object with appropriate properties
466
- const result: Writeable<SqliteDsl.ColumnDefinition.Any> = {
467
- columnType: columnDef.columnType,
468
- schema: columnDef.schema,
469
- default: columnDef.default,
470
- nullable: columnDef.nullable,
471
- primaryKey: columnDef.primaryKey,
472
- autoIncrement: columnDef.autoIncrement,
473
- }
474
-
475
- // Set primaryKey property explicitly
476
- if (forceHasPrimaryKey || columnDef.primaryKey) {
477
- result.primaryKey = true
478
- } else {
479
- result.primaryKey = false
480
- }
481
-
482
- // Check for invalid primary key + nullable combination
483
- if (result.primaryKey && (propertySignature.isOptional || columnDef.nullable)) {
484
- return shouldNeverHappen(
485
- `Primary key columns cannot be nullable. Found nullable primary key for column. ` +
486
- `Either remove the primary key annotation or use a non-nullable schema.`,
487
- )
488
- }
489
-
490
- // Set nullable property explicitly
491
- if (propertySignature.isOptional) {
492
- result.nullable = true
493
- } else if (columnDef.nullable) {
494
- result.nullable = true
495
- } else {
496
- result.nullable = false
497
- }
498
-
499
- // Only add autoIncrement if it's true
500
- if (columnDef.autoIncrement) {
501
- result.autoIncrement = true
502
- }
503
-
504
- return result as SqliteDsl.ColumnDefinition.Any
505
- }
506
-
507
- /**
508
- * Maps a schema to a SQLite column definition, respecting column annotations.
509
- */
510
- export const getColumnDefForSchema = (
511
- schema: Schema.Schema.AnyNoContext,
512
- propertySignature?: SchemaAST.PropertySignature,
513
- ): SqliteDsl.ColumnDefinition.Any => {
514
- const ast = schema.ast
515
-
516
- // Check for annotations
517
- const hasPrimaryKey = propertySignature
518
- ? hasPropertyAnnotation<boolean>(propertySignature, PrimaryKeyId).pipe(Option.getOrElse(() => false))
519
- : SchemaAST.getAnnotation<boolean>(PrimaryKeyId)(ast).pipe(Option.getOrElse(() => false))
520
-
521
- const hasAutoIncrement = propertySignature
522
- ? hasPropertyAnnotation<boolean>(propertySignature, AutoIncrement).pipe(Option.getOrElse(() => false))
523
- : SchemaAST.getAnnotation<boolean>(AutoIncrement)(ast).pipe(Option.getOrElse(() => false))
524
-
525
- const defaultValue = propertySignature
526
- ? hasPropertyAnnotation<unknown>(propertySignature, Default)
527
- : SchemaAST.getAnnotation<unknown>(Default)(ast)
528
-
529
- /** Adds annotations to a column definition if they are present. */
530
- const withAnnotationsIfNeeded = (columnDef: SqliteDsl.ColumnDefinition.Any): SqliteDsl.ColumnDefinition.Any => {
531
- const result = { ...columnDef }
532
-
533
- if (hasPrimaryKey) {
534
- result.primaryKey = true
535
- }
536
-
537
- if (hasAutoIncrement) {
538
- result.autoIncrement = true
539
- }
540
-
541
- if (Option.isSome(defaultValue)) {
542
- result.default = Option.some(defaultValue.value)
543
- }
544
-
545
- return result
546
- }
547
-
548
- // Check for custom column type annotation
549
- const columnTypeAnnotation = SchemaAST.getAnnotation<SqliteDsl.FieldColumnType>(ColumnType)(ast)
550
- if (Option.isSome(columnTypeAnnotation)) {
551
- const columnType = columnTypeAnnotation.value
552
- let columnDef: SqliteDsl.ColumnDefinition.Any
553
- switch (columnType) {
554
- case 'text':
555
- columnDef = SqliteDsl.text()
556
- break
557
- case 'integer':
558
- columnDef = SqliteDsl.integer()
559
- break
560
- case 'real':
561
- columnDef = SqliteDsl.real()
562
- break
563
- case 'blob':
564
- columnDef = SqliteDsl.blob()
565
- break
566
- default:
567
- return shouldNeverHappen(`Unsupported column type annotation: ${columnType}`)
568
- }
569
-
570
- return withAnnotationsIfNeeded(columnDef)
571
- }
572
-
573
- // Check for refinements (e.g., Schema.Int)
574
- if (SchemaAST.isRefinement(ast)) {
575
- // Check if this is specifically Schema.Int by looking at the identifier annotation
576
- const identifier = SchemaAST.getIdentifierAnnotation(ast).pipe(Option.getOrElse(() => ''))
577
- if (identifier === 'Int') {
578
- return withAnnotationsIfNeeded(SqliteDsl.integer())
579
- }
580
- // For other refinements, check the underlying type
581
- return getColumnDefForSchema(Schema.make(ast.from), propertySignature)
582
- }
583
-
584
- // Check for string types
585
- if (SchemaAST.isStringKeyword(ast)) {
586
- return withAnnotationsIfNeeded(SqliteDsl.text())
587
- }
588
-
589
- // Check for number types
590
- if (SchemaAST.isNumberKeyword(ast)) {
591
- return withAnnotationsIfNeeded(SqliteDsl.real())
592
- }
593
-
594
- // Check for boolean types
595
- if (SchemaAST.isBooleanKeyword(ast)) {
596
- return withAnnotationsIfNeeded(SqliteDsl.boolean())
597
- }
598
-
599
- // Check for unions (like optional or nullable)
600
- if (SchemaAST.isUnion(ast)) {
601
- // Check if this union contains null or undefined (making it nullable/optional)
602
- let hasNull = false
603
- let hasUndefined = false
604
- let nonNullableType: SchemaAST.AST | undefined
605
-
606
- for (const type of ast.types) {
607
- if (SchemaAST.isUndefinedKeyword(type)) {
608
- hasUndefined = true
609
- } else if (SchemaAST.isLiteral(type) && type.literal === null) {
610
- hasNull = true
611
- } else {
612
- nonNullableType = type
613
- }
614
- }
615
-
616
- // If we found a non-nullable type, use it for the column definition
617
- if (nonNullableType) {
618
- const innerSchema = Schema.make(nonNullableType)
619
- const innerColumnDef = getColumnDefForSchema(innerSchema, propertySignature)
620
-
621
- // If the union contains null or undefined, mark as nullable
622
- if (hasNull || hasUndefined) {
623
- return withAnnotationsIfNeeded({
624
- ...innerColumnDef,
625
- nullable: true,
626
- })
627
- }
628
-
629
- return withAnnotationsIfNeeded(innerColumnDef)
630
- }
631
- }
632
-
633
- // Check for Date types
634
- if (SchemaAST.isTransformation(ast)) {
635
- // Try to map the transformation's target type
636
- return getColumnDefForSchema(Schema.make(ast.to), propertySignature)
637
- }
638
-
639
- // Check for literal types
640
- if (SchemaAST.isLiteral(ast)) {
641
- const value = ast.literal
642
- if (typeof value === 'string') {
643
- return withAnnotationsIfNeeded(SqliteDsl.text())
644
- } else if (typeof value === 'number') {
645
- return withAnnotationsIfNeeded(SqliteDsl.real())
646
- } else if (typeof value === 'boolean') {
647
- return withAnnotationsIfNeeded(SqliteDsl.boolean())
648
- }
649
- }
650
-
651
- // Default to JSON column for complex types
652
- return withAnnotationsIfNeeded(SqliteDsl.json({ schema }))
653
- }
@@ -0,0 +1,131 @@
1
+ import { Effect } from '@livestore/utils/effect'
2
+
3
+ import { UnknownEventError } from '../errors.ts'
4
+ import type { EventDef, Materializer } from './EventDef.ts'
5
+ import type * as LiveStoreEvent from './LiveStoreEvent.ts'
6
+ import type { LiveStoreSchema } from './schema.ts'
7
+
8
+ export type UnknownEventContext = {
9
+ readonly event: Pick<LiveStoreEvent.AnyEncoded, 'name' | 'args' | 'seqNum' | 'clientId' | 'sessionId'>
10
+ readonly reason: 'event-definition-missing' | 'materializer-missing'
11
+ readonly operation: string
12
+ }
13
+
14
+ export namespace UnknownEvents {
15
+ export type HandlingStrategy = 'warn' | 'fail' | 'ignore' | 'callback'
16
+
17
+ export type Callback = (
18
+ context: UnknownEventContext,
19
+ error: UnknownEventError,
20
+ ) => Effect.SyncOrPromiseOrEffect<void, unknown, never>
21
+
22
+ export type HandlingConfig =
23
+ | { readonly strategy: 'warn' }
24
+ | { readonly strategy: 'ignore' }
25
+ | { readonly strategy: 'fail' }
26
+ | { readonly strategy: 'callback'; readonly onUnknownEvent: Callback }
27
+
28
+ export type Reason = UnknownEventContext['reason']
29
+
30
+ export type ResolveContext = Omit<UnknownEventContext, 'reason'>
31
+
32
+ export type Resolved =
33
+ | {
34
+ readonly _tag: 'known'
35
+ readonly eventDef: EventDef.AnyWithoutFn
36
+ readonly materializer: Materializer
37
+ }
38
+ | {
39
+ readonly _tag: 'unknown'
40
+ readonly reason: Reason
41
+ }
42
+ }
43
+
44
+ const DEFAULT_UNKNOWN_EVENT_HANDLING: UnknownEvents.HandlingConfig = { strategy: 'warn' }
45
+
46
+ export const normalizeUnknownEventHandling = (
47
+ input: UnknownEvents.HandlingConfig | undefined,
48
+ ): UnknownEvents.HandlingConfig => input ?? DEFAULT_UNKNOWN_EVENT_HANDLING
49
+
50
+ const handleUnknownEvent = ({
51
+ schema,
52
+ context,
53
+ }: {
54
+ schema: LiveStoreSchema
55
+ context: UnknownEventContext
56
+ }): Effect.Effect<void, UnknownEventError> =>
57
+ Effect.gen(function* () {
58
+ const config = schema.unknownEventHandling
59
+ const error = new UnknownEventError(context)
60
+
61
+ switch (config.strategy) {
62
+ case 'fail': {
63
+ return yield* Effect.fail(error)
64
+ }
65
+ case 'warn': {
66
+ yield* Effect.logWarning('@livestore/common:schema:unknown-event', context)
67
+ return
68
+ }
69
+ case 'ignore': {
70
+ return
71
+ }
72
+ case 'callback': {
73
+ const callback = config.onUnknownEvent
74
+
75
+ yield* Effect.tryAll<void>(() => callback(context, error)).pipe(
76
+ Effect.catchAll((cause) =>
77
+ Effect.logWarning('@livestore/common:schema:unknown-event:callback-error', {
78
+ event: context.event,
79
+ reason: context.reason,
80
+ operation: context.operation,
81
+ cause,
82
+ }),
83
+ ),
84
+ )
85
+ return
86
+ }
87
+ }
88
+ })
89
+
90
+ /**
91
+ * Resolves the runtime event definition + materializer for a given event name.
92
+ *
93
+ * Behaviour is intentionally split across the result and error channels:
94
+ * - For `'fail'` handling, we surface an `UnknownEventError` via the failure channel so
95
+ * callers can convert it into the appropriate domain error (for example `MaterializeError`).
96
+ * - For all other strategies (`warn`, `ignore`, `callback`) we succeed with an
97
+ * `{ _tag: 'unknown' }` value, signalling that the caller should skip the event while
98
+ * continuing normal processing.
99
+ */
100
+ export const resolveEventDef = (
101
+ schema: LiveStoreSchema,
102
+ context: UnknownEvents.ResolveContext,
103
+ ): Effect.Effect<UnknownEvents.Resolved, UnknownEventError> =>
104
+ Effect.gen(function* () {
105
+ const eventName = context.event.name
106
+ const eventDef = schema.eventsDefsMap.get(eventName)
107
+ if (eventDef === undefined) {
108
+ yield* handleUnknownEvent({
109
+ schema,
110
+ context: {
111
+ event: context.event,
112
+ reason: 'event-definition-missing',
113
+ operation: context.operation,
114
+ },
115
+ })
116
+ return { _tag: 'unknown', reason: 'event-definition-missing' }
117
+ }
118
+ const materializer = schema.state.materializers.get(eventName)
119
+ if (materializer === undefined) {
120
+ yield* handleUnknownEvent({
121
+ schema,
122
+ context: {
123
+ event: context.event,
124
+ reason: 'materializer-missing',
125
+ operation: context.operation,
126
+ },
127
+ })
128
+ return { _tag: 'unknown', reason: 'materializer-missing' }
129
+ }
130
+ return { _tag: 'known', eventDef, materializer }
131
+ })
@@ -1,3 +1,4 @@
1
+ import { omitUndefineds } from '@livestore/utils'
1
2
  import type { SqliteDsl } from '../schema/state/sqlite/db-schema/mod.ts'
2
3
  import type { BindValues } from './sql-queries.ts'
3
4
  import * as SqlQueries from './sql-queries.ts'
@@ -16,7 +17,7 @@ export const makeSqlQueryBuilder = <TSchema extends SqliteDsl.DbSchema>(schema:
16
17
  limit?: number
17
18
  }): [string, BindValues, TTableName] => {
18
19
  const columns = schema[tableName]!.columns
19
- const [stmt, bindValues] = SqlQueries.findManyRows({ columns, tableName, where, limit })
20
+ const [stmt, bindValues] = SqlQueries.findManyRows({ columns, tableName, where, ...omitUndefineds({ limit }) })
20
21
  return [stmt, bindValues, tableName]
21
22
  }
22
23
 
@@ -13,12 +13,13 @@ import {
13
13
  Stream,
14
14
  Subscribable,
15
15
  } from '@livestore/utils/effect'
16
- import * as otel from '@opentelemetry/api'
16
+ import type * as otel from '@opentelemetry/api'
17
17
 
18
- import { type ClientSession, SyncError, type UnexpectedError } from '../adapter-types.ts'
18
+ import { type ClientSession, UnexpectedError } from '../adapter-types.ts'
19
+ import type { MaterializeError } from '../errors.ts'
19
20
  import * as EventSequenceNumber from '../schema/EventSequenceNumber.ts'
20
21
  import * as LiveStoreEvent from '../schema/LiveStoreEvent.ts'
21
- import { getEventDef, type LiveStoreSchema } from '../schema/mod.ts'
22
+ import type { LiveStoreSchema } from '../schema/mod.ts'
22
23
  import * as SyncState from './syncstate.ts'
23
24
 
24
25
  /**
@@ -51,16 +52,19 @@ export const makeClientSessionSyncProcessor = ({
51
52
  clientSession: ClientSession
52
53
  runtime: Runtime.Runtime<Scope.Scope>
53
54
  materializeEvent: (
54
- eventDecoded: LiveStoreEvent.AnyDecoded,
55
- options: { otelContext: otel.Context; withChangeset: boolean; materializerHashLeader: Option.Option<number> },
56
- ) => {
57
- writeTables: Set<string>
58
- sessionChangeset:
59
- | { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
60
- | { _tag: 'no-op' }
61
- | { _tag: 'unset' }
62
- materializerHash: Option.Option<number>
63
- }
55
+ eventEncoded: LiveStoreEvent.EncodedWithMeta,
56
+ options: { withChangeset: boolean; materializerHashLeader: Option.Option<number> },
57
+ ) => Effect.Effect<
58
+ {
59
+ writeTables: Set<string>
60
+ sessionChangeset:
61
+ | { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
62
+ | { _tag: 'no-op' }
63
+ | { _tag: 'unset' }
64
+ materializerHash: Option.Option<number>
65
+ },
66
+ MaterializeError
67
+ >
64
68
  rollback: (changeset: Uint8Array<ArrayBuffer>) => void
65
69
  refreshTables: (tables: Set<string>) => void
66
70
  span: otel.Span
@@ -91,23 +95,26 @@ export const makeClientSessionSyncProcessor = ({
91
95
  }),
92
96
  }
93
97
 
94
- /** Only used for debugging / observability, it's not relied upon for correctness of the sync processor. */
98
+ /** Only used for debugging / observability / testing, it's not relied upon for correctness of the sync processor. */
95
99
  const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
96
100
  const isClientEvent = (eventEncoded: LiveStoreEvent.EncodedWithMeta) =>
97
- getEventDef(schema, eventEncoded.name).eventDef.options.clientOnly
101
+ schema.eventsDefsMap.get(eventEncoded.name)?.options.clientOnly ?? false
98
102
 
99
103
  /** We're queuing push requests to reduce the number of messages sent to the leader by batching them */
100
104
  const leaderPushQueue = BucketQueue.make<LiveStoreEvent.EncodedWithMeta>().pipe(Effect.runSync)
101
105
 
102
- const push: ClientSessionSyncProcessor['push'] = (batch, { otelContext }) => {
106
+ const push: ClientSessionSyncProcessor['push'] = Effect.fn('client-session-sync-processor:push')(function* (batch) {
103
107
  // TODO validate batch
104
108
 
105
109
  let baseEventSequenceNumber = syncStateRef.current.localHead
106
110
  const encodedEventDefs = batch.map(({ name, args }) => {
107
- const eventDef = getEventDef(schema, name)
111
+ const eventDef = schema.eventsDefsMap.get(name)
112
+ if (eventDef === undefined) {
113
+ return shouldNeverHappen(`No event definition found for \`${name}\`.`)
114
+ }
108
115
  const nextNumPair = EventSequenceNumber.nextPair({
109
116
  seqNum: baseEventSequenceNumber,
110
- isClient: eventDef.eventDef.options.clientOnly,
117
+ isClient: eventDef.options.clientOnly,
111
118
  })
112
119
  baseEventSequenceNumber = nextNumPair.seqNum
113
120
  return new LiveStoreEvent.EncodedWithMeta(
@@ -128,33 +135,35 @@ export const makeClientSessionSyncProcessor = ({
128
135
  isEqualEvent: LiveStoreEvent.isEqualEncoded,
129
136
  })
130
137
 
138
+ yield* Effect.annotateCurrentSpan({
139
+ batchSize: encodedEventDefs.length,
140
+ mergeResultTag: mergeResult._tag,
141
+ eventCounts: encodedEventDefs.reduce<Record<string, number>>((acc, event) => {
142
+ acc[event.name] = (acc[event.name] ?? 0) + 1
143
+ return acc
144
+ }, {}),
145
+ ...(TRACE_VERBOSE && { mergeResult: JSON.stringify(mergeResult) }),
146
+ })
147
+
131
148
  if (mergeResult._tag === 'unexpected-error') {
132
149
  return shouldNeverHappen('Unexpected error in client-session-sync-processor', mergeResult.message)
133
150
  }
134
151
 
135
- span.addEvent('local-push', {
136
- batchSize: encodedEventDefs.length,
137
- mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
138
- })
139
-
140
152
  if (mergeResult._tag !== 'advance') {
141
153
  return shouldNeverHappen(`Expected advance, got ${mergeResult._tag}`)
142
154
  }
143
155
 
144
156
  syncStateRef.current = mergeResult.newSyncState
145
- syncStateUpdateQueue.offer(mergeResult.newSyncState).pipe(Effect.runSync)
157
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
146
158
 
147
159
  // Materialize events to state
148
160
  const writeTables = new Set<string>()
149
161
  for (const event of mergeResult.newEvents) {
150
- // TODO avoid encoding and decoding here again
151
- const decodedEventDef = Schema.decodeSync(eventSchema)(event)
152
162
  const {
153
163
  writeTables: newWriteTables,
154
164
  sessionChangeset,
155
165
  materializerHash,
156
- } = materializeEvent(decodedEventDef, {
157
- otelContext,
166
+ } = yield* materializeEvent(event, {
158
167
  withChangeset: true,
159
168
  materializerHashLeader: Option.none(),
160
169
  })
@@ -167,10 +176,10 @@ export const makeClientSessionSyncProcessor = ({
167
176
 
168
177
  // Trigger push to leader
169
178
  // console.debug('pushToLeader', encodedEventDefs.length, ...encodedEventDefs.map((_) => _.toJSON()))
170
- BucketQueue.offerAll(leaderPushQueue, encodedEventDefs).pipe(Effect.runSync)
179
+ yield* BucketQueue.offerAll(leaderPushQueue, encodedEventDefs)
171
180
 
172
181
  return { writeTables }
173
- }
182
+ })
174
183
 
175
184
  const debugInfo = {
176
185
  rebaseCount: 0,
@@ -178,8 +187,6 @@ export const makeClientSessionSyncProcessor = ({
178
187
  rejectCount: 0,
179
188
  }
180
189
 
181
- const otelContext = otel.trace.setSpan(otel.context.active(), span)
182
-
183
190
  const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
184
191
  if (confirmUnsavedChanges && typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
185
192
  const onBeforeUnload = (event: BeforeUnloadEvent) => {
@@ -229,13 +236,12 @@ export const makeClientSessionSyncProcessor = ({
229
236
  })
230
237
 
231
238
  if (mergeResult._tag === 'unexpected-error') {
232
- return yield* new SyncError({ cause: mergeResult.message })
239
+ return yield* new UnexpectedError({ cause: mergeResult.message })
233
240
  } else if (mergeResult._tag === 'reject') {
234
241
  return shouldNeverHappen('Unexpected reject in client-session-sync-processor', mergeResult)
235
242
  }
236
243
 
237
244
  syncStateRef.current = mergeResult.newSyncState
238
- yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
239
245
 
240
246
  if (mergeResult._tag === 'rebase') {
241
247
  span.addEvent('merge:pull:rebase', {
@@ -244,7 +250,6 @@ export const makeClientSessionSyncProcessor = ({
244
250
  newEventsCount: mergeResult.newEvents.length,
245
251
  rollbackCount: mergeResult.rollbackEvents.length,
246
252
  res: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
247
- rebaseGeneration: mergeResult.newSyncState.localHead.rebaseGeneration,
248
253
  })
249
254
 
250
255
  debugInfo.rebaseCount++
@@ -294,18 +299,19 @@ export const makeClientSessionSyncProcessor = ({
294
299
  debugInfo.advanceCount++
295
300
  }
296
301
 
297
- if (mergeResult.newEvents.length === 0) return
302
+ if (mergeResult.newEvents.length === 0) {
303
+ // If there are no new events, we need to update the sync state as well
304
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
305
+ return
306
+ }
298
307
 
299
308
  const writeTables = new Set<string>()
300
309
  for (const event of mergeResult.newEvents) {
301
- // TODO apply changeset if available (will require tracking of write tables as well)
302
- const decodedEventDef = Schema.decodeSync(eventSchema)(event)
303
310
  const {
304
311
  writeTables: newWriteTables,
305
312
  sessionChangeset,
306
313
  materializerHash,
307
- } = materializeEvent(decodedEventDef, {
308
- otelContext,
314
+ } = yield* materializeEvent(event, {
309
315
  withChangeset: true,
310
316
  materializerHashLeader: event.meta.materializerHashLeader,
311
317
  })
@@ -318,6 +324,9 @@ export const makeClientSessionSyncProcessor = ({
318
324
  }
319
325
 
320
326
  refreshTables(writeTables)
327
+
328
+ // We're only triggering the sync state update after all events have been materialized
329
+ yield* syncStateUpdateQueue.offer(mergeResult.newSyncState)
321
330
  }).pipe(
322
331
  Effect.tapCauseLogPretty,
323
332
  Effect.catchAllCause((cause) => clientSession.shutdown(Exit.failCause(cause))),
@@ -364,10 +373,7 @@ export const makeClientSessionSyncProcessor = ({
364
373
  export interface ClientSessionSyncProcessor {
365
374
  push: (
366
375
  batch: ReadonlyArray<LiveStoreEvent.PartialAnyDecoded>,
367
- options: { otelContext: otel.Context },
368
- ) => {
369
- writeTables: Set<string>
370
- }
376
+ ) => Effect.Effect<{ writeTables: Set<string> }, MaterializeError>
371
377
  boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
372
378
  /**
373
379
  * Only used for debugging / observability.
@@ -388,11 +394,11 @@ const SIMULATION_ENABLED = true
388
394
  // Warning: High values for the simulation params can lead to very long test runs since those get multiplied with the number of events
389
395
  export const ClientSessionSyncProcessorSimulationParams = Schema.Struct({
390
396
  pull: Schema.Struct({
391
- '1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0, 25)),
392
- '2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0, 25)),
393
- '3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 25)),
394
- '4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 25)),
395
- '5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 25)),
397
+ '1_before_leader_push_fiber_interrupt': Schema.Int.pipe(Schema.between(0, 15)),
398
+ '2_before_leader_push_queue_clear': Schema.Int.pipe(Schema.between(0, 15)),
399
+ '3_before_rebase_rollback': Schema.Int.pipe(Schema.between(0, 15)),
400
+ '4_before_leader_push_queue_offer': Schema.Int.pipe(Schema.between(0, 15)),
401
+ '5_before_leader_push_fiber_run': Schema.Int.pipe(Schema.between(0, 15)),
396
402
  }),
397
403
  })
398
404
  type ClientSessionSyncProcessorSimulationParams = typeof ClientSessionSyncProcessorSimulationParams.Type