@livestore/livestore 0.4.0-dev.9 → 0.5.0-dev.0

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 (132) hide show
  1. package/README.md +0 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/QueryCache.js +1 -1
  4. package/dist/QueryCache.js.map +1 -1
  5. package/dist/SqliteDbWrapper.d.ts +5 -5
  6. package/dist/SqliteDbWrapper.d.ts.map +1 -1
  7. package/dist/SqliteDbWrapper.js +15 -13
  8. package/dist/SqliteDbWrapper.js.map +1 -1
  9. package/dist/SqliteDbWrapper.test.js +24 -6
  10. package/dist/SqliteDbWrapper.test.js.map +1 -1
  11. package/dist/effect/LiveStore.d.ts +134 -6
  12. package/dist/effect/LiveStore.d.ts.map +1 -1
  13. package/dist/effect/LiveStore.js +190 -11
  14. package/dist/effect/LiveStore.js.map +1 -1
  15. package/dist/effect/LiveStore.test.d.ts +2 -0
  16. package/dist/effect/LiveStore.test.d.ts.map +1 -0
  17. package/dist/effect/LiveStore.test.js +41 -0
  18. package/dist/effect/LiveStore.test.js.map +1 -0
  19. package/dist/effect/mod.d.ts +1 -1
  20. package/dist/effect/mod.d.ts.map +1 -1
  21. package/dist/effect/mod.js +3 -1
  22. package/dist/effect/mod.js.map +1 -1
  23. package/dist/live-queries/base-class.d.ts +129 -9
  24. package/dist/live-queries/base-class.d.ts.map +1 -1
  25. package/dist/live-queries/base-class.js +30 -4
  26. package/dist/live-queries/base-class.js.map +1 -1
  27. package/dist/live-queries/client-document-get-query.d.ts +1 -1
  28. package/dist/live-queries/client-document-get-query.d.ts.map +1 -1
  29. package/dist/live-queries/client-document-get-query.js +4 -3
  30. package/dist/live-queries/client-document-get-query.js.map +1 -1
  31. package/dist/live-queries/computed.d.ts +56 -0
  32. package/dist/live-queries/computed.d.ts.map +1 -1
  33. package/dist/live-queries/computed.js +62 -6
  34. package/dist/live-queries/computed.js.map +1 -1
  35. package/dist/live-queries/db-query.d.ts +2 -2
  36. package/dist/live-queries/db-query.d.ts.map +1 -1
  37. package/dist/live-queries/db-query.js +41 -30
  38. package/dist/live-queries/db-query.js.map +1 -1
  39. package/dist/live-queries/db-query.test.js +112 -29
  40. package/dist/live-queries/db-query.test.js.map +1 -1
  41. package/dist/live-queries/signal.d.ts +49 -0
  42. package/dist/live-queries/signal.d.ts.map +1 -1
  43. package/dist/live-queries/signal.js +51 -2
  44. package/dist/live-queries/signal.js.map +1 -1
  45. package/dist/live-queries/signal.test.js +4 -4
  46. package/dist/live-queries/signal.test.js.map +1 -1
  47. package/dist/mod.d.ts +3 -3
  48. package/dist/mod.d.ts.map +1 -1
  49. package/dist/mod.js +3 -2
  50. package/dist/mod.js.map +1 -1
  51. package/dist/reactive.d.ts +10 -10
  52. package/dist/reactive.d.ts.map +1 -1
  53. package/dist/reactive.js +29 -27
  54. package/dist/reactive.js.map +1 -1
  55. package/dist/reactive.test.js +2 -2
  56. package/dist/reactive.test.js.map +1 -1
  57. package/dist/store/StoreRegistry.d.ts +215 -0
  58. package/dist/store/StoreRegistry.d.ts.map +1 -0
  59. package/dist/store/StoreRegistry.js +267 -0
  60. package/dist/store/StoreRegistry.js.map +1 -0
  61. package/dist/store/StoreRegistry.test.d.ts +2 -0
  62. package/dist/store/StoreRegistry.test.d.ts.map +1 -0
  63. package/dist/store/StoreRegistry.test.js +384 -0
  64. package/dist/store/StoreRegistry.test.js.map +1 -0
  65. package/dist/store/create-store.d.ts +99 -22
  66. package/dist/store/create-store.d.ts.map +1 -1
  67. package/dist/store/create-store.js +85 -37
  68. package/dist/store/create-store.js.map +1 -1
  69. package/dist/store/devtools.d.ts +6 -18
  70. package/dist/store/devtools.d.ts.map +1 -1
  71. package/dist/store/devtools.js +61 -19
  72. package/dist/store/devtools.js.map +1 -1
  73. package/dist/store/store-eventstream.test.d.ts +2 -0
  74. package/dist/store/store-eventstream.test.d.ts.map +1 -0
  75. package/dist/store/store-eventstream.test.js +65 -0
  76. package/dist/store/store-eventstream.test.js.map +1 -0
  77. package/dist/store/store-types.d.ts +271 -31
  78. package/dist/store/store-types.d.ts.map +1 -1
  79. package/dist/store/store-types.js +41 -1
  80. package/dist/store/store-types.js.map +1 -1
  81. package/dist/store/store-types.test.d.ts +2 -0
  82. package/dist/store/store-types.test.d.ts.map +1 -0
  83. package/dist/store/store-types.test.js +39 -0
  84. package/dist/store/store-types.test.js.map +1 -0
  85. package/dist/store/store.d.ts +255 -68
  86. package/dist/store/store.d.ts.map +1 -1
  87. package/dist/store/store.js +533 -186
  88. package/dist/store/store.js.map +1 -1
  89. package/dist/utils/dev.d.ts.map +1 -1
  90. package/dist/utils/dev.js +1 -1
  91. package/dist/utils/dev.js.map +1 -1
  92. package/dist/utils/stack-info.d.ts.map +1 -1
  93. package/dist/utils/stack-info.js +3 -2
  94. package/dist/utils/stack-info.js.map +1 -1
  95. package/dist/utils/tests/fixture.d.ts +46 -39
  96. package/dist/utils/tests/fixture.d.ts.map +1 -1
  97. package/dist/utils/tests/fixture.js +7 -0
  98. package/dist/utils/tests/fixture.js.map +1 -1
  99. package/dist/utils/tests/otel.d.ts.map +1 -1
  100. package/dist/utils/tests/otel.js +5 -5
  101. package/dist/utils/tests/otel.js.map +1 -1
  102. package/package.json +50 -18
  103. package/src/QueryCache.ts +1 -1
  104. package/src/SqliteDbWrapper.test.ts +30 -6
  105. package/src/SqliteDbWrapper.ts +21 -17
  106. package/src/ambient.d.ts +0 -7
  107. package/src/effect/LiveStore.test.ts +61 -0
  108. package/src/effect/LiveStore.ts +426 -22
  109. package/src/effect/mod.ts +13 -1
  110. package/src/live-queries/__snapshots__/db-query.test.ts.snap +814 -178
  111. package/src/live-queries/base-class.ts +162 -32
  112. package/src/live-queries/client-document-get-query.ts +6 -4
  113. package/src/live-queries/computed.ts +65 -8
  114. package/src/live-queries/db-query.test.ts +168 -30
  115. package/src/live-queries/db-query.ts +58 -43
  116. package/src/live-queries/signal.test.ts +5 -4
  117. package/src/live-queries/signal.ts +52 -3
  118. package/src/mod.ts +19 -2
  119. package/src/reactive.test.ts +3 -2
  120. package/src/reactive.ts +58 -49
  121. package/src/store/StoreRegistry.test.ts +543 -0
  122. package/src/store/StoreRegistry.ts +429 -0
  123. package/src/store/create-store.ts +227 -74
  124. package/src/store/devtools.ts +305 -261
  125. package/src/store/store-eventstream.test.ts +123 -0
  126. package/src/store/store-types.test.ts +52 -0
  127. package/src/store/store-types.ts +318 -44
  128. package/src/store/store.ts +695 -283
  129. package/src/utils/dev.ts +3 -4
  130. package/src/utils/stack-info.ts +5 -2
  131. package/src/utils/tests/fixture.ts +9 -1
  132. package/src/utils/tests/otel.ts +8 -7
