@livestore/common 0.0.0-snapshot-f5ece014485b5ab43795c218f7ba7cfa32c81707 → 0.0.0-snapshot-057a9e3a18ca69a310d4eb8cf35a34e94fa1841e

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 (228) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +35 -0
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -0
  4. package/dist/ClientSessionLeaderThreadProxy.js +6 -0
  5. package/dist/ClientSessionLeaderThreadProxy.js.map +1 -0
  6. package/dist/adapter-types.d.ts +10 -161
  7. package/dist/adapter-types.d.ts.map +1 -1
  8. package/dist/adapter-types.js +5 -49
  9. package/dist/adapter-types.js.map +1 -1
  10. package/dist/defs.d.ts +20 -0
  11. package/dist/defs.d.ts.map +1 -0
  12. package/dist/defs.js +12 -0
  13. package/dist/defs.js.map +1 -0
  14. package/dist/devtools/devtools-messages-client-session.d.ts +23 -21
  15. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  16. package/dist/devtools/devtools-messages-common.d.ts +6 -6
  17. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  18. package/dist/devtools/devtools-messages-leader.d.ts +26 -24
  19. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  20. package/dist/devtools/devtools-messages.d.ts +1 -1
  21. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  22. package/dist/devtools/devtools-messages.js +1 -1
  23. package/dist/devtools/devtools-messages.js.map +1 -1
  24. package/dist/errors.d.ts +50 -0
  25. package/dist/errors.d.ts.map +1 -0
  26. package/dist/errors.js +36 -0
  27. package/dist/errors.js.map +1 -0
  28. package/dist/index.d.ts +11 -11
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +11 -11
  31. package/dist/index.js.map +1 -1
  32. package/dist/leader-thread/LeaderSyncProcessor.d.ts +6 -7
  33. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  34. package/dist/leader-thread/LeaderSyncProcessor.js +112 -122
  35. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  36. package/dist/leader-thread/eventlog.d.ts +17 -6
  37. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  38. package/dist/leader-thread/eventlog.js +33 -18
  39. package/dist/leader-thread/eventlog.js.map +1 -1
  40. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  41. package/dist/leader-thread/leader-worker-devtools.js +1 -2
  42. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  43. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  44. package/dist/leader-thread/make-leader-thread-layer.js +37 -7
  45. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  46. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  47. package/dist/leader-thread/materialize-event.js +7 -1
  48. package/dist/leader-thread/materialize-event.js.map +1 -1
  49. package/dist/leader-thread/mod.d.ts +4 -3
  50. package/dist/leader-thread/mod.d.ts.map +1 -1
  51. package/dist/leader-thread/mod.js +4 -3
  52. package/dist/leader-thread/mod.js.map +1 -1
  53. package/dist/leader-thread/recreate-db.d.ts +13 -6
  54. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  55. package/dist/leader-thread/recreate-db.js +1 -3
  56. package/dist/leader-thread/recreate-db.js.map +1 -1
  57. package/dist/leader-thread/types.d.ts +5 -7
  58. package/dist/leader-thread/types.d.ts.map +1 -1
  59. package/dist/make-client-session.d.ts +1 -1
  60. package/dist/make-client-session.d.ts.map +1 -1
  61. package/dist/make-client-session.js +1 -1
  62. package/dist/make-client-session.js.map +1 -1
  63. package/dist/rematerialize-from-eventlog.d.ts +1 -1
  64. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  65. package/dist/rematerialize-from-eventlog.js +10 -2
  66. package/dist/rematerialize-from-eventlog.js.map +1 -1
  67. package/dist/schema/EventDef.d.ts +2 -2
  68. package/dist/schema/EventDef.d.ts.map +1 -1
  69. package/dist/schema/EventDef.js +2 -2
  70. package/dist/schema/EventDef.js.map +1 -1
  71. package/dist/schema/EventSequenceNumber.d.ts +20 -2
  72. package/dist/schema/EventSequenceNumber.d.ts.map +1 -1
  73. package/dist/schema/EventSequenceNumber.js +71 -19
  74. package/dist/schema/EventSequenceNumber.js.map +1 -1
  75. package/dist/schema/EventSequenceNumber.test.js +88 -3
  76. package/dist/schema/EventSequenceNumber.test.js.map +1 -1
  77. package/dist/schema/LiveStoreEvent.d.ts +23 -9
  78. package/dist/schema/LiveStoreEvent.d.ts.map +1 -1
  79. package/dist/schema/LiveStoreEvent.js +12 -4
  80. package/dist/schema/LiveStoreEvent.js.map +1 -1
  81. package/dist/schema/events.d.ts +1 -1
  82. package/dist/schema/events.d.ts.map +1 -1
  83. package/dist/schema/events.js +1 -1
  84. package/dist/schema/events.js.map +1 -1
  85. package/dist/schema/mod.d.ts +6 -6
  86. package/dist/schema/mod.d.ts.map +1 -1
  87. package/dist/schema/mod.js +6 -6
  88. package/dist/schema/mod.js.map +1 -1
  89. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  90. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  91. package/dist/schema/state/sqlite/client-document-def.test.js +3 -5
  92. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  93. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +2 -2
  94. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
  95. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
  96. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
  97. package/dist/schema/state/sqlite/db-schema/hash.d.ts.map +1 -1
  98. package/dist/schema/state/sqlite/db-schema/hash.js +3 -2
  99. package/dist/schema/state/sqlite/db-schema/hash.js.map +1 -1
  100. package/dist/schema/state/sqlite/db-schema/mod.d.ts +1 -1
  101. package/dist/schema/state/sqlite/db-schema/mod.d.ts.map +1 -1
  102. package/dist/schema/state/sqlite/db-schema/mod.js +1 -1
  103. package/dist/schema/state/sqlite/db-schema/mod.js.map +1 -1
  104. package/dist/schema/state/sqlite/mod.d.ts +3 -3
  105. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  106. package/dist/schema/state/sqlite/mod.js +2 -2
  107. package/dist/schema/state/sqlite/mod.js.map +1 -1
  108. package/dist/schema/state/sqlite/query-builder/api.d.ts +35 -8
  109. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  110. package/dist/schema/state/sqlite/query-builder/api.js.map +1 -1
  111. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  112. package/dist/schema/state/sqlite/query-builder/impl.js +18 -23
  113. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  114. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts +1 -81
  115. package/dist/schema/state/sqlite/query-builder/impl.test.d.ts.map +1 -1
  116. package/dist/schema/state/sqlite/query-builder/impl.test.js +34 -20
  117. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  118. package/dist/schema/state/sqlite/system-tables.d.ts +67 -62
  119. package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -1
  120. package/dist/schema/state/sqlite/system-tables.js +8 -17
  121. package/dist/schema/state/sqlite/system-tables.js.map +1 -1
  122. package/dist/schema/state/sqlite/table-def.d.ts +1 -1
  123. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  124. package/dist/schema-management/migrations.d.ts +3 -1
  125. package/dist/schema-management/migrations.d.ts.map +1 -1
  126. package/dist/schema-management/migrations.js.map +1 -1
  127. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  128. package/dist/sql-queries/sql-queries.js +2 -0
  129. package/dist/sql-queries/sql-queries.js.map +1 -1
  130. package/dist/sqlite-types.d.ts +72 -0
  131. package/dist/sqlite-types.d.ts.map +1 -0
  132. package/dist/sqlite-types.js +5 -0
  133. package/dist/sqlite-types.js.map +1 -0
  134. package/dist/sync/ClientSessionSyncProcessor.d.ts +7 -4
  135. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  136. package/dist/sync/ClientSessionSyncProcessor.js +17 -16
  137. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  138. package/dist/sync/index.d.ts +1 -1
  139. package/dist/sync/index.d.ts.map +1 -1
  140. package/dist/sync/index.js +1 -1
  141. package/dist/sync/index.js.map +1 -1
  142. package/dist/sync/next/graphology.d.ts.map +1 -1
  143. package/dist/sync/next/graphology.js +0 -6
  144. package/dist/sync/next/graphology.js.map +1 -1
  145. package/dist/sync/next/graphology_.d.ts +1 -1
  146. package/dist/sync/next/graphology_.d.ts.map +1 -1
  147. package/dist/sync/next/graphology_.js +1 -1
  148. package/dist/sync/next/graphology_.js.map +1 -1
  149. package/dist/sync/next/mod.d.ts +3 -3
  150. package/dist/sync/next/mod.d.ts.map +1 -1
  151. package/dist/sync/next/mod.js +3 -3
  152. package/dist/sync/next/mod.js.map +1 -1
  153. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  154. package/dist/sync/next/rebase-events.js +1 -0
  155. package/dist/sync/next/rebase-events.js.map +1 -1
  156. package/dist/sync/next/test/compact-events.test.js +1 -1
  157. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  158. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  159. package/dist/sync/next/test/event-fixtures.js +12 -3
  160. package/dist/sync/next/test/event-fixtures.js.map +1 -1
  161. package/dist/sync/sync.d.ts +2 -0
  162. package/dist/sync/sync.d.ts.map +1 -1
  163. package/dist/sync/sync.js +3 -0
  164. package/dist/sync/sync.js.map +1 -1
  165. package/dist/sync/syncstate.d.ts +13 -4
  166. package/dist/sync/syncstate.d.ts.map +1 -1
  167. package/dist/sync/syncstate.js +23 -10
  168. package/dist/sync/syncstate.js.map +1 -1
  169. package/dist/sync/syncstate.test.js +17 -18
  170. package/dist/sync/syncstate.test.js.map +1 -1
  171. package/dist/util.d.ts.map +1 -1
  172. package/dist/util.js +0 -1
  173. package/dist/util.js.map +1 -1
  174. package/dist/version.d.ts +1 -1
  175. package/dist/version.d.ts.map +1 -1
  176. package/dist/version.js +1 -1
  177. package/dist/version.js.map +1 -1
  178. package/package.json +6 -5
  179. package/src/ClientSessionLeaderThreadProxy.ts +40 -0
  180. package/src/adapter-types.ts +19 -165
  181. package/src/defs.ts +17 -0
  182. package/src/devtools/devtools-messages.ts +1 -1
  183. package/src/errors.ts +49 -0
  184. package/src/index.ts +11 -11
  185. package/src/leader-thread/LeaderSyncProcessor.ts +141 -180
  186. package/src/leader-thread/eventlog.ts +79 -57
  187. package/src/leader-thread/leader-worker-devtools.ts +1 -2
  188. package/src/leader-thread/make-leader-thread-layer.ts +52 -8
  189. package/src/leader-thread/materialize-event.ts +8 -1
  190. package/src/leader-thread/mod.ts +4 -3
  191. package/src/leader-thread/recreate-db.ts +99 -91
  192. package/src/leader-thread/types.ts +6 -11
  193. package/src/make-client-session.ts +2 -3
  194. package/src/rematerialize-from-eventlog.ts +10 -2
  195. package/src/schema/EventDef.ts +5 -3
  196. package/src/schema/EventSequenceNumber.test.ts +120 -3
  197. package/src/schema/EventSequenceNumber.ts +95 -23
  198. package/src/schema/LiveStoreEvent.ts +20 -4
  199. package/src/schema/events.ts +1 -1
  200. package/src/schema/mod.ts +6 -6
  201. package/src/schema/state/sqlite/client-document-def.test.ts +7 -5
  202. package/src/schema/state/sqlite/client-document-def.ts +36 -24
  203. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +2 -2
  204. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +16 -10
  205. package/src/schema/state/sqlite/db-schema/hash.ts +3 -4
  206. package/src/schema/state/sqlite/db-schema/mod.ts +1 -1
  207. package/src/schema/state/sqlite/mod.ts +6 -6
  208. package/src/schema/state/sqlite/query-builder/api.ts +38 -8
  209. package/src/schema/state/sqlite/query-builder/impl.test.ts +60 -20
  210. package/src/schema/state/sqlite/query-builder/impl.ts +19 -24
  211. package/src/schema/state/sqlite/system-tables.ts +9 -22
  212. package/src/schema/state/sqlite/table-def.ts +1 -1
  213. package/src/schema-management/migrations.ts +3 -1
  214. package/src/sql-queries/sql-queries.ts +2 -0
  215. package/src/sqlite-types.ts +76 -0
  216. package/src/sync/ClientSessionSyncProcessor.ts +29 -23
  217. package/src/sync/index.ts +1 -1
  218. package/src/sync/next/graphology.ts +3 -11
  219. package/src/sync/next/graphology_.ts +1 -1
  220. package/src/sync/next/mod.ts +3 -3
  221. package/src/sync/next/rebase-events.ts +2 -1
  222. package/src/sync/next/test/compact-events.test.ts +1 -1
  223. package/src/sync/next/test/event-fixtures.ts +12 -3
  224. package/src/sync/sync.ts +3 -0
  225. package/src/sync/syncstate.test.ts +17 -18
  226. package/src/sync/syncstate.ts +31 -10
  227. package/src/util.ts +0 -1
  228. package/src/version.ts +1 -1
