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

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 (35) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +1 -1
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  4. package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
  5. package/dist/devtools/devtools-messages-common.d.ts +6 -6
  6. package/dist/devtools/devtools-messages-leader.d.ts +24 -24
  7. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  8. package/dist/leader-thread/LeaderSyncProcessor.js +18 -8
  9. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  10. package/dist/schema/EventSequenceNumber.d.ts +4 -1
  11. package/dist/schema/EventSequenceNumber.d.ts.map +1 -1
  12. package/dist/schema/EventSequenceNumber.js +4 -1
  13. package/dist/schema/EventSequenceNumber.js.map +1 -1
  14. package/dist/schema/EventSequenceNumber.test.js +2 -2
  15. package/dist/schema/state/sqlite/column-def.js +9 -3
  16. package/dist/schema/state/sqlite/column-def.js.map +1 -1
  17. package/dist/schema/state/sqlite/table-def.test.js +36 -0
  18. package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
  19. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  20. package/dist/sync/ClientSessionSyncProcessor.js +1 -0
  21. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  22. package/dist/sync/syncstate.test.js +16 -15
  23. package/dist/sync/syncstate.test.js.map +1 -1
  24. package/dist/version.d.ts +1 -1
  25. package/dist/version.js +1 -1
  26. package/package.json +4 -4
  27. package/src/ClientSessionLeaderThreadProxy.ts +1 -1
  28. package/src/leader-thread/LeaderSyncProcessor.ts +31 -12
  29. package/src/schema/EventSequenceNumber.test.ts +2 -2
  30. package/src/schema/EventSequenceNumber.ts +5 -2
  31. package/src/schema/state/sqlite/column-def.ts +11 -3
  32. package/src/schema/state/sqlite/table-def.test.ts +48 -0
  33. package/src/sync/ClientSessionSyncProcessor.ts +1 -0
  34. package/src/sync/syncstate.test.ts +17 -15
  35. package/src/version.ts +1 -1