package/src/mod.ts CHANGED
@@ -35,14 +35,31 @@ export {
35
35
  signal,
36
36
  } from './live-queries/mod.ts'
37
37
  export { emptyDebugInfo, SqliteDbWrapper } from './SqliteDbWrapper.ts'
38
- export { type CreateStoreOptions, createStore, createStorePromise } from './store/create-store.ts'
38
+ export {
39
+ type CreateStoreOptions,
40
+ type CreateStoreOptionsPromise,
41
+ createStore,
42
+ createStorePromise,
43
+ } from './store/create-store.ts'
44
+ export { type RegistryStoreOptions, StoreRegistry, storeOptions } from './store/StoreRegistry.ts'
39
45
  export { Store } from './store/store.ts'
40
- export type { OtelOptions, QueryDebugInfo, RefreshReason, Unsubscribe } from './store/store-types.ts'
41
46
  export {
47
+ isLiveQueryDef,
48
+ isLiveQueryInstance,
49
+ isQueryable,
42
50
  type LiveStoreContext,
43
51
  type LiveStoreContextRunning,
44
52
  makeShutdownDeferred,
53
+ type OtelOptions,
54
+ type Queryable,
55
+ type QueryDebugInfo,
56
+ type RefreshReason,
45
57
  type ShutdownDeferred,
58
+ type StoreInternals,
59
+ StoreInternalsSymbol,
60
+ type SubscribeOptions,
61
+ type SyncStatus,
62
+ type Unsubscribe,
46
63
  } from './store/store-types.ts'
