@livestore/livestore 0.0.54-dev.19 → 0.0.54-dev.21

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 (73) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/MainDatabaseWrapper.d.ts +5 -3
  3. package/dist/MainDatabaseWrapper.d.ts.map +1 -1
  4. package/dist/MainDatabaseWrapper.js +3 -3
  5. package/dist/MainDatabaseWrapper.js.map +1 -1
  6. package/dist/__tests__/react/fixture.d.ts +3 -3
  7. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  8. package/dist/__tests__/react/fixture.js +10 -8
  9. package/dist/__tests__/react/fixture.js.map +1 -1
  10. package/dist/effect/LiveStore.d.ts +2 -3
  11. package/dist/effect/LiveStore.d.ts.map +1 -1
  12. package/dist/effect/LiveStore.js +4 -2
  13. package/dist/effect/LiveStore.js.map +1 -1
  14. package/dist/global-state.d.ts +1 -1
  15. package/dist/global-state.d.ts.map +1 -1
  16. package/dist/global-state.js +2 -2
  17. package/dist/global-state.js.map +1 -1
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/react/LiveStoreProvider.d.ts +3 -4
  23. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  24. package/dist/react/LiveStoreProvider.js +8 -13
  25. package/dist/react/LiveStoreProvider.js.map +1 -1
  26. package/dist/react/useRow.d.ts +2 -2
  27. package/dist/react/useRow.d.ts.map +1 -1
  28. package/dist/react/useRow.js +3 -3
  29. package/dist/react/useRow.js.map +1 -1
  30. package/dist/react/useRow.test.js +21 -21
  31. package/dist/react/useRow.test.js.map +1 -1
  32. package/dist/react/useTemporaryQuery.js +1 -1
  33. package/dist/react/useTemporaryQuery.js.map +1 -1
  34. package/dist/reactiveQueries/base-class.d.ts +6 -6
  35. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  36. package/dist/reactiveQueries/base-class.js +3 -3
  37. package/dist/reactiveQueries/base-class.js.map +1 -1
  38. package/dist/reactiveQueries/graphql.d.ts +8 -8
  39. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  40. package/dist/reactiveQueries/graphql.js +10 -10
  41. package/dist/reactiveQueries/graphql.js.map +1 -1
  42. package/dist/reactiveQueries/js.d.ts +6 -6
  43. package/dist/reactiveQueries/js.d.ts.map +1 -1
  44. package/dist/reactiveQueries/js.js +8 -8
  45. package/dist/reactiveQueries/js.js.map +1 -1
  46. package/dist/reactiveQueries/sql.d.ts +8 -8
  47. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  48. package/dist/reactiveQueries/sql.js +11 -11
  49. package/dist/reactiveQueries/sql.js.map +1 -1
  50. package/dist/row-query.d.ts +2 -2
  51. package/dist/row-query.d.ts.map +1 -1
  52. package/dist/row-query.js +2 -2
  53. package/dist/row-query.js.map +1 -1
  54. package/dist/store.d.ts +17 -16
  55. package/dist/store.d.ts.map +1 -1
  56. package/dist/store.js +46 -50
  57. package/dist/store.js.map +1 -1
  58. package/package.json +5 -12
  59. package/src/MainDatabaseWrapper.ts +7 -6
  60. package/src/__tests__/react/fixture.tsx +11 -9
  61. package/src/effect/LiveStore.ts +6 -5
  62. package/src/global-state.ts +2 -2
  63. package/src/index.ts +7 -2
  64. package/src/react/LiveStoreProvider.tsx +10 -18
  65. package/src/react/useRow.test.tsx +21 -21
  66. package/src/react/useRow.ts +5 -5
  67. package/src/react/useTemporaryQuery.ts +2 -2
  68. package/src/reactiveQueries/base-class.ts +9 -9
  69. package/src/reactiveQueries/graphql.ts +19 -15
  70. package/src/reactiveQueries/js.ts +12 -12
  71. package/src/reactiveQueries/sql.ts +18 -18
  72. package/src/row-query.ts +6 -6
  73. package/src/store.ts +75 -69
package/src/store.ts CHANGED
@@ -8,18 +8,18 @@ import type {
8
8
  } from '@livestore/common'
9
9
  import { Devtools, getExecArgsFromMutation, prepareBindValues } from '@livestore/common'
10
10
  import { version as liveStoreVersion } from '@livestore/common/package.json'