@@ -23,7 +23,7 @@ Vitest.describe('EventSequenceNumber', () => {
23
23
  expect(EventSequenceNumber.nextPair({ seqNum: e_0_0, isClient: false, rebaseGeneration: 1 }).seqNum).toStrictEqual({
24
24
  global: 1,
25
25
  client: 0,
26
- rebaseGeneration: 0,
26
+ rebaseGeneration: 1,
27
27
  })
28
28
  expect(EventSequenceNumber.nextPair({ seqNum: e_0_0, isClient: true, rebaseGeneration: 1 }).seqNum).toStrictEqual({
29
29
  global: 0,
@@ -35,7 +35,7 @@ Vitest.describe('EventSequenceNumber', () => {
35
35
  expect(EventSequenceNumber.nextPair({ seqNum: e_0_0_g1, isClient: false }).seqNum).toStrictEqual({
36
36
  global: 1,
37
37
  client: 0,
38
- rebaseGeneration: 0,
38
+ rebaseGeneration: 2,
39
39
  })
40
40
  })
41
41
 
@@ -23,7 +23,7 @@ export type EventSequenceNumber = {
23
23
  client: ClientEventSequenceNumber
24
24
  /**
25
25
  * Generation integer that is incremented whenever the client rebased.
26
- * Starts from and resets to 0 for each global sequence number.
26
+ * Remains constant for all subsequent events until another rebase occurs.
27
27
  */
28
28
  rebaseGeneration: number
29
29
  }
@@ -167,6 +167,9 @@ export const make = (seqNum: EventSequenceNumberInput): EventSequenceNumber => {
167
167
  })
168
168
  }
169
169
 
170
+ /**
171
+ * Computes the next event sequence/parent pair.
172
+ */
170
173
  export const nextPair = ({
171
174
  seqNum,
172
175
  isClient,
@@ -191,7 +194,7 @@ export const nextPair = ({
191
194
  seqNum: {
192
195
  global: (seqNum.global + 1) as any as GlobalEventSequenceNumber,
193
196
  client: clientDefault,
194
- rebaseGeneration: rebaseGenerationDefault,
197
+ rebaseGeneration: rebaseGeneration ?? seqNum.rebaseGeneration,
195
198
  },
196
199
  // NOTE we always point to `client: 0` for non-clientOnly events
197
200
  parentSeqNum: { global: seqNum.global, client: clientDefault, rebaseGeneration: seqNum.rebaseGeneration },
@@ -206,10 +206,18 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
206
206
  const stripNullable = (ast: SchemaAST.AST): SchemaAST.AST => {
207
207
  if (!SchemaAST.isUnion(ast)) return ast
208
208
 
209
- // Find non-null/undefined type
210
- const core = ast.types.find(
209
+ // Filter out null/undefined members while preserving any annotations on the union
210
+ const coreTypes = ast.types.filter(
211
211
  (type) => !(SchemaAST.isLiteral(type) && type.literal === null) && !SchemaAST.isUndefinedKeyword(type),
212
212
  )
213
213
 
214
- return core || ast
214
+ if (coreTypes.length === 0 || coreTypes.length === ast.types.length) {
215
+ return ast
216
+ }
217
+
218
+ if (coreTypes.length === 1) {
219
+ return coreTypes[0]!
220
+ }
221
+
222
+ return SchemaAST.Union.make(coreTypes, ast.annotations)
215
223
  }
@@ -300,4 +300,52 @@ describe('table function overloads', () => {
300
300
  expect(userTable.sqliteDef.columns.metadata.columnType).toBe('text')
301
301
  expect(userTable.sqliteDef.columns.metadata.nullable).toBe(true)
302
302
  })
303
+
304
+ it('supports discriminated unions with parsed JSON payloads', () => {
305
+ const CircleDataSchema = Schema.Struct({
306
+ radius: Schema.Number,
307
+ })
308
+ const CircleSchema = Schema.Struct({
309
+ kind: Schema.Literal('circle'),
310
+ data: Schema.parseJson(CircleDataSchema),
311
+ })
312
+
313
+ const SquareDataSchema = Schema.Struct({
314
+ sideLength: Schema.Number,
315
+ })
316
+ const SquareSchema = Schema.Struct({
317
+ kind: Schema.Literal('square'),
318
+ data: Schema.parseJson(SquareDataSchema),
319
+ })
320
+
321
+ const ShapeSchema = Schema.Union(CircleSchema, SquareSchema)
322
+
323
+ const shapes = State.SQLite.table({
324
+ name: 'shapes',
325
+ schema: ShapeSchema,
326
+ })
327
+
328
+ expect(shapes.sqliteDef.columns.kind.columnType).toBe('text')
329
+
330
+ const kindSchema = shapes.sqliteDef.columns.kind.schema.toString()
331
+ expect(kindSchema).toContain('"circle" | "square"')
332
+
333
+ expect(() =>
334
+ shapes
335
+ .insert({
336
+ kind: 'square',
337
+ data: { sideLength: 10 },
338
+ })
339
+ .asSql(),
340
+ ).not.toThrow()
341
+
342
+ expect(() =>
343
+ shapes
344
+ .insert({
345
+ kind: 'circle',
346
+ data: { radius: 5 },
347
+ })
348
+ .asSql(),
349
+ ).not.toThrow()
350
+ })
303
351
  })
@@ -115,6 +115,7 @@ export const makeClientSessionSyncProcessor = ({
115
115
  const nextNumPair = EventSequenceNumber.nextPair({
116
116
  seqNum: baseEventSequenceNumber,
117
117
  isClient: eventDef.options.clientOnly,
118
+ rebaseGeneration: baseEventSequenceNumber.rebaseGeneration,
118
119
  })
119
120
  baseEventSequenceNumber = nextNumPair.seqNum
120
121
  return new LiveStoreEvent.EncodedWithMeta(
@@ -5,15 +5,16 @@ import * as LiveStoreEvent from '../schema/LiveStoreEvent.ts'
5
5
  import * as SyncState from './syncstate.ts'
6
6
 
7
7
  class TestEvent extends LiveStoreEvent.EncodedWithMeta {
8
- public readonly payload: string
9
- public readonly isClient: boolean
10
- constructor(
8
+ public payload = 'uninitialized'
9
+ public isClient = false
10
+
11
+ static new = (
11
12
  seqNum: EventSequenceNumber.EventSequenceNumberInput,
12
13
  parentSeqNum: EventSequenceNumber.EventSequenceNumberInput,
13
14
  payload: string,
14
15
  isClient: boolean,
15
- ) {
16
- super({
16
+ ) => {
17
+ const event = new TestEvent({
17
18
  seqNum: EventSequenceNumber.make(seqNum),
18
19
  parentSeqNum: EventSequenceNumber.make(parentSeqNum),
19
20
  name: 'a',
@@ -21,8 +22,9 @@ class TestEvent extends LiveStoreEvent.EncodedWithMeta {
21
22
  clientId: 'static-local-id',
22
23
  sessionId: 'static-session-id',
23
24
  })
24
- this.payload = payload
25
- this.isClient = isClient
25
+ event.payload = payload
26
+ event.isClient = isClient
27
+ return event
26
28
  }
27
29
 
28
30
  rebase_ = (parentSeqNum: EventSequenceNumber.EventSequenceNumber, rebaseGeneration: number) => {
@@ -34,13 +36,13 @@ class TestEvent extends LiveStoreEvent.EncodedWithMeta {
34
36
  // toString = () => this.toJSON()
35
37
  }
36
38
 
37
- const e0_1 = new TestEvent({ global: 0, client: 1 }, EventSequenceNumber.ROOT, 'a', true)
38
- const e1_0 = new TestEvent({ global: 1, client: 0 }, EventSequenceNumber.ROOT, 'a', false)
39
- const e1_1 = new TestEvent({ global: 1, client: 1 }, e1_0.seqNum, 'a', true)
40
- const e1_2 = new TestEvent({ global: 1, client: 2 }, e1_1.seqNum, 'a', true)
41
- const e1_3 = new TestEvent({ global: 1, client: 3 }, e1_2.seqNum, 'a', true)
42
- const e2_0 = new TestEvent({ global: 2, client: 0 }, e1_0.seqNum, 'a', false)
43
- const e2_1 = new TestEvent({ global: 2, client: 1 }, e2_0.seqNum, 'a', true)
39
+ const e0_1 = TestEvent.new({ global: 0, client: 1 }, EventSequenceNumber.ROOT, 'a', true)
40
+ const e1_0 = TestEvent.new({ global: 1, client: 0 }, EventSequenceNumber.ROOT, 'a', false)
41
+ const e1_1 = TestEvent.new({ global: 1, client: 1 }, e1_0.seqNum, 'a', true)
42
+ const e1_2 = TestEvent.new({ global: 1, client: 2 }, e1_1.seqNum, 'a', true)
43
+ const e1_3 = TestEvent.new({ global: 1, client: 3 }, e1_2.seqNum, 'a', true)
44
+ const e2_0 = TestEvent.new({ global: 2, client: 0 }, e1_0.seqNum, 'a', false)
45
+ const e2_1 = TestEvent.new({ global: 2, client: 1 }, e2_0.seqNum, 'a', true)
44
46
 
45
47
  const isEqualEvent = LiveStoreEvent.isEqualEncoded
46
48
 
@@ -340,7 +342,7 @@ describe('syncstate', () => {
340
342
  })
341
343
 
342
344
  it('should rebase different event with same id', () => {
343
- const e2_0_b = new TestEvent({ global: 1, client: 0 }, e1_0.seqNum, '1_0_b', false)
345
+ const e2_0_b = TestEvent.new({ global: 1, client: 0 }, e1_0.seqNum, '1_0_b', false)
344
346
  const syncState = new SyncState.SyncState({
345
347
  pending: [e2_0_b],
346
348
  upstreamHead: EventSequenceNumber.ROOT,
package/src/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // import packageJson from '../package.json' with { type: 'json' }
3
3
  // export const liveStoreVersion = packageJson.version
4
4
 
5
- export const liveStoreVersion = '0.4.0-dev.10' as const
5
+ export const liveStoreVersion = '0.4.0-dev.11' as const
6
6
 
7
7
  /**
8
8
  * This version number is incremented whenever the internal storage format changes in a breaking way.