@@ -28,7 +28,9 @@ export type EventDef<TName extends string, TType, TEncoded = TType, TDerived ext
28
28
  }
29
29
 
30
30
  /** Helper function to construct a partial event */
31
- (args: TType): {
31
+ (
32
+ args: TType,
33
+ ): {
32
34
  name: TName
33
35
  args: TType
34
36
  }
@@ -194,14 +196,14 @@ export type Materializer<TEventDef extends EventDef.AnyWithoutFn = EventDef.AnyW
194
196
  ) => SingleOrReadonlyArray<MaterializerResult>
195
197
 
196
198
  export const defineMaterializer = <TEventDef extends EventDef.AnyWithoutFn>(
197
- eventDef: TEventDef,
199
+ _eventDef: TEventDef,
198
200
  materializer: Materializer<TEventDef>,
199
201
  ): Materializer<TEventDef> => {
200
202
  return materializer
201
203
  }
202
204
 
203
205
  export const materializers = <TInputRecord extends Record<string, EventDef.AnyWithoutFn>>(
204
- eventDefRecord: TInputRecord,
206
+ _eventDefRecord: TInputRecord,
205
207
  handlers: {
206
208
  [TEventName in TInputRecord[keyof TInputRecord]['name'] as Extract<
207
209
  TInputRecord[keyof TInputRecord],
@@ -4,9 +4,126 @@ import { expect } from 'vitest'
4
4
  import { EventSequenceNumber } from './mod.js'
5
5
 
6
6
  Vitest.describe('EventSequenceNumber', () => {
7
- Vitest.test('nextPair', () => {
7
+ Vitest.test('nextPair (no rebase)', () => {
8
8
  const e_0_0 = EventSequenceNumber.make({ global: 0, client: 0 })
9
- expect(EventSequenceNumber.nextPair(e_0_0, false).seqNum).toStrictEqual({ global: 1, client: 0 })
10
- expect(EventSequenceNumber.nextPair(e_0_0, true).seqNum).toStrictEqual({ global: 0, client: 1 })
9
+ expect(EventSequenceNumber.nextPair({ seqNum: e_0_0, isClient: false }).seqNum).toStrictEqual({
10
+ global: 1,
11
+ client: 0,
12
+ rebaseGeneration: 0,
13
+ })
14
+ expect(EventSequenceNumber.nextPair({ seqNum: e_0_0, isClient: true }).seqNum).toStrictEqual({
15
+ global: 0,
16
+ client: 1,
17
+ rebaseGeneration: 0,
18
+ })
19
+ })
20
+
21
+ Vitest.test('nextPair (rebase)', () => {
22
+ const e_0_0 = EventSequenceNumber.make({ global: 0, client: 0 })
23
+ expect(EventSequenceNumber.nextPair({ seqNum: e_0_0, isClient: false, rebaseGeneration: 1 }).seqNum).toStrictEqual({
24
+ global: 1,
25
+ client: 0,
26
+ rebaseGeneration: 1,
27
+ })
28
+ expect(EventSequenceNumber.nextPair({ seqNum: e_0_0, isClient: true, rebaseGeneration: 1 }).seqNum).toStrictEqual({
29
+ global: 0,
30
+ client: 1,
31
+ rebaseGeneration: 1,
32
+ })
33
+
34
+ const e_0_0_g1 = EventSequenceNumber.make({ global: 0, client: 0, rebaseGeneration: 2 })
35
+ expect(EventSequenceNumber.nextPair({ seqNum: e_0_0_g1, isClient: false }).seqNum).toStrictEqual({
36
+ global: 1,
37
+ client: 0,
38
+ rebaseGeneration: 2,
39
+ })
40
+ })
41
+
42
+ Vitest.test('toString', () => {
43
+ expect(EventSequenceNumber.toString(EventSequenceNumber.make({ global: 0, client: 0 }))).toBe('e0')
44
+ expect(EventSequenceNumber.toString(EventSequenceNumber.make({ global: 0, client: 0, rebaseGeneration: 1 }))).toBe(
45
+ 'e0r1',
46
+ )
47
+ expect(EventSequenceNumber.toString(EventSequenceNumber.make({ global: 0, client: 1 }))).toBe('e0+1')
48
+ expect(EventSequenceNumber.toString(EventSequenceNumber.make({ global: 0, client: 1, rebaseGeneration: 1 }))).toBe(
49
+ 'e0+1r1',
50
+ )
51
+ expect(EventSequenceNumber.toString(EventSequenceNumber.make({ global: 5, client: 3, rebaseGeneration: 2 }))).toBe(
52
+ 'e5+3r2',
53
+ )
54
+ })
55
+
56
+ Vitest.test('fromString', () => {
57
+ // Basic cases
58
+ expect(EventSequenceNumber.fromString('e0')).toStrictEqual(EventSequenceNumber.make({ global: 0, client: 0 }))
59
+ expect(EventSequenceNumber.fromString('e0r1')).toStrictEqual(
60
+ EventSequenceNumber.make({ global: 0, client: 0, rebaseGeneration: 1 }),
61
+ )
62
+ expect(EventSequenceNumber.fromString('e0+1')).toStrictEqual(EventSequenceNumber.make({ global: 0, client: 1 }))
63
+ expect(EventSequenceNumber.fromString('e0+1r1')).toStrictEqual(
64
+ EventSequenceNumber.make({ global: 0, client: 1, rebaseGeneration: 1 }),
65
+ )
66
+ expect(EventSequenceNumber.fromString('e5+3r2')).toStrictEqual(
67
+ EventSequenceNumber.make({ global: 5, client: 3, rebaseGeneration: 2 }),
68
+ )
69
+
70
+ // Error cases
71
+ expect(() => EventSequenceNumber.fromString('0')).toThrow(
72
+ 'Invalid event sequence number string: must start with "e"',
73
+ )
74
+ expect(() => EventSequenceNumber.fromString('eabc')).toThrow(
75
+ 'Invalid event sequence number string: invalid number format',
76
+ )
77
+ expect(() => EventSequenceNumber.fromString('e0+abc')).toThrow(
78
+ 'Invalid event sequence number string: invalid number format',
79
+ )
80
+ expect(() => EventSequenceNumber.fromString('e0rabc')).toThrow(
81
+ 'Invalid event sequence number string: invalid number format',
82
+ )
83
+ })
84
+
85
+ Vitest.test('toString/fromString roundtrip', () => {
86
+ const testCases = [
87
+ { global: 0, client: 0, rebaseGeneration: 0 },
88
+ { global: 0, client: 0, rebaseGeneration: 1 },
89
+ { global: 0, client: 1, rebaseGeneration: 0 },
90
+ { global: 0, client: 1, rebaseGeneration: 1 },
91
+ { global: 5, client: 3, rebaseGeneration: 2 },
92
+ { global: 100, client: 50, rebaseGeneration: 10 },
93
+ ]
94
+
95
+ for (const testCase of testCases) {
96
+ const original = EventSequenceNumber.make(testCase)
97
+ const str = EventSequenceNumber.toString(original)
98
+ const parsed = EventSequenceNumber.fromString(str)
99
+ expect(parsed).toStrictEqual(original)
100
+ }
101
+ })
102
+
103
+ Vitest.test('compare', () => {
104
+ const e_0_0_r0 = EventSequenceNumber.make({ global: 0, client: 0, rebaseGeneration: 0 })
105
+ const e_0_0_r1 = EventSequenceNumber.make({ global: 0, client: 0, rebaseGeneration: 1 })
106
+ const e_0_1_r0 = EventSequenceNumber.make({ global: 0, client: 1, rebaseGeneration: 0 })
107
+ const e_0_1_r1 = EventSequenceNumber.make({ global: 0, client: 1, rebaseGeneration: 1 })
108
+ const e_1_0_r0 = EventSequenceNumber.make({ global: 1, client: 0, rebaseGeneration: 0 })
109
+ const e_1_1_r0 = EventSequenceNumber.make({ global: 1, client: 1, rebaseGeneration: 0 })
110
+
111
+ // Global comparison (strongest level)
112
+ expect(EventSequenceNumber.compare(e_0_0_r0, e_1_0_r0)).toBeLessThan(0)
113
+ expect(EventSequenceNumber.compare(e_1_0_r0, e_0_0_r0)).toBeGreaterThan(0)
114
+ expect(EventSequenceNumber.compare(e_0_1_r1, e_1_0_r0)).toBeLessThan(0) // global overrides client and rebase
115
+
116
+ // Client comparison (second level)
117
+ expect(EventSequenceNumber.compare(e_0_0_r0, e_0_1_r0)).toBeLessThan(0)
118
+ expect(EventSequenceNumber.compare(e_0_1_r0, e_0_0_r0)).toBeGreaterThan(0)
119
+ expect(EventSequenceNumber.compare(e_0_0_r1, e_0_1_r0)).toBeLessThan(0) // client overrides rebase
120
+
121
+ // Rebase generation comparison (weakest level)
122
+ expect(EventSequenceNumber.compare(e_0_0_r0, e_0_0_r1)).toBeLessThan(0)
123
+ expect(EventSequenceNumber.compare(e_0_0_r1, e_0_0_r0)).toBeGreaterThan(0)
124
+
125
+ // Equal comparison
126
+ expect(EventSequenceNumber.compare(e_0_0_r0, e_0_0_r0)).toBe(0)
127
+ expect(EventSequenceNumber.compare(e_1_1_r0, e_1_1_r0)).toBe(0)
11
128
  })
12
129
  })
@@ -10,6 +10,8 @@ export const GlobalEventSequenceNumber = Schema.fromBrand(globalEventSequenceNum
10
10
 
11
11
  export const clientDefault = 0 as any as ClientEventSequenceNumber
12
12
 
13
+ export const rebaseGenerationDefault = 0
14
+
13
15
  /**
14
16
  * LiveStore event sequence number value consisting of a globally unique event sequence number
15
17
  * and a client sequence number.
@@ -20,16 +22,17 @@ export type EventSequenceNumber = {
20
22
  global: GlobalEventSequenceNumber
21
23
  client: ClientEventSequenceNumber
22
24
  /**
23
- * TODO add generation number in favour of LEADER_MERGE_COUNTER_TABLE
25
+ * Generation integer that is incremented whenever the client rebased.
26
+ * Starts from and resets to 0 for each global sequence number.
24
27
  */
25
- // generation: number
28
+ rebaseGeneration: number
26
29
  }
27
30
 
28
- // export const EventSequenceNumber = Schema.Struct({})
29
- // export const EventSequenceNumber = Schema.Struct({})
30
- // export const ClientEventSequenceNumber = Schema.Struct({})
31
- // export const GlobalEventSequenceNumber = Schema.Struct({})
31
+ export type EventSequenceNumberInput =
32
+ | EventSequenceNumber
33
+ | (Omit<typeof EventSequenceNumber.Encoded, 'rebaseGeneration'> & { rebaseGeneration?: number })
32
34
 
35
+ // TODO adjust name to `ClientEventSequenceNumber`
33
36
  /**
34
37
  * NOTE: Client mutation events with a non-0 client id, won't be synced to the sync backend.
35
38
  */
@@ -40,36 +43,83 @@ export const EventSequenceNumber = Schema.Struct({
40
43
 
41
44
  // TODO also provide a way to see "confirmation level" of event (e.g. confirmed by leader/sync backend)
42
45
 
43
- // TODO: actually add this field
44
46
  // Client only
45
- // generation: Schema.Number.pipe(Schema.optional),
46
- }).annotations({ title: 'LiveStore.EventSequenceNumber' })
47
+ rebaseGeneration: Schema.Int,
48
+ }).annotations({
49
+ title: 'LiveStore.EventSequenceNumber',
50
+ pretty: () => (seqNum) => toString(seqNum),
51
+ })
47
52
 
48
53
  /**
49
54
  * Compare two event sequence numbers i.e. checks if the first event sequence number is less than the second.
55
+ * Comparison hierarchy: global > client > rebaseGeneration
50
56
  */
51
57
  export const compare = (a: EventSequenceNumber, b: EventSequenceNumber) => {
52
58
  if (a.global !== b.global) {
53
59
  return a.global - b.global
54
60
  }
55
- return a.client - b.client
61
+ if (a.client !== b.client) {
62
+ return a.client - b.client
63
+ }
64
+ return a.rebaseGeneration - b.rebaseGeneration
56
65
  }
57
66
 
58
67
  /**
59
68
  * Convert an event sequence number to a string representation.
60
69
  */
61
- export const toString = (seqNum: EventSequenceNumber) =>
62
- seqNum.client === 0 ? `e${seqNum.global}` : `e${seqNum.global}+${seqNum.client}`
70
+ export const toString = (seqNum: EventSequenceNumber) => {
71
+ const rebaseGenerationStr = seqNum.rebaseGeneration > 0 ? `r${seqNum.rebaseGeneration}` : ''
72
+ return seqNum.client === 0
73
+ ? `e${seqNum.global}${rebaseGenerationStr}`
74
+ : `e${seqNum.global}+${seqNum.client}${rebaseGenerationStr}`
75
+ }
63
76
 
64
77
  /**
65
78
  * Convert a string representation of an event sequence number to an event sequence number.
79
+ * Parses strings in the format: e{global}[+{client}][r{rebaseGeneration}]
80
+ * Examples: "e0", "e0r1", "e0+1", "e0+1r1"
66
81
  */
67
82
  export const fromString = (str: string): EventSequenceNumber => {
68
- const [global, client] = str.slice(1, -1).split(',').map(Number)
69
- if (global === undefined || client === undefined) {
70
- throw new Error('Invalid event sequence number string')
83
+ if (!str.startsWith('e')) {
84
+ throw new Error('Invalid event sequence number string: must start with "e"')
85
+ }
86
+
87
+ // Remove the 'e' prefix
88
+ const remaining = str.slice(1)
89
+
90
+ // Parse rebase generation if present
91
+ let rebaseGeneration = rebaseGenerationDefault
92
+ let withoutRebase = remaining
93
+ const rebaseMatch = remaining.match(/r(\d+)$/)
94
+ if (rebaseMatch !== null) {
95
+ rebaseGeneration = Number.parseInt(rebaseMatch[1]!, 10)
96
+ withoutRebase = remaining.slice(0, -rebaseMatch[0].length)
97
+ }
98
+
99
+ // Parse global and client parts
100
+ const parts = withoutRebase.split('+')
101
+
102
+ // Validate that parts contain only digits (and possibly empty for client)
103
+ if (parts[0] === '' || !/^\d+$/.test(parts[0]!)) {
104
+ throw new Error('Invalid event sequence number string: invalid number format')
105
+ }
106
+
107
+ if (parts.length > 1 && parts[1] !== undefined && (parts[1] === '' || !/^\d+$/.test(parts[1]))) {
108
+ throw new Error('Invalid event sequence number string: invalid number format')
109
+ }
110
+
111
+ const global = Number.parseInt(parts[0]!, 10)
112
+ const client = parts.length > 1 && parts[1] !== undefined ? Number.parseInt(parts[1], 10) : 0
113
+
114
+ if (Number.isNaN(global) || Number.isNaN(client) || Number.isNaN(rebaseGeneration)) {
115
+ throw new TypeError('Invalid event sequence number string: invalid number format')
116
+ }
117
+
118
+ return {
119
+ global: global as any as GlobalEventSequenceNumber,
120
+ client: client as any as ClientEventSequenceNumber,
121
+ rebaseGeneration,
71
122
  }
72
- return { global, client } as EventSequenceNumber
73
123
  }
74
124
 
75
125
  export const isEqual = (a: EventSequenceNumber, b: EventSequenceNumber) =>
@@ -80,6 +130,7 @@ export type EventSequenceNumberPair = { seqNum: EventSequenceNumber; parentSeqNu
80
130
  export const ROOT = {
81
131
  global: 0 as any as GlobalEventSequenceNumber,
82
132
  client: clientDefault,
133
+ rebaseGeneration: rebaseGenerationDefault,
83
134
  } satisfies EventSequenceNumber
84
135
 
85
136
  export const isGreaterThan = (a: EventSequenceNumber, b: EventSequenceNumber) => {
@@ -101,21 +152,42 @@ export const diff = (a: EventSequenceNumber, b: EventSequenceNumber) => {
101
152
  }
102
153
  }
103
154
 
104
- export const make = (seqNum: EventSequenceNumber | typeof EventSequenceNumber.Encoded): EventSequenceNumber => {
105
- return Schema.is(EventSequenceNumber)(seqNum) ? seqNum : Schema.decodeSync(EventSequenceNumber)(seqNum)
155
+ export const make = (seqNum: EventSequenceNumberInput): EventSequenceNumber => {
156
+ return Schema.is(EventSequenceNumber)(seqNum)
157
+ ? seqNum
158
+ : Schema.decodeSync(EventSequenceNumber)({
159
+ ...seqNum,
160
+ rebaseGeneration: seqNum.rebaseGeneration ?? rebaseGenerationDefault,
161
+ })
106
162
  }
107
163
 
108
- export const nextPair = (seqNum: EventSequenceNumber, isLocal: boolean): EventSequenceNumberPair => {
109
- if (isLocal) {
164
+ export const nextPair = ({
165
+ seqNum,
166
+ isClient,
167
+ rebaseGeneration,
168
+ }: {
169
+ seqNum: EventSequenceNumber
170
+ isClient: boolean
171
+ rebaseGeneration?: number
172
+ }): EventSequenceNumberPair => {
173
+ if (isClient) {
110
174
  return {
111
- seqNum: { global: seqNum.global, client: (seqNum.client + 1) as any as ClientEventSequenceNumber },
175
+ seqNum: {
176
+ global: seqNum.global,
177
+ client: (seqNum.client + 1) as any as ClientEventSequenceNumber,
178
+ rebaseGeneration: rebaseGeneration ?? seqNum.rebaseGeneration,
179
+ },
112
180
  parentSeqNum: seqNum,
113
181
  }
114
182
  }
115
183
 
116
184
  return {
117
- seqNum: { global: (seqNum.global + 1) as any as GlobalEventSequenceNumber, client: clientDefault },
185
+ seqNum: {
186
+ global: (seqNum.global + 1) as any as GlobalEventSequenceNumber,
187
+ client: clientDefault,
188
+ rebaseGeneration: rebaseGeneration ?? seqNum.rebaseGeneration,
189
+ },
118
190
  // NOTE we always point to `client: 0` for non-clientOnly events
119
- parentSeqNum: { global: seqNum.global, client: clientDefault },
191
+ parentSeqNum: { global: seqNum.global, client: clientDefault, rebaseGeneration: seqNum.rebaseGeneration },
120
192
  }
121
193
  }
@@ -221,10 +221,18 @@ export class EncodedWithMeta extends Schema.Class<EncodedWithMeta>('LiveStoreEve
221
221
  * +---- global number
222
222
  * Client num is ommitted for global events
223
223
  */
224
- rebase = (parentSeqNum: EventSequenceNumber.EventSequenceNumber, isClient: boolean) =>
224
+ rebase = ({
225
+ parentSeqNum,
226
+ isClient,
227
+ rebaseGeneration,
228
+ }: {
229
+ parentSeqNum: EventSequenceNumber.EventSequenceNumber
230
+ isClient: boolean
231
+ rebaseGeneration: number
232
+ }) =>
225
233
  new EncodedWithMeta({
226
234
  ...this,
227
- ...EventSequenceNumber.nextPair(parentSeqNum, isClient),
235
+ ...EventSequenceNumber.nextPair({ seqNum: parentSeqNum, isClient, rebaseGeneration }),
228
236
  })
229
237
 
230
238
  static fromGlobal = (
@@ -237,8 +245,16 @@ export class EncodedWithMeta extends Schema.Class<EncodedWithMeta>('LiveStoreEve
237
245
  ) =>
238
246
  new EncodedWithMeta({
239
247
  ...event,
240
- seqNum: { global: event.seqNum, client: EventSequenceNumber.clientDefault },
241
- parentSeqNum: { global: event.parentSeqNum, client: EventSequenceNumber.clientDefault },
248
+ seqNum: {
249
+ global: event.seqNum,
250
+ client: EventSequenceNumber.clientDefault,
251
+ rebaseGeneration: EventSequenceNumber.rebaseGenerationDefault,
252
+ },
253
+ parentSeqNum: {
254
+ global: event.parentSeqNum,
255
+ client: EventSequenceNumber.clientDefault,
256
+ rebaseGeneration: EventSequenceNumber.rebaseGenerationDefault,
257
+ },
242
258
  meta: {
243
259
  sessionChangeset: { _tag: 'unset' as const },
244
260
  syncMetadata: meta.syncMetadata,
@@ -1 +1 @@
1
- export { defineEvent, synced, clientOnly } from './EventDef.js'
1
+ export { clientOnly, defineEvent, synced } from './EventDef.js'
package/src/schema/mod.ts CHANGED
@@ -1,9 +1,9 @@
1
- export * as SystemTables from './state/sqlite/system-tables.js'
2
- export { SqliteAst, SqliteDsl } from './state/sqlite/db-schema/mod.js'
3
1
  export * from './EventDef.js'
4
- export * from './state/sqlite/schema-helpers.js'
5
- export * from './schema.js'
6
- export * as State from './state/mod.js'
7
- export * as LiveStoreEvent from './LiveStoreEvent.js'
8
2
  export * as EventSequenceNumber from './EventSequenceNumber.js'
9
3
  export * as Events from './events.js'
4
+ export * as LiveStoreEvent from './LiveStoreEvent.js'
5
+ export * from './schema.js'
6
+ export * as State from './state/mod.js'
7
+ export { SqliteAst, SqliteDsl } from './state/sqlite/db-schema/mod.js'
8
+ export * from './state/sqlite/schema-helpers.js'
9
+ export * as SystemTables from './state/sqlite/system-tables.js'
@@ -3,7 +3,7 @@ import { describe, expect, test } from 'vitest'
3
3
 
4
4
  import { tables } from '../../../__tests__/fixture.js'
5
5
  import type * as LiveStoreEvent from '../../LiveStoreEvent.js'
6
- import { clientDocument, ClientDocumentTableDefSymbol } from './client-document-def.js'
6
+ import { ClientDocumentTableDefSymbol, clientDocument } from './client-document-def.js'
7
7
 
8
8
  describe('client document table', () => {
9
9
  test('set event', () => {
@@ -70,8 +70,9 @@ describe('client document table', () => {
70
70
  })
71
71
 
72
72
  test('struct value (partial set=true)', () => {
73
- expect(forSchema(Schema.Struct({ a: Schema.String }), { a: 'hello' }, 'id1', { partialSet: true }))
74
- .toMatchInlineSnapshot(`
73
+ expect(
74
+ forSchema(Schema.Struct({ a: Schema.String }), { a: 'hello' }, 'id1', { partialSet: true }),
75
+ ).toMatchInlineSnapshot(`
75
76
  {
76
77
  "bindValues": [
77
78
  "id1",
@@ -92,8 +93,9 @@ describe('client document table', () => {
92
93
  })
93
94
 
94
95
  test('struct value (partial set=false)', () => {
95
- expect(forSchema(Schema.Struct({ a: Schema.String }), { a: 'hello' }, 'id1', { partialSet: false }))
96
- .toMatchInlineSnapshot(`
96
+ expect(
97
+ forSchema(Schema.Struct({ a: Schema.String }), { a: 'hello' }, 'id1', { partialSet: false }),
98
+ ).toMatchInlineSnapshot(`
97
99
  {
98
100
  "bindValues": [
99
101
  "id1",
@@ -386,37 +386,49 @@ export namespace ClientDocumentTableDef {
386
386
  }
387
387
  }
388
388
 
389
- export type GetOptions<TTableDef extends TraitAny> =
390
- TTableDef extends ClientDocumentTableDef.Trait<any, any, any, infer TOptions> ? TOptions : never
389
+ export type GetOptions<TTableDef extends TraitAny> = TTableDef extends ClientDocumentTableDef.Trait<
390
+ any,
391
+ any,
392
+ any,
393
+ infer TOptions
394
+ >
395
+ ? TOptions
396
+ : never
391
397
 
392
398
  export type TraitAny = Trait<any, any, any, any>
393
399
 
394
- export type DefaultIdType<TTableDef extends TraitAny> =
395
- TTableDef extends ClientDocumentTableDef.Trait<any, any, any, infer TOptions>
396
- ? TOptions['default']['id'] extends SessionIdSymbol | string
397
- ? TOptions['default']['id']
398
- : never
400
+ export type DefaultIdType<TTableDef extends TraitAny> = TTableDef extends ClientDocumentTableDef.Trait<
401
+ any,
402
+ any,
403
+ any,
404
+ infer TOptions
405
+ >
406
+ ? TOptions['default']['id'] extends SessionIdSymbol | string
407
+ ? TOptions['default']['id']
399
408
  : never
409
+ : never
400
410
 
401
- export type SetEventDefLike<TName extends string, TType, TOptions extends ClientDocumentTableOptions<TType>> =
402
- // Helper to create partial event
403
- (TOptions['default']['id'] extends undefined
404
- ? (
405
- args: TOptions['partialSet'] extends false ? TType : Partial<TType>,
406
- id: string | SessionIdSymbol,
407
- ) => { name: `${TName}Set`; args: { id: string; value: TType } }
408
- : (
409
- args: TOptions['partialSet'] extends false ? TType : Partial<TType>,
410
- id?: string | SessionIdSymbol,
411
- ) => { name: `${TName}Set`; args: { id: string; value: TType } }) & {
411
+ export type SetEventDefLike<
412
+ TName extends string,
413
+ TType,
414
+ TOptions extends ClientDocumentTableOptions<TType>,
415
+ > = (TOptions['default']['id'] extends undefined // Helper to create partial event
416
+ ? (
417
+ args: TOptions['partialSet'] extends false ? TType : Partial<TType>,
418
+ id: string | SessionIdSymbol,
419
+ ) => { name: `${TName}Set`; args: { id: string; value: TType } }
420
+ : (
421
+ args: TOptions['partialSet'] extends false ? TType : Partial<TType>,
422
+ id?: string | SessionIdSymbol,
423
+ ) => { name: `${TName}Set`; args: { id: string; value: TType } }) & {
424
+ readonly name: `${TName}Set`
425
+ readonly schema: Schema.Schema<any>
426
+ readonly Event: {
412
427
  readonly name: `${TName}Set`
413
- readonly schema: Schema.Schema<any>
414
- readonly Event: {
415
- readonly name: `${TName}Set`
416
- readonly args: { id: string; value: TType }
417
- }
418
- readonly options: { derived: true; clientOnly: true; facts: undefined }
428
+ readonly args: { id: string; value: TType }
419
429
  }
430
+ readonly options: { derived: true; clientOnly: true; facts: undefined }
431
+ }
420
432
 
421
433
  export type SetEventDef<TName extends string, TType, TOptions extends ClientDocumentTableOptions<TType>> = EventDef<
422
434
  TName,
@@ -17,7 +17,7 @@ export const isColumnDefinition = (value: unknown): value is ColumnDefinition<an
17
17
  typeof value === 'object' &&
18
18
  value !== null &&
19
19
  'columnType' in value &&
20
- validColumnTypes.includes(value['columnType'] as any)
20
+ validColumnTypes.includes(value.columnType as any)
21
21
  )
22
22
  }
23
23
 
@@ -36,7 +36,7 @@ export type SqlDefaultValue = {
36
36
  }
37
37
 
38
38
  export const isSqlDefaultValue = (value: unknown): value is SqlDefaultValue => {
39
- return typeof value === 'object' && value !== null && 'sql' in value && typeof value['sql'] === 'string'
39
+ return typeof value === 'object' && value !== null && 'sql' in value && typeof value.sql === 'string'
40
40
  }
41
41
 
42
42
  export type ColDefFn<TColumnType extends FieldColumnType> = {
@@ -19,12 +19,13 @@ export type DbSchemaInput = Record<string, TableDefinition<any, any>> | Readonly
19
19
  * - array: we use the table name of each array item (= table definition) as the object key
20
20
  * - object: we discard the keys of the input object and use the table name of each object value (= table definition) as the new object key
21
21
  */
22
- export type DbSchemaFromInputSchema<TSchemaInput extends DbSchemaInput> =
23
- TSchemaInput extends ReadonlyArray<TableDefinition<any, any>>
24
- ? { [K in TSchemaInput[number] as K['name']]: K }
25
- : TSchemaInput extends Record<string, TableDefinition<any, any>>
26
- ? { [K in keyof TSchemaInput as TSchemaInput[K]['name']]: TSchemaInput[K] }
27
- : never
22
+ export type DbSchemaFromInputSchema<TSchemaInput extends DbSchemaInput> = TSchemaInput extends ReadonlyArray<
23
+ TableDefinition<any, any>
24
+ >
25
+ ? { [K in TSchemaInput[number] as K['name']]: K }
26
+ : TSchemaInput extends Record<string, TableDefinition<any, any>>
27
+ ? { [K in keyof TSchemaInput as TSchemaInput[K]['name']]: TSchemaInput[K] }
28
+ : never
28
29
 
29
30
  // TODO ensure via runtime check (possibly even via type-level check) that all index names are unique
30
31
  export const makeDbSchema = <TDbSchemaInput extends DbSchemaInput>(
@@ -113,8 +114,12 @@ export type TableDefinition<TName extends string, TColumns extends Columns> = {
113
114
 
114
115
  export type Columns = Record<string, ColumnDefinition<any, any>>
115
116
 
116
- export type IsSingleColumn<TColumns extends Columns | ColumnDefinition<any, any>> =
117
- TColumns extends ColumnDefinition<any, any> ? true : false
117
+ export type IsSingleColumn<TColumns extends Columns | ColumnDefinition<any, any>> = TColumns extends ColumnDefinition<
118
+ any,
119
+ any
120
+ >
121
+ ? true
122
+ : false
118
123
 
119
124
  /**
120
125
  * NOTE this is only needed to avoid a TS limitation where `StructSchemaForColumns` in the default case
@@ -207,8 +212,9 @@ export namespace FromColumns {
207
212
 
208
213
  export type RequiredInsertColumnNames<TColumns extends Columns> = keyof RequiredInsertColumns<TColumns>
209
214
 
210
- export type RequiresInsertValues<TColumns extends Columns> =
211
- RequiredInsertColumnNames<TColumns> extends never ? false : true
215
+ export type RequiresInsertValues<TColumns extends Columns> = RequiredInsertColumnNames<TColumns> extends never
216
+ ? false
217
+ : true
212
218
 
213
219
  export type InsertRowDecoded<TColumns extends Columns> = Types.Simplify<
214
220
  Pick<RowDecodedAll<TColumns>, RequiredInsertColumnNames<TColumns>> &
@@ -1,11 +1,10 @@
1
1
  // Based on https://stackoverflow.com/a/7616484
2
2
  export const hashCode = (str: string) => {
3
- let hash = 0,
4
- i,
5
- chr
3
+ let hash = 0
4
+ let i: number
5
+ let chr: number
6
6
  if (str.length === 0) return hash
7
7
  for (i = 0; i < str.length; i++) {
8
- // eslint-disable-next-line unicorn/prefer-code-point
9
8
  chr = str.charCodeAt(i)
10
9
  hash = (hash << 5) - hash + chr
11
10
  hash = Math.trunc(hash) // Convert to 32bit integer
@@ -1,2 +1,2 @@
1
- export * as SqliteDsl from './dsl/mod.js'
2
1
  export * as SqliteAst from './ast/sqlite.js'
2
+ export * as SqliteDsl from './dsl/mod.js'
@@ -6,17 +6,17 @@ import type { InternalState } from '../../schema.js'
6
6
  import { ClientDocumentTableDefSymbol, tableIsClientDocumentTable } from './client-document-def.js'
7
7
  import { SqliteAst } from './db-schema/mod.js'
8
8
  import { stateSystemTables } from './system-tables.js'
9
- import { type TableDef, type TableDefBase } from './table-def.js'
9
+ import type { TableDef, TableDefBase } from './table-def.js'
10
10
 
11
- export * from './table-def.js'
11
+ export * from '../../EventDef.js'
12
12
  export {
13
- ClientDocumentTableDefSymbol,
14
- tableIsClientDocumentTable,
15
- clientDocument,
16
13
  type ClientDocumentTableDef,
14
+ ClientDocumentTableDefSymbol,
17
15
  type ClientDocumentTableOptions,
16
+ clientDocument,
17
+ tableIsClientDocumentTable,
18
18
  } from './client-document-def.js'
19
- export * from '../../EventDef.js'
19
+ export * from './table-def.js'
20
20
 
21
21
  export const makeState = <TStateInput extends InputState>(inputSchema: TStateInput): InternalState => {
22
22
  const inputTables: ReadonlyArray<TableDef> = Array.isArray(inputSchema.tables)