11
- import type { LiveStoreSchema, MutationEvent, MutationEventSchema } from '@livestore/common/schema'
12
- import { makeMutationEventSchema } from '@livestore/common/schema'
13
- import { assertNever, isPromise, makeNoopTracer, ref, shouldNeverHappen } from '@livestore/utils'
11
+ import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
12
+ import { makeMutationEventSchemaMemo } from '@livestore/common/schema'
13
+ import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen } from '@livestore/utils'
14
14
  import { Effect, Schema, Stream } from '@livestore/utils/effect'
15
15
  import * as otel from '@opentelemetry/api'
16
16
  import type { GraphQLSchema } from 'graphql'
17
17
 
18
- import { globalDbGraph } from './global-state.js'
18
+ import { globalReactivityGraph } from './global-state.js'
19
19
  import { MainDatabaseWrapper } from './MainDatabaseWrapper.js'
20
20
  import type { StackInfo } from './react/utils/stack-info.js'
21
- import type { DebugRefreshReasonBase, ReactiveGraph, Ref } from './reactive.js'
22
- import type { DbContext, DbGraph, LiveQuery } from './reactiveQueries/base-class.js'
21
+ import type { DebugRefreshReasonBase, Ref } from './reactive.js'
22
+ import type { LiveQuery, QueryContext, ReactivityGraph } from './reactiveQueries/base-class.js'
23
23
  import { downloadBlob } from './utils/dev.js'
24
24
  import { getDurationMsFromSpan } from './utils/otel.js'
25
25
 
@@ -34,17 +34,21 @@ export type GraphQLOptions<TContext> = {
34
34
  makeContext: (db: MainDatabaseWrapper, tracer: otel.Tracer) => TContext
35
35
  }
36
36
 
37
+ export type OtelOptions = {
38
+ tracer: otel.Tracer
39
+ rootSpanContext: otel.Context
40
+ }
41
+
37
42
  export type StoreOptions<
38
43
  TGraphQLContext extends BaseGraphQLContext,
39
44
  TSchema extends LiveStoreSchema = LiveStoreSchema,
40
45
  > = {
41
46
  adapter: StoreAdapter
42
47
  schema: TSchema
48
+ // TODO remove graphql-related stuff from store and move to GraphQL query directly
43
49
  graphQLOptions?: GraphQLOptions<TGraphQLContext>
44
- otelTracer: otel.Tracer
45
- otelRootSpanContext: otel.Context
46
- dbGraph: DbGraph
47
- mutationEventSchema: MutationEventSchema<any>
50
+ otelOptions: OtelOptions
51
+ reactivityGraph: ReactivityGraph
48
52
  disableDevtools?: boolean
49
53
  }
50
54
 
@@ -100,11 +104,8 @@ export class Store<
100
104
  TSchema extends LiveStoreSchema = LiveStoreSchema,