47
64
  export { exposeDebugUtils } from './utils/dev.ts'
48
65
  export * from './utils/stack-info.ts'
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest'
2
+
2
3
  import type { DebugRefreshReasonBase, DebugThunkInfo } from './reactive.ts'
3
4
  import { ReactiveGraph } from './reactive.ts'
4
5
 
@@ -142,7 +143,7 @@ describe('a trivial graph', () => {
142
143
  expect(numberOfEffectRuns).toBe(0)
143
144
  const effect = graph.makeEffect((get) => {
144
145
  // establish a dependency on thunk c and mutate an outside value
145
- expect(get(c)).toBe(aHasChanged ? 3 : 4)
146
+ expect(get(c)).toBe(aHasChanged === true ? 3 : 4)
146
147
  numberOfEffectRuns++
147
148
  })
148
149
 
@@ -562,7 +563,7 @@ describe('bug fix: node corruption protection', () => {
562
563
 
563
564
  let firstRun = true
564
565
  const effect = graph.makeEffect((get) => {
565
- if (firstRun) {
566
+ if (firstRun !== undefined) {
566
567
  firstRun = false
567
568
  graph.destroyNode(thunk1) // Destroy dependency mid-execution
568
569
  }
package/src/reactive.ts CHANGED
@@ -21,20 +21,17 @@
21
21
  // is maintained eagerly as edges are added and removed.)
22
22
  // - At every thunk we check value equality with the previous value and cutoff propagation if possible.
23
23
 
24
+ import type * as otel from '@opentelemetry/api'
25
+
24
26
  import { BoundArray } from '@livestore/common'
25
27
  import { deepEqual, omitUndefineds, shouldNeverHappen } from '@livestore/utils'
26
- import type { Types } from '@livestore/utils/effect'
27
- import type * as otel from '@opentelemetry/api'
28
+ import { Schema, type Types } from '@livestore/utils/effect'
28
29
  // import { getDurationMsFromSpan } from './otel.ts'
29
30
 
30
31
  export const NOT_REFRESHED_YET = Symbol.for('NOT_REFRESHED_YET')
31
32
  export type NOT_REFRESHED_YET = typeof NOT_REFRESHED_YET
32
33
 
33
- export type GetAtom = <T>(
34
- atom: Atom<T, any, any>,
35
- otelContext?: otel.Context | undefined,
36
- debugRefreshReason?: TODO | undefined,
37
- ) => T
34
+ export type GetAtom = <T>(atom: Atom<T, any, any>, otelContext?: otel.Context, debugRefreshReason?: TODO) => T
38
35
 
39
36
  export type Ref<T, TContext, TDebugRefreshReason extends DebugRefreshReason> = {
40
37
  _tag: 'ref'
@@ -47,7 +44,7 @@ export type Ref<T, TContext, TDebugRefreshReason extends DebugRefreshReason> = {
47
44
  super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>>
48
45
  label?: string | undefined
49
46
  /** Container for meta information (e.g. the LiveStore Store) */
50
- meta?: any | undefined
47
+ meta?: unknown
51
48
  equal: (a: T, b: T) => boolean
52
49
  refreshes: number
53
50
  }
@@ -63,7 +60,7 @@ export type Thunk<TResult, TContext, TDebugRefreshReason extends DebugRefreshRea
63
60
  super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>>
64
61
  label?: string | undefined
65
62
  /** Container for meta information (e.g. the LiveStore Store) */
66
- meta?: any | undefined
63
+ meta?: unknown
67
64
  equal: (a: TResult, b: TResult) => boolean
68
65
  recomputations: number
69
66
 
@@ -78,7 +75,7 @@ export type Effect<TDebugRefreshReason extends DebugRefreshReason> = {
78
75
  _tag: 'effect'
79
76
  id: string
80
77
  isDestroyed: boolean
81
- doEffect: (otelContext?: otel.Context | undefined, debugRefreshReason?: TDebugRefreshReason | undefined) => void
78
+ doEffect: (otelContext?: otel.Context, debugRefreshReason?: TDebugRefreshReason) => void
82
79
  sub: Set<Atom<any, TODO, TODO>>
83
80
  label?: string | undefined
84
81
  invocations: number
@@ -91,7 +88,7 @@ export type Node<T, TContext, TDebugRefreshReason extends DebugRefreshReason> =
91
88
  export const isThunk = <T, TContext, TDebugRefreshReason extends DebugRefreshReason>(
92
89
  obj: unknown,
93
90
  ): obj is Thunk<T, TContext, TDebugRefreshReason> => {
94
- return typeof obj === 'object' && obj !== null && '_tag' in obj && (obj as any)._tag === 'thunk'
91
+ return typeof obj === 'object' && obj !== null && '_tag' in obj && (obj as { _tag: unknown })._tag === 'thunk'
95
92
  }
96
93
 
97
94
  export type DebugThunkInfo<T extends string = string> = {
@@ -203,9 +200,7 @@ export class ReactiveGraph<
203
200
 
204
201
  debugRefreshInfos: BoundArray<RefreshDebugInfo<TDebugRefreshReason, TDebugThunkInfo>> = new BoundArray(200)
205
202
 
206
- private currentDebugRefresh:
207
- | { refreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[]; startMs: DOMHighResTimeStamp }
208
- | undefined
203
+ private currentDebugRefresh: { refreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[]; startMs: number } | undefined
209
204
 
210
205
  private deferredEffects: Map<Effect<TDebugRefreshReason>, Set<TDebugRefreshReason>> = new Map()
211
206
 
@@ -247,13 +242,11 @@ export class ReactiveGraph<
247
242
  otelContext: otel.Context | undefined,
248
243
  debugRefreshReason: TDebugRefreshReason | undefined,
249
244
  ) => T,
250
- options?:
251
- | {
252
- label?: string
253
- meta?: any
254
- equal?: (a: T, b: T) => boolean
255
- }
256
- | undefined,
245
+ options?: {
246
+ label?: string
247
+ meta?: any
248
+ equal?: (a: T, b: T) => boolean
249
+ },
257
250
  ): Thunk<T, TContext, TDebugRefreshReason> {
258
251
  const thunk: Thunk<T, TContext, TDebugRefreshReason> = {
259
252
  _tag: 'thunk',
@@ -262,10 +255,10 @@ export class ReactiveGraph<
262
255
  isDirty: true,
263
256
  isDestroyed: false,
264
257
  computeResult: (otelContext, debugRefreshReason) => {
265
- if (thunk.isDirty) {
258
+ if (thunk.isDirty === true) {
266
259
  const neededCurrentRefresh = this.currentDebugRefresh === undefined
267
260
  let localDebugRefresh: { refreshedAtoms: any[]; startMs: number } | undefined
268
- if (neededCurrentRefresh) {
261
+ if (neededCurrentRefresh === true) {
269
262
  // Use local variable to prevent corruption from nested computations
270
263
  localDebugRefresh = { refreshedAtoms: [], startMs: performance.now() }
271
264
  this.currentDebugRefresh = localDebugRefresh
@@ -292,7 +285,7 @@ export class ReactiveGraph<
292
285
  debugRefreshReason,
293
286
  )
294
287
 
295
- const resultChanged = thunk.equal(thunk.previousResult as T, result) === false
288
+ const resultChanged = !thunk.equal(thunk.previousResult as T, result)
296
289
 
297
290
  const debugInfoForAtom = {
298
291
  atom: serializeAtom(thunk, false),
@@ -302,7 +295,7 @@ export class ReactiveGraph<
302
295
 
303
296
  // Use currentDebugRefresh if available (could be from parent or local)
304
297
  const debugRefresh = localDebugRefresh ?? this.currentDebugRefresh
305
- if (debugRefresh) {
298
+ if (debugRefresh !== undefined) {
306
299
  debugRefresh.refreshedAtoms.push(debugInfoForAtom)
307
300
  }
308
301
 
@@ -310,7 +303,7 @@ export class ReactiveGraph<
310
303
  thunk.previousResult = result
311
304
  thunk.recomputations++
312
305
 
313
- if (neededCurrentRefresh && localDebugRefresh) {
306
+ if (neededCurrentRefresh === true && localDebugRefresh !== undefined) {
314
307
  // Use local reference which can't be corrupted by nested calls
315
308
  const refreshedAtoms = localDebugRefresh.refreshedAtoms
316
309
  const durationMs = performance.now() - localDebugRefresh.startMs
@@ -385,7 +378,7 @@ export class ReactiveGraph<
385
378
  otelContext: otel.Context | undefined,
386
379
  debugRefreshReason: DebugRefreshReason | undefined,
387
380
  ) => void,
388
- options?: { label?: string } | undefined,
381
+ options?: { label?: string },
389
382
  ): Effect<TDebugRefreshReason> {
390
383
  const effect: Effect<TDebugRefreshReason> = {
391
384
  _tag: 'effect',
@@ -423,26 +416,22 @@ export class ReactiveGraph<
423
416
  setRef<T>(
424
417
  ref: Ref<T, TContext, TDebugRefreshReason>,
425
418
  val: T,
426
- options?:
427
- | {
428
- skipRefresh?: boolean
429
- debugRefreshReason?: TDebugRefreshReason
430
- otelContext?: otel.Context
431
- }
432
- | undefined,
419
+ options?: {
420
+ skipRefresh?: boolean
421
+ debugRefreshReason?: TDebugRefreshReason
422
+ otelContext?: otel.Context
423
+ },
433
424
  ) {
434
425
  this.setRefs([[ref, val]], options)
435
426
  }
436
427
 
437
428
  setRefs<T>(
438
429
  refs: [Ref<T, TContext, TDebugRefreshReason>, T][],
439
- options?:
440
- | {
441
- skipRefresh?: boolean
442
- debugRefreshReason?: TDebugRefreshReason
443
- otelContext?: otel.Context
444
- }
445
- | undefined,
430
+ options?: {
431
+ skipRefresh?: boolean
432
+ debugRefreshReason?: TDebugRefreshReason
433
+ otelContext?: otel.Context
434
+ },
446
435
  ) {
447
436
  const effectsToRefresh = new Set<Effect<TDebugRefreshReason>>()
448
437
  for (const [ref, val] of refs) {
@@ -452,7 +441,7 @@ export class ReactiveGraph<
452
441
  markSuperCompDirtyRec(ref, effectsToRefresh)
453
442
  }
454
443
 
455
- if (options?.skipRefresh) {
444
+ if (options?.skipRefresh === true) {
456
445
  for (const effect of effectsToRefresh) {
457
446
  if (this.deferredEffects.has(effect) === false) {
458
447
  this.deferredEffects.set(effect, new Set())
@@ -598,11 +587,11 @@ const compute = <T>(
598
587
  debugRefreshReason: DebugRefreshReason | undefined,
599
588
  ): T => {
600
589
  // const __getResult = atom._tag === 'thunk' ? atom.__getResult.toString() : ''
601
- if (atom.isDestroyed) {
590
+ if (atom.isDestroyed === true) {
602
591
  shouldNeverHappen(`LiveStore Error: Attempted to compute destroyed ${atom._tag} (${atom.id}): ${atom.label ?? ''}`)
603
592
  }
604
593
 
605
- if (atom.isDirty) {
594
+ if (atom.isDirty === true) {
606
595
  // console.log('atom is dirty', atom.id, atom.label ?? '', atom._tag, __getResult)
607
596
  const result = atom.computeResult(otelContext, debugRefreshReason)
608
597
  atom.isDirty = false
@@ -641,11 +630,31 @@ const serializeAtom = (atom: Atom<any, unknown, any>, includeResult: boolean): S
641
630
  super_.push(a.id)
642
631
  }
643
632
 
644
- const previousResult: EncodedOption<string> = includeResult
645
- ? encodedOptionSome(
646
- atom.previousResult === NOT_REFRESHED_YET ? '"SYMBOL_NOT_REFRESHED_YET"' : JSON.stringify(atom.previousResult),
647
- )
648
- : encodedOptionNone()
633
+ const previousResult: EncodedOption<string> =
634
+ includeResult === true
635
+ ? encodedOptionSome(
636
+ atom.previousResult === NOT_REFRESHED_YET
637
+ ? '"SYMBOL_NOT_REFRESHED_YET"'
638
+ : JSON.stringify(atom.previousResult, (key, nestedValue) => {
639
+ if (Schema.isSchema(nestedValue) === false) return nestedValue
640
+
641
+ // Query inputs carry schemas for result decoding, but graph snapshots use previousResult
642
+ // to explain reactive data flow. Omitting that field preserves the stable query shape
643
+ // without embedding Effect full schema object graph.
644
+ if (key === 'schema') return undefined
645
+
646
+ const annotations = Schema.resolveAnnotations(nestedValue)
647
+
648
+ return omitUndefineds({
649
+ _tag: 'Schema',
650
+ ast: nestedValue.ast._tag,
651
+ identifier: annotations?.identifier,
652
+ title: annotations?.title,
653
+ hash: Schema.hash(nestedValue),
654
+ })
655
+ }),
656
+ )
657
+ : encodedOptionNone()
649
658
 
650
659
  if (atom._tag === 'ref') {
651
660
  return {