101
105
  > {
102
106
  id = uniqueStoreId()
103
- graph: ReactiveGraph<RefreshReason, QueryDebugInfo, DbContext>
107
+ reactivityGraph: ReactivityGraph
104
108
  mainDbWrapper: MainDatabaseWrapper
105
- // TODO refactor
106
- // _proxyDb: InMemoryDatabase
107
- // TODO
108
109
  adapter: StoreAdapter
109
110
  schema: LiveStoreSchema
110
111
  graphQLSchema?: GraphQLSchema
@@ -114,7 +115,7 @@ export class Store<
114
115
  * Note we're using `Ref<null>` here as we don't care about the value but only about *that* something has changed.
115
116
  * This only works in combination with `equal: () => false` which will always trigger a refresh.
116
117
  */
117
- tableRefs: { [key: string]: Ref<null, DbContext, RefreshReason> }
118
+ tableRefs: { [key: string]: Ref<null, QueryContext, RefreshReason> }
118
119
 
119
120
  // TODO remove this temporary solution and find a better way to avoid re-processing the same mutation
120
121
  __processedMutationIds = new Set<string>()
@@ -129,32 +130,33 @@ export class Store<
129
130
  adapter,
130
131
  schema,
131
132
  graphQLOptions,
132
- dbGraph,
133
- otelTracer,
134
- otelRootSpanContext,
135
- mutationEventSchema,
133
+ reactivityGraph,
134
+ otelOptions,
136
135
  disableDevtools,
137
136
  }: StoreOptions<TGraphQLContext, TSchema>) {
138
- this.mainDbWrapper = new MainDatabaseWrapper({ otelTracer, otelRootSpanContext, db: adapter.mainDb })
137
+ this.mainDbWrapper = new MainDatabaseWrapper({ otel: otelOptions, db: adapter.mainDb })
139
138
  this.adapter = adapter
140
139
  this.schema = schema
141
140
 
142
141
  // TODO refactor
143
- this.__mutationEventSchema = mutationEventSchema
144
- // this.mutationEventSchema = makeMutationEventSchema(Object.fromEntries(schema.mutations.entries()) as any)
142
+ this.__mutationEventSchema = makeMutationEventSchemaMemo(schema)
145
143
 
146
144
  // TODO generalize the `tableRefs` concept to allow finer-grained refs
147
145
  this.tableRefs = {}
148
146
  this.activeQueries = new ReferenceCountedSet()
149
147
 
150
- const mutationsSpan = otelTracer.startSpan('LiveStore:mutations', {}, otelRootSpanContext)
148
+ const mutationsSpan = otelOptions.tracer.startSpan('LiveStore:mutations', {}, otelOptions.rootSpanContext)
151
149
  const otelMuationsSpanContext = otel.trace.setSpan(otel.context.active(), mutationsSpan)
152
150
 
153
- const queriesSpan = otelTracer.startSpan('LiveStore:queries', {}, otelRootSpanContext)
151
+ const queriesSpan = otelOptions.tracer.startSpan('LiveStore:queries', {}, otelOptions.rootSpanContext)
154
152
  const otelQueriesSpanContext = otel.trace.setSpan(otel.context.active(), queriesSpan)
155
153
 
156
- this.graph = dbGraph
157
- this.graph.context = { store: this as any, otelTracer, rootOtelContext: otelQueriesSpanContext }
154
+ this.reactivityGraph = reactivityGraph
155
+ this.reactivityGraph.context = {
156
+ store: this as any,
157
+ otelTracer: otelOptions.tracer,
158
+ rootOtelContext: otelQueriesSpanContext,
159
+ }
158
160
 
159
161
  this.adapter.coordinator.syncMutations.pipe(
160
162
  Stream.tapSync((mutationEventDecoded) => {
@@ -170,7 +172,7 @@ export class Store<
170
172
  }
171
173
 
172
174
  this.otel = {
173
- tracer: otelTracer,
175
+ tracer: otelOptions.tracer,
174
176
  mutationsSpanContext: otelMuationsSpanContext,
175
177
  queriesSpanContext: otelQueriesSpanContext,
176
178
  }
@@ -182,7 +184,7 @@ export class Store<
182
184
  // ...Array.from(dynamicallyRegisteredTables.values()).map((_) => _.sqliteDef.name),
183
185
  )
184
186
  const existingTableRefs = new Map(
185
- Array.from(this.graph.atoms.values())
187
+ Array.from(this.reactivityGraph.atoms.values())
186
188
  .filter((_): _ is Ref<any, any, any> => _._tag === 'ref' && _.label?.startsWith('tableRef:') === true)
187
189
  .map((_) => [_.label!.slice('tableRef:'.length), _] as const),
188
190
  )
@@ -201,7 +203,7 @@ export class Store<
201
203
  parentSpan: otel.Span,
202
204
  ): Store<TGraphQLContext, TSchema> => {
203
205
  const ctx = otel.trace.setSpan(otel.context.active(), parentSpan)
204
- return storeOptions.otelTracer.startActiveSpan('LiveStore:store-constructor', {}, ctx, (span) => {
206
+ return storeOptions.otelOptions.tracer.startActiveSpan('LiveStore:store-constructor', {}, ctx, (span) => {
205
207
  try {
206
208
  return new Store(storeOptions)
207
209
  } finally {
@@ -229,7 +231,7 @@ export class Store<
229
231
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
230
232
 
231
233
  const label = `subscribe:${options?.label}`
232
- const effect = this.graph.makeEffect((get) => onNewValue(get(query$.results$)), { label })
234
+ const effect = this.reactivityGraph.makeEffect((get) => onNewValue(get(query$.results$)), { label })
233
235
 
234
236
  this.activeQueries.add(query$ as LiveQuery<TResult>)
235
237
 
@@ -241,7 +243,7 @@ export class Store<
241
243
  const unsubscribe = () => {
242
244
  // console.log('store unsub', query$.label)
243
245
  try {
244
- this.graph.destroyNode(effect)
246
+ this.reactivityGraph.destroyNode(effect)
245
247
  this.activeQueries.remove(query$ as LiveQuery<TResult>)
246
248
  onUnsubsubscribe?.()
247
249
  } finally {
@@ -261,7 +263,7 @@ export class Store<
261
263
  destroy = async () => {
262
264
  for (const tableRef of Object.values(this.tableRefs)) {
263
265
  for (const superComp of tableRef.super) {
264
- this.graph.removeEdge(superComp, tableRef)
266
+ this.reactivityGraph.removeEdge(superComp, tableRef)
265
267
  }
266
268
  }
267
269
 
@@ -379,7 +381,7 @@ export class Store<
379
381
  },
380
382
  )
381
383
 
382
- const tablesToUpdate = [] as [Ref<null, DbContext, RefreshReason>, null][]
384
+ const tablesToUpdate = [] as [Ref<null, QueryContext, RefreshReason>, null][]
383
385
  for (const tableName of writeTables) {
384
386
  const tableRef = this.tableRefs[tableName]
385
387
  assertNever(tableRef !== undefined, `No table ref found for ${tableName}`)
@@ -393,7 +395,7 @@ export class Store<
393
395
  }
394
396
 
395
397
  // Update all table refs together in a batch, to only trigger one reactive update
396
- this.graph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext, skipRefresh })
398
+ this.reactivityGraph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext, skipRefresh })
397
399
  } catch (e: any) {
398
400
  span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
399
401
  } finally {
@@ -417,7 +419,7 @@ export class Store<
417
419
  this.otel.mutationsSpanContext,
418
420
  (span) => {
419
421
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
420
- this.graph.runDeferredEffects({ otelContext })
422
+ this.reactivityGraph.runDeferredEffects({ otelContext })
421
423
  span.end()
422
424
  },
423
425
  )
@@ -485,7 +487,7 @@ export class Store<
485
487
 
486
488
  if (coordinatorMode !== 'skip-coordinator') {
487
489
  // Asynchronously apply mutation to a persistent storage (we're not awaiting this promise here)
488
- void this.adapter.coordinator.mutate(mutationEventEncoded, {
490
+ void this.adapter.coordinator.mutate(mutationEventEncoded as MutationEvent.AnyEncoded, {
489
491
  span,
490
492
  persisted: coordinatorMode !== 'skip-persist',
491
493
  })
@@ -523,7 +525,7 @@ export class Store<
523
525
  }
524
526
 
525
527
  makeTableRef = (tableName: string) =>
526
- this.graph.makeRef(null, {
528
+ this.reactivityGraph.makeRef(null, {
527
529
  equal: () => false,
528
530
  label: `tableRef:${tableName}`,
529
531
  meta: { liveStoreRefType: 'table' },
@@ -532,9 +534,11 @@ export class Store<
532
534
  private bootDevtools = () => {
533
535
  const devtoolsChannel = Devtools.makeBroadcastChannels()
534
536
 
535
- const signalsSubcriptionRef = ref<undefined | (() => void)>(undefined)
536
- // let alreadySubscribedToLiveQueries = false
537
- const liveQueriesSubscriptionRef = ref<undefined | (() => void)>(undefined)
537
+ type Unsub = () => void
538
+ type RequestId = string
539
+
540
+ const reactivityGraphSubcriptions = new Map<RequestId, Unsub>()
541
+ const liveQueriesSubscriptions = new Map<RequestId, Unsub>()
538
542
  devtoolsChannel.toAppHost.addEventListener('message', async (event) => {
539
543
  const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHost)(event.data)
540
544
  if (
@@ -552,12 +556,12 @@ export class Store<
552
556
  devtoolsChannel.fromAppHost.postMessage(Schema.encodeSync(Devtools.MessageFromAppHost)(message))
553
557
 
554
558
  switch (decoded.value._tag) {
555
- case 'LSD.SignalsSubscribe': {
559
+ case 'LSD.ReactivityGraphSubscribe': {
556
560
  const includeResults = decoded.value.includeResults
557
561
  const send = () =>
558
562
  sendToDevtools(
559
- Devtools.SignalsRes.make({
560
- signals: this.graph.getSnapshot({ includeResults }),
563
+ Devtools.ReactivityGraphRes.make({
564
+ reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
561
565
  requestId,
562
566
  liveStoreVersion,
563
567
  }),
@@ -565,9 +569,10 @@ export class Store<
565
569
 
566
570
  send()
567
571
 
568
- if (signalsSubcriptionRef.current === undefined) {
569
- signalsSubcriptionRef.current = this.graph.subscribeToRefresh(() => send())
570
- }
572
+ reactivityGraphSubcriptions.set(
573
+ requestId,
574
+ this.reactivityGraph.subscribeToRefresh(() => send()),
575
+ )
571
576
 
572
577
  break
573
578
  }
@@ -588,9 +593,8 @@ export class Store<
588
593
  sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
589
594
  break
590
595
  }
591
- case 'LSD.SignalsUnsubscribe': {
592
- signalsSubcriptionRef.current!()
593
- signalsSubcriptionRef.current = undefined
596
+ case 'LSD.ReactivityGraphUnsubscribe': {
597
+ reactivityGraphSubcriptions.get(requestId)!()
594
598
  break
595
599
  }
596
600
  case 'LSD.LiveQueriesSubscribe': {
@@ -613,15 +617,15 @@ export class Store<
613
617
 
614
618
  send()
615
619
 
616
- if (liveQueriesSubscriptionRef.current === undefined) {
617
- liveQueriesSubscriptionRef.current = this.graph.subscribeToRefresh(() => send())
618
- }
620
+ liveQueriesSubscriptions.set(
621
+ requestId,
622
+ this.reactivityGraph.subscribeToRefresh(() => send()),
623
+ )
619
624
 
620
625
  break
621
626
  }
622
627
  case 'LSD.LiveQueriesUnsubscribe': {
623
- liveQueriesSubscriptionRef.current!()
624
- liveQueriesSubscriptionRef.current = undefined
628
+ liveQueriesSubscriptions.get(requestId)!()
625
629
  break
626
630
  }
627
631
  case 'LSD.ResetAllDataReq': {
@@ -656,24 +660,24 @@ export const createStore = async <
656
660
  >({
657
661
  schema,
658
662
  graphQLOptions,
659
- otelTracer = makeNoopTracer(),
660
- otelRootSpanContext = otel.context.active(),
663
+ otelOptions,
661
664
  adapter: adapterFactory,
662
665
  boot,
663
- dbGraph = globalDbGraph,
666
+ reactivityGraph = globalReactivityGraph,
664
667
  batchUpdates,
665
668
  disableDevtools,
666
669
  }: {
667
670
  schema: TSchema
668
- graphQLOptions?: GraphQLOptions<TGraphQLContext>
669
- otelTracer?: otel.Tracer
670
- otelRootSpanContext?: otel.Context
671
671
  adapter: StoreAdapterFactory
672
+ reactivityGraph?: ReactivityGraph
673
+ graphQLOptions?: GraphQLOptions<TGraphQLContext>
674
+ otelOptions?: Partial<OtelOptions>
672
675
  boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
673
- dbGraph?: DbGraph
674
676
  batchUpdates?: (run: () => void) => void
675
677
  disableDevtools?: boolean
676
678
  }): Promise<Store<TGraphQLContext, TSchema>> => {
679
+ const otelTracer = otelOptions?.tracer ?? makeNoopTracer()
680
+ const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active()
677
681
  return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
678
682
  try {
679
683
  performance.mark('livestore:db-creating')
@@ -685,10 +689,10 @@ export const createStore = async <
685
689
  performance.measure('livestore:db-create', 'livestore:db-creating', 'livestore:db-created')
686
690
 
687
691
  if (batchUpdates !== undefined) {
688
- dbGraph.effectsWrapper = batchUpdates
692
+ reactivityGraph.effectsWrapper = batchUpdates
689
693
  }
690
694
 
691
- const mutationEventSchema = makeMutationEventSchema(Object.fromEntries(schema.mutations.entries()) as any)
695
+ const mutationEventSchema = makeMutationEventSchemaMemo(schema)
692
696
 
693
697
  // TODO consider moving booting into the storage backend
694
698
  if (boot !== undefined) {
@@ -721,7 +725,11 @@ export const createStore = async <
721
725
  }
722
726
 
723
727
  const mutationEventEncoded = Schema.encodeUnknownSync(mutationEventSchema)(mutationEventDecoded)
724
- void adapter.coordinator.mutate(mutationEventEncoded, { span, persisted: true })
728
+
729
+ void adapter.coordinator.mutate(mutationEventEncoded as MutationEvent.AnyEncoded, {
730
+ span,
731
+ persisted: true,
732
+ })
725
733
  }
726
734
  },
727
735
  select: (queryStr, bindValues) => {
@@ -764,13 +772,11 @@ export const createStore = async <
764
772
  // await applySchema(db, schema)
765
773
  return Store.createStore<TGraphQLContext, TSchema>(
766
774
  {
767
- adapter: adapter,
775
+ adapter,
768
776
  schema,
769
777
  graphQLOptions,
770
- otelTracer,
771
- otelRootSpanContext,
772
- dbGraph,
773
- mutationEventSchema,
778
+ otelOptions: { tracer: otelTracer, rootSpanContext: otelRootSpanContext },
779
+ reactivityGraph,
774
780
  disableDevtools,
775
781
  },
776
782
  span,