@livestore/livestore 0.0.54-dev.2 → 0.0.54-dev.22

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 (89) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/MainDatabaseWrapper.d.ts +6 -5
  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/QueryCache.d.ts +1 -1
  7. package/dist/QueryCache.d.ts.map +1 -1
  8. package/dist/QueryCache.js.map +1 -1
  9. package/dist/__tests__/react/fixture.d.ts +4 -4
  10. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  11. package/dist/__tests__/react/fixture.js +10 -8
  12. package/dist/__tests__/react/fixture.js.map +1 -1
  13. package/dist/effect/LiveStore.d.ts +2 -3
  14. package/dist/effect/LiveStore.d.ts.map +1 -1
  15. package/dist/effect/LiveStore.js +4 -2
  16. package/dist/effect/LiveStore.js.map +1 -1
  17. package/dist/global-state.d.ts +1 -1
  18. package/dist/global-state.d.ts.map +1 -1
  19. package/dist/global-state.js +2 -2
  20. package/dist/global-state.js.map +1 -1
  21. package/dist/index.d.ts +3 -4
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/react/LiveStoreProvider.d.ts +3 -4
  26. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  27. package/dist/react/LiveStoreProvider.js +8 -13
  28. package/dist/react/LiveStoreProvider.js.map +1 -1
  29. package/dist/react/useRow.d.ts +2 -2
  30. package/dist/react/useRow.d.ts.map +1 -1
  31. package/dist/react/useRow.js +3 -3
  32. package/dist/react/useRow.js.map +1 -1
  33. package/dist/react/useRow.test.js +21 -21
  34. package/dist/react/useRow.test.js.map +1 -1
  35. package/dist/react/useTemporaryQuery.js +1 -1
  36. package/dist/react/useTemporaryQuery.js.map +1 -1
  37. package/dist/reactive.js +1 -1
  38. package/dist/reactive.js.map +1 -1
  39. package/dist/reactiveQueries/base-class.d.ts +6 -6
  40. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  41. package/dist/reactiveQueries/base-class.js +3 -3
  42. package/dist/reactiveQueries/base-class.js.map +1 -1
  43. package/dist/reactiveQueries/graphql.d.ts +8 -8
  44. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  45. package/dist/reactiveQueries/graphql.js +10 -10
  46. package/dist/reactiveQueries/graphql.js.map +1 -1
  47. package/dist/reactiveQueries/js.d.ts +6 -6
  48. package/dist/reactiveQueries/js.d.ts.map +1 -1
  49. package/dist/reactiveQueries/js.js +8 -8
  50. package/dist/reactiveQueries/js.js.map +1 -1
  51. package/dist/reactiveQueries/sql.d.ts +9 -10
  52. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  53. package/dist/reactiveQueries/sql.js +12 -12
  54. package/dist/reactiveQueries/sql.js.map +1 -1
  55. package/dist/row-query.d.ts +2 -2
  56. package/dist/row-query.d.ts.map +1 -1
  57. package/dist/row-query.js +4 -4
  58. package/dist/row-query.js.map +1 -1
  59. package/dist/store.d.ts +23 -20
  60. package/dist/store.d.ts.map +1 -1
  61. package/dist/store.js +140 -144
  62. package/dist/store.js.map +1 -1
  63. package/package.json +5 -12
  64. package/src/MainDatabaseWrapper.ts +14 -8
  65. package/src/QueryCache.ts +1 -2
  66. package/src/__tests__/react/fixture.tsx +11 -9
  67. package/src/effect/LiveStore.ts +6 -5
  68. package/src/global-state.ts +2 -2
  69. package/src/index.ts +17 -4
  70. package/src/react/LiveStoreProvider.tsx +10 -18
  71. package/src/react/useRow.test.tsx +21 -21
  72. package/src/react/useRow.ts +5 -5
  73. package/src/react/useTemporaryQuery.ts +2 -2
  74. package/src/reactive.ts +3 -1
  75. package/src/reactiveQueries/base-class.ts +9 -9
  76. package/src/reactiveQueries/graphql.ts +19 -15
  77. package/src/reactiveQueries/js.ts +12 -12
  78. package/src/reactiveQueries/sql.ts +19 -21
  79. package/src/row-query.ts +8 -8
  80. package/src/store.ts +214 -179
  81. package/dist/utils/bounded-collections.d.ts +0 -34
  82. package/dist/utils/bounded-collections.d.ts.map +0 -1
  83. package/dist/utils/bounded-collections.js +0 -91
  84. package/dist/utils/bounded-collections.js.map +0 -1
  85. package/dist/utils/util.d.ts +0 -14
  86. package/dist/utils/util.d.ts.map +0 -1
  87. package/dist/utils/util.js +0 -19
  88. package/dist/utils/util.js.map +0 -1
  89. package/src/utils/util.ts +0 -31
package/src/store.ts CHANGED
@@ -1,22 +1,29 @@
1
- import type { BootDb, PreparedBindValues, ResetMode, StoreAdapter, StoreAdapterFactory } from '@livestore/common'
2
- import { Devtools, getExecArgsFromMutation } from '@livestore/common'
1
+ import type {
2
+ BootDb,
3
+ ParamsObject,
4
+ PreparedBindValues,
5
+ ResetMode,
6
+ StoreAdapter,
7
+ StoreAdapterFactory,
8
+ UnexpectedError,
9
+ } from '@livestore/common'
10
+ import { Devtools, getExecArgsFromMutation, prepareBindValues } from '@livestore/common'
3
11
  import { version as liveStoreVersion } from '@livestore/common/package.json'
4
- import type { LiveStoreSchema, MutationEvent, MutationEventSchema } from '@livestore/common/schema'
5
- import { makeMutationEventSchema } from '@livestore/common/schema'
6
- import { assertNever, isPromise, makeNoopTracer, ref, shouldNeverHappen } from '@livestore/utils'
7
- import { Effect, Schema, Stream } from '@livestore/utils/effect'
12
+ import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
13
+ import { makeMutationEventSchemaMemo } from '@livestore/common/schema'
14
+ import { assertNever, isPromise, makeNoopTracer, shouldNeverHappen } from '@livestore/utils'
15
+ import { Effect, Layer, OtelTracer, Schema, Stream } from '@livestore/utils/effect'
8
16
  import * as otel from '@opentelemetry/api'
9
17
  import type { GraphQLSchema } from 'graphql'
10
18
 
11
- import { globalDbGraph } from './global-state.js'
19
+ import { globalReactivityGraph } from './global-state.js'
12
20
  import { MainDatabaseWrapper } from './MainDatabaseWrapper.js'
13
21
  import type { StackInfo } from './react/utils/stack-info.js'
14
- import type { DebugRefreshReasonBase, ReactiveGraph, Ref } from './reactive.js'
15
- import type { DbContext, DbGraph, LiveQuery } from './reactiveQueries/base-class.js'
22
+ import type { DebugRefreshReasonBase, Ref } from './reactive.js'
23
+ import { NOT_REFRESHED_YET } from './reactive.js'
24
+ import type { LiveQuery, QueryContext, ReactivityGraph } from './reactiveQueries/base-class.js'
16
25
  import { downloadBlob } from './utils/dev.js'
17
26
  import { getDurationMsFromSpan } from './utils/otel.js'
18
- import type { ParamsObject } from './utils/util.js'
19
- import { prepareBindValues } from './utils/util.js'
20
27
 
21
28
  export type BaseGraphQLContext = {
22
29
  queriedTables: Set<string>
@@ -29,17 +36,21 @@ export type GraphQLOptions<TContext> = {
29
36
  makeContext: (db: MainDatabaseWrapper, tracer: otel.Tracer) => TContext
30
37
  }
31
38
 
39
+ export type OtelOptions = {
40
+ tracer: otel.Tracer
41
+ rootSpanContext: otel.Context
42
+ }
43
+
32
44
  export type StoreOptions<
33
45
  TGraphQLContext extends BaseGraphQLContext,
34
46
  TSchema extends LiveStoreSchema = LiveStoreSchema,
35
47
  > = {
36
48
  adapter: StoreAdapter
37
49
  schema: TSchema
50
+ // TODO remove graphql-related stuff from store and move to GraphQL query directly
38
51
  graphQLOptions?: GraphQLOptions<TGraphQLContext>
39
- otelTracer: otel.Tracer
40
- otelRootSpanContext: otel.Context
41
- dbGraph: DbGraph
42
- mutationEventSchema: MutationEventSchema<any>
52
+ otelOptions: OtelOptions
53
+ reactivityGraph: ReactivityGraph
43
54
  disableDevtools?: boolean
44
55
  }
45
56
 
@@ -95,11 +106,8 @@ export class Store<
95
106
  TSchema extends LiveStoreSchema = LiveStoreSchema,
96
107
  > {
97
108
  id = uniqueStoreId()
98
- graph: ReactiveGraph<RefreshReason, QueryDebugInfo, DbContext>
109
+ reactivityGraph: ReactivityGraph
99
110
  mainDbWrapper: MainDatabaseWrapper
100
- // TODO refactor
101
- // _proxyDb: InMemoryDatabase
102
- // TODO
103
111
  adapter: StoreAdapter
104
112
  schema: LiveStoreSchema
105
113
  graphQLSchema?: GraphQLSchema
@@ -109,7 +117,7 @@ export class Store<
109
117
  * Note we're using `Ref<null>` here as we don't care about the value but only about *that* something has changed.
110
118
  * This only works in combination with `equal: () => false` which will always trigger a refresh.
111
119
  */
112
- tableRefs: { [key: string]: Ref<null, DbContext, RefreshReason> }
120
+ tableRefs: { [key: string]: Ref<null, QueryContext, RefreshReason> }
113
121
 
114
122
  // TODO remove this temporary solution and find a better way to avoid re-processing the same mutation
115
123
  __processedMutationIds = new Set<string>()
@@ -124,32 +132,33 @@ export class Store<
124
132
  adapter,
125
133
  schema,
126
134
  graphQLOptions,
127
- dbGraph,
128
- otelTracer,
129
- otelRootSpanContext,
130
- mutationEventSchema,
135
+ reactivityGraph,
136
+ otelOptions,
131
137
  disableDevtools,
132
138
  }: StoreOptions<TGraphQLContext, TSchema>) {
133
- this.mainDbWrapper = new MainDatabaseWrapper({ otelTracer, otelRootSpanContext, db: adapter.mainDb })
139
+ this.mainDbWrapper = new MainDatabaseWrapper({ otel: otelOptions, db: adapter.mainDb })
134
140
  this.adapter = adapter
135
141
  this.schema = schema
136
142
 
137
143
  // TODO refactor
138
- this.__mutationEventSchema = mutationEventSchema
139
- // this.mutationEventSchema = makeMutationEventSchema(Object.fromEntries(schema.mutations.entries()) as any)
144
+ this.__mutationEventSchema = makeMutationEventSchemaMemo(schema)
140
145
 
141
146
  // TODO generalize the `tableRefs` concept to allow finer-grained refs
142
147
  this.tableRefs = {}
143
148
  this.activeQueries = new ReferenceCountedSet()
144
149
 
145
- const mutationsSpan = otelTracer.startSpan('LiveStore:mutations', {}, otelRootSpanContext)
150
+ const mutationsSpan = otelOptions.tracer.startSpan('LiveStore:mutations', {}, otelOptions.rootSpanContext)
146
151
  const otelMuationsSpanContext = otel.trace.setSpan(otel.context.active(), mutationsSpan)
147
152
 
148
- const queriesSpan = otelTracer.startSpan('LiveStore:queries', {}, otelRootSpanContext)
153
+ const queriesSpan = otelOptions.tracer.startSpan('LiveStore:queries', {}, otelOptions.rootSpanContext)
149
154
  const otelQueriesSpanContext = otel.trace.setSpan(otel.context.active(), queriesSpan)
150
155
 
151
- this.graph = dbGraph
152
- this.graph.context = { store: this as any, otelTracer, rootOtelContext: otelQueriesSpanContext }
156
+ this.reactivityGraph = reactivityGraph
157
+ this.reactivityGraph.context = {
158
+ store: this as unknown as Store<BaseGraphQLContext, LiveStoreSchema>,
159
+ otelTracer: otelOptions.tracer,
160
+ rootOtelContext: otelQueriesSpanContext,
161
+ }
153
162
 
154
163
  this.adapter.coordinator.syncMutations.pipe(
155
164
  Stream.tapSync((mutationEventDecoded) => {
@@ -165,7 +174,7 @@ export class Store<
165
174
  }
166
175
 
167
176
  this.otel = {
168
- tracer: otelTracer,
177
+ tracer: otelOptions.tracer,
169
178
  mutationsSpanContext: otelMuationsSpanContext,
170
179
  queriesSpanContext: otelQueriesSpanContext,
171
180
  }
@@ -177,7 +186,7 @@ export class Store<
177
186
  // ...Array.from(dynamicallyRegisteredTables.values()).map((_) => _.sqliteDef.name),
178
187
  )
179
188
  const existingTableRefs = new Map(
180
- Array.from(this.graph.atoms.values())
189
+ Array.from(this.reactivityGraph.atoms.values())
181
190
  .filter((_): _ is Ref<any, any, any> => _._tag === 'ref' && _.label?.startsWith('tableRef:') === true)
182
191
  .map((_) => [_.label!.slice('tableRef:'.length), _] as const),
183
192
  )
@@ -196,7 +205,7 @@ export class Store<
196
205
  parentSpan: otel.Span,
197
206
  ): Store<TGraphQLContext, TSchema> => {
198
207
  const ctx = otel.trace.setSpan(otel.context.active(), parentSpan)
199
- return storeOptions.otelTracer.startActiveSpan('LiveStore:store-constructor', {}, ctx, (span) => {
208
+ return storeOptions.otelOptions.tracer.startActiveSpan('LiveStore:store-constructor', {}, ctx, (span) => {
200
209
  try {
201
210
  return new Store(storeOptions)
202
211
  } finally {
@@ -224,7 +233,7 @@ export class Store<
224
233
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
225
234
 
226
235
  const label = `subscribe:${options?.label}`
227
- const effect = this.graph.makeEffect((get) => onNewValue(get(query$.results$)), { label })
236
+ const effect = this.reactivityGraph.makeEffect((get) => onNewValue(get(query$.results$)), { label })
228
237
 
229
238
  this.activeQueries.add(query$ as LiveQuery<TResult>)
230
239
 
@@ -236,7 +245,7 @@ export class Store<
236
245
  const unsubscribe = () => {
237
246
  // console.log('store unsub', query$.label)
238
247
  try {
239
- this.graph.destroyNode(effect)
248
+ this.reactivityGraph.destroyNode(effect)
240
249
  this.activeQueries.remove(query$ as LiveQuery<TResult>)
241
250
  onUnsubsubscribe?.()
242
251
  } finally {
@@ -256,14 +265,14 @@ export class Store<
256
265
  destroy = async () => {
257
266
  for (const tableRef of Object.values(this.tableRefs)) {
258
267
  for (const superComp of tableRef.super) {
259
- this.graph.removeEdge(superComp, tableRef)
268
+ this.reactivityGraph.removeEdge(superComp, tableRef)
260
269
  }
261
270
  }
262
271
 
263
272
  otel.trace.getSpan(this.otel.mutationsSpanContext)!.end()
264
273
  otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
265
274
 
266
- await this.adapter.coordinator.shutdown()
275
+ await this.adapter.coordinator.shutdown.pipe(Effect.tapCauseLogPretty, Effect.runPromise)
267
276
  }
268
277
 
269
278
  mutate: {
@@ -374,7 +383,7 @@ export class Store<
374
383
  },
375
384
  )
376
385
 
377
- const tablesToUpdate = [] as [Ref<null, DbContext, RefreshReason>, null][]
386
+ const tablesToUpdate = [] as [Ref<null, QueryContext, RefreshReason>, null][]
378
387
  for (const tableName of writeTables) {
379
388
  const tableRef = this.tableRefs[tableName]
380
389
  assertNever(tableRef !== undefined, `No table ref found for ${tableName}`)
@@ -388,7 +397,7 @@ export class Store<
388
397
  }
389
398
 
390
399
  // Update all table refs together in a batch, to only trigger one reactive update
391
- this.graph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext, skipRefresh })
400
+ this.reactivityGraph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext, skipRefresh })
392
401
  } catch (e: any) {
393
402
  span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
394
403
  } finally {
@@ -412,7 +421,7 @@ export class Store<
412
421
  this.otel.mutationsSpanContext,
413
422
  (span) => {
414
423
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
415
- this.graph.runDeferredEffects({ otelContext })
424
+ this.reactivityGraph.runDeferredEffects({ otelContext })
416
425
  span.end()
417
426
  },
418
427
  )
@@ -480,10 +489,9 @@ export class Store<
480
489
 
481
490
  if (coordinatorMode !== 'skip-coordinator') {
482
491
  // Asynchronously apply mutation to a persistent storage (we're not awaiting this promise here)
483
- void this.adapter.coordinator.mutate(mutationEventEncoded, {
484
- span,
485
- persisted: coordinatorMode !== 'skip-persist',
486
- })
492
+ this.adapter.coordinator
493
+ .mutate(mutationEventEncoded as MutationEvent.AnyEncoded, { persisted: coordinatorMode !== 'skip-persist' })
494
+ .pipe(Effect.tapCauseLogPretty, Effect.runFork)
487
495
  }
488
496
 
489
497
  // Uncomment to print a list of queries currently registered on the store
@@ -509,8 +517,9 @@ export class Store<
509
517
  ) => {
510
518
  this.mainDbWrapper.execute(query, prepareBindValues(params, query), writeTables, { otelContext })
511
519
 
512
- const parentSpan = otel.trace.getSpan(otel.context.active())
513
- this.adapter.coordinator.execute(query, prepareBindValues(params, query), parentSpan)
520
+ this.adapter.coordinator
521
+ .execute(query, prepareBindValues(params, query))
522
+ .pipe(Effect.tapCauseLogPretty, Effect.runFork)
514
523
  }
515
524
 
516
525
  select = (query: string, params: ParamsObject = {}) => {
@@ -518,7 +527,7 @@ export class Store<
518
527
  }
519
528
 
520
529
  makeTableRef = (tableName: string) =>
521
- this.graph.makeRef(null, {
530
+ this.reactivityGraph.makeRef(null, {
522
531
  equal: () => false,
523
532
  label: `tableRef:${tableName}`,
524
533
  meta: { liveStoreRefType: 'table' },
@@ -527,14 +536,16 @@ export class Store<
527
536
  private bootDevtools = () => {
528
537
  const devtoolsChannel = Devtools.makeBroadcastChannels()
529
538
 
530
- const signalsSubcriptionRef = ref<undefined | (() => void)>(undefined)
531
- // let alreadySubscribedToLiveQueries = false
532
- const liveQueriesSubscriptionRef = ref<undefined | (() => void)>(undefined)
539
+ type Unsub = () => void
540
+ type RequestId = string
541
+
542
+ const reactivityGraphSubcriptions = new Map<RequestId, Unsub>()
543
+ const liveQueriesSubscriptions = new Map<RequestId, Unsub>()
533
544
  devtoolsChannel.toAppHost.addEventListener('message', async (event) => {
534
545
  const decoded = Schema.decodeUnknownOption(Devtools.MessageToAppHost)(event.data)
535
546
  if (
536
547
  decoded._tag === 'None' ||
537
- decoded.value._tag === 'LSD.DevtoolsReadyBroadcast' ||
548
+ decoded.value._tag === 'LSD.DevtoolsReady' ||
538
549
  decoded.value._tag === 'LSD.DevtoolsConnected' ||
539
550
  decoded.value.channelId !== this.adapter.coordinator.devtools.channelId
540
551
  ) {
@@ -547,12 +558,12 @@ export class Store<
547
558
  devtoolsChannel.fromAppHost.postMessage(Schema.encodeSync(Devtools.MessageFromAppHost)(message))
548
559
 
549
560
  switch (decoded.value._tag) {
550
- case 'LSD.SignalsSubscribe': {
561
+ case 'LSD.ReactivityGraphSubscribe': {
551
562
  const includeResults = decoded.value.includeResults
552
563
  const send = () =>
553
564
  sendToDevtools(
554
- Devtools.SignalsRes.make({
555
- signals: this.graph.getSnapshot({ includeResults }),
565
+ Devtools.ReactivityGraphRes.make({
566
+ reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults }),
556
567
  requestId,
557
568
  liveStoreVersion,
558
569
  }),
@@ -560,9 +571,10 @@ export class Store<
560
571
 
561
572
  send()
562
573
 
563
- if (signalsSubcriptionRef.current === undefined) {
564
- signalsSubcriptionRef.current = this.graph.subscribeToRefresh(() => send())
565
- }
574
+ reactivityGraphSubcriptions.set(
575
+ requestId,
576
+ this.reactivityGraph.subscribeToRefresh(() => send()),
577
+ )
566
578
 
567
579
  break
568
580
  }
@@ -583,9 +595,8 @@ export class Store<
583
595
  sendToDevtools(Devtools.DebugInfoRerunQueryRes.make({ requestId, liveStoreVersion }))
584
596
  break
585
597
  }
586
- case 'LSD.SignalsUnsubscribe': {
587
- signalsSubcriptionRef.current!()
588
- signalsSubcriptionRef.current = undefined
598
+ case 'LSD.ReactivityGraphUnsubscribe': {
599
+ reactivityGraphSubcriptions.get(requestId)!()
589
600
  break
590
601
  }
591
602
  case 'LSD.LiveQueriesSubscribe': {
@@ -598,7 +609,10 @@ export class Store<
598
609
  label: q.label,
599
610
  runs: q.runs,
600
611
  executionTimes: q.executionTimes.map((_) => Number(_.toString().slice(0, 5))),
601
- lastestResult: q.results$.previousResult,
612
+ lastestResult:
613
+ q.results$.previousResult === NOT_REFRESHED_YET
614
+ ? 'SYMBOL_NOT_REFRESHED_YET'
615
+ : q.results$.previousResult,
602
616
  activeSubscriptions: Array.from(q.activeSubscriptions),
603
617
  })),
604
618
  requestId,
@@ -608,19 +622,22 @@ export class Store<
608
622
 
609
623
  send()
610
624
 
611
- if (liveQueriesSubscriptionRef.current === undefined) {
612
- liveQueriesSubscriptionRef.current = this.graph.subscribeToRefresh(() => send())
613
- }
625
+ liveQueriesSubscriptions.set(
626
+ requestId,
627
+ this.reactivityGraph.subscribeToRefresh(() => send()),
628
+ )
614
629
 
615
630
  break
616
631
  }
617
632
  case 'LSD.LiveQueriesUnsubscribe': {
618
- liveQueriesSubscriptionRef.current!()
619
- liveQueriesSubscriptionRef.current = undefined
633
+ liveQueriesSubscriptions.get(requestId)!()
620
634
  break
621
635
  }
622
636
  case 'LSD.ResetAllDataReq': {
623
- await this.adapter.coordinator.dangerouslyReset(decoded.value.mode)
637
+ await this.adapter.coordinator
638
+ .dangerouslyReset(decoded.value.mode)
639
+ .pipe(Effect.tapCauseLogPretty, Effect.runPromise)
640
+
624
641
  sendToDevtools(Devtools.ResetAllDataRes.make({ requestId, liveStoreVersion }))
625
642
 
626
643
  break
@@ -636,144 +653,162 @@ export class Store<
636
653
  }
637
654
 
638
655
  __devDownloadMutationLogDb = async () => {
639
- const data = await this.adapter.coordinator.getMutationLogData()
656
+ const data = await this.adapter.coordinator.getMutationLogData.pipe(Effect.tapCauseLogPretty, Effect.runPromise)
640
657
  downloadBlob(data, `livestore-mutationlog-${Date.now()}.db`)
641
658
  }
642
659
 
643
660
  // TODO allow for graceful store reset without requiring a full page reload (which should also call .boot)
644
- dangerouslyResetStorage = (mode: ResetMode) => this.adapter.coordinator.dangerouslyReset(mode)
661
+ dangerouslyResetStorage = (mode: ResetMode) =>
662
+ this.adapter.coordinator.dangerouslyReset(mode).pipe(Effect.tapCauseLogPretty, Effect.runPromise)
663
+ }
664
+
665
+ export type CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext, TSchema extends LiveStoreSchema> = {
666
+ schema: TSchema
667
+ adapter: StoreAdapterFactory
668
+ reactivityGraph?: ReactivityGraph
669
+ graphQLOptions?: GraphQLOptions<TGraphQLContext>
670
+ otelOptions?: Partial<OtelOptions>
671
+ boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
672
+ batchUpdates?: (run: () => void) => void
673
+ disableDevtools?: boolean
645
674
  }
646
675
 
647
676
  /** Create a new LiveStore Store */
648
677
  export const createStore = async <
649
678
  TGraphQLContext extends BaseGraphQLContext,
650
679
  TSchema extends LiveStoreSchema = LiveStoreSchema,
680
+ >(
681
+ options: CreateStoreOptions<TGraphQLContext, TSchema>,
682
+ ): Promise<Store<TGraphQLContext, TSchema>> => createStoreEff(options).pipe(Effect.tapCauseLogPretty, Effect.runPromise)
683
+
684
+ export const createStoreEff = <
685
+ TGraphQLContext extends BaseGraphQLContext,
686
+ TSchema extends LiveStoreSchema = LiveStoreSchema,
651
687
  >({
652
688
  schema,
653
689
  graphQLOptions,
654
- otelTracer = makeNoopTracer(),
655
- otelRootSpanContext = otel.context.active(),
690
+ otelOptions,
656
691
  adapter: adapterFactory,
657
692
  boot,
658
- dbGraph = globalDbGraph,
693
+ reactivityGraph = globalReactivityGraph,
659
694
  batchUpdates,
660
695
  disableDevtools,
661
- }: {
662
- schema: TSchema
663
- graphQLOptions?: GraphQLOptions<TGraphQLContext>
664
- otelTracer?: otel.Tracer
665
- otelRootSpanContext?: otel.Context
666
- adapter: StoreAdapterFactory
667
- boot?: (db: BootDb, parentSpan: otel.Span) => unknown | Promise<unknown>
668
- dbGraph?: DbGraph
669
- batchUpdates?: (run: () => void) => void
670
- disableDevtools?: boolean
671
- }): Promise<Store<TGraphQLContext, TSchema>> => {
672
- return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
673
- try {
674
- performance.mark('livestore:db-creating')
675
- const otelContext = otel.trace.setSpan(otel.context.active(), span)
676
-
677
- const adapterPromise = adapterFactory({ otelTracer, otelContext, schema })
678
- const adapter = adapterPromise instanceof Promise ? await adapterPromise : adapterPromise
679
- performance.mark('livestore:db-created')
680
- performance.measure('livestore:db-create', 'livestore:db-creating', 'livestore:db-created')
681
-
682
- if (batchUpdates !== undefined) {
683
- dbGraph.effectsWrapper = batchUpdates
684
- }
696
+ }: CreateStoreOptions<TGraphQLContext, TSchema>): Effect.Effect<Store<TGraphQLContext, TSchema>, UnexpectedError> => {
697
+ const otelTracer = otelOptions?.tracer ?? makeNoopTracer()
698
+ const otelRootSpanContext = otelOptions?.rootSpanContext ?? otel.context.active()
685
699
 
686
- const mutationEventSchema = makeMutationEventSchema(Object.fromEntries(schema.mutations.entries()) as any)
700
+ const TracingLive = Layer.unwrapEffect(Effect.map(OtelTracer.make, Layer.setTracer)).pipe(
701
+ Layer.provide(Layer.sync(OtelTracer.Tracer, () => otelTracer)),
702
+ )
687
703
 
688
- // TODO consider moving booting into the storage backend
689
- if (boot !== undefined) {
690
- let isInTxn = false
691
- let txnExecuteStmnts: [string, PreparedBindValues | undefined][] = []
704
+ return Effect.gen(function* () {
705
+ const span = yield* OtelTracer.currentOtelSpan.pipe(Effect.orDie)
692
706
 
693
- const bootDbImpl: BootDb = {
694
- _tag: 'BootDb',
695
- execute: (queryStr, bindValues) => {
696
- const stmt = adapter.mainDb.prepare(queryStr)
697
- stmt.execute(bindValues)
707
+ const adapter = yield* adapterFactory({ schema }).pipe(
708
+ Effect.withPerformanceMeasure('livestore:makeAdapter'),
709
+ Effect.withSpan('createStore:makeAdapter'),
710
+ )
698
711
 
699
- if (isInTxn === true) {
700
- txnExecuteStmnts.push([queryStr, bindValues])
701
- } else {
702
- void adapter.coordinator.execute(queryStr, bindValues, undefined)
703
- }
704
- },
705
- mutate: (...list) => {
706
- for (const mutationEventDecoded of list) {
707
- const mutationDef =
708
- schema.mutations.get(mutationEventDecoded.mutation) ??
709
- shouldNeverHappen(`Unknown mutation type: ${mutationEventDecoded.mutation}`)
710
-
711
- const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
712
- // const { bindValues, statementSql } = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
713
-
714
- for (const { statementSql, bindValues } of execArgsArr) {
715
- adapter.mainDb.execute(statementSql, bindValues)
716
- }
712
+ if (batchUpdates !== undefined) {
713
+ reactivityGraph.effectsWrapper = batchUpdates
714
+ }
717
715
 
718
- const mutationEventEncoded = Schema.encodeUnknownSync(mutationEventSchema)(mutationEventDecoded)
719
- void adapter.coordinator.mutate(mutationEventEncoded, { span, persisted: true })
720
- }
721
- },
722
- select: (queryStr, bindValues) => {
723
- const stmt = adapter.mainDb.prepare(queryStr)
724
- return stmt.select(bindValues)
725
- },
726
- txn: (callback) => {
727
- try {
728
- isInTxn = true
729
- adapter.mainDb.execute('BEGIN', undefined)
730
-
731
- callback()
732
-
733
- adapter.mainDb.execute('COMMIT', undefined)
734
-
735
- // adapter.coordinator.execute('BEGIN', undefined, undefined)
736
- for (const [queryStr, bindValues] of txnExecuteStmnts) {
737
- adapter.coordinator.execute(queryStr, bindValues, undefined)
738
- }
739
- // adapter.coordinator.execute('COMMIT', undefined, undefined)
740
- } catch (e: any) {
741
- adapter.mainDb.execute('ROLLBACK', undefined)
742
- throw e
743
- } finally {
744
- isInTxn = false
745
- txnExecuteStmnts = []
716
+ const mutationEventSchema = makeMutationEventSchemaMemo(schema)
717
+
718
+ // TODO consider moving booting into the storage backend
719
+ if (boot !== undefined) {
720
+ let isInTxn = false
721
+ let txnExecuteStmnts: [string, PreparedBindValues | undefined][] = []
722
+
723
+ const bootDbImpl: BootDb = {
724
+ _tag: 'BootDb',
725
+ execute: (queryStr, bindValues) => {
726
+ const stmt = adapter.mainDb.prepare(queryStr)
727
+ stmt.execute(bindValues)
728
+
729
+ if (isInTxn === true) {
730
+ txnExecuteStmnts.push([queryStr, bindValues])
731
+ } else {
732
+ adapter.coordinator.execute(queryStr, bindValues).pipe(Effect.tapCauseLogPretty, Effect.runFork)
733
+ }
734
+ },
735
+ mutate: (...list) => {
736
+ for (const mutationEventDecoded of list) {
737
+ const mutationDef =
738
+ schema.mutations.get(mutationEventDecoded.mutation) ??
739
+ shouldNeverHappen(`Unknown mutation type: ${mutationEventDecoded.mutation}`)
740
+
741
+ const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
742
+ // const { bindValues, statementSql } = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
743
+
744
+ for (const { statementSql, bindValues } of execArgsArr) {
745
+ adapter.mainDb.execute(statementSql, bindValues)
746
746
  }
747
- },
748
- }
749
747
 
750
- const booting = boot(bootDbImpl, span)
751
- // NOTE only awaiting if it's actually a promise to avoid unnecessary async/await
752
- if (isPromise(booting)) {
753
- await booting
754
- }
755
- }
748
+ const mutationEventEncoded = Schema.encodeUnknownSync(mutationEventSchema)(mutationEventDecoded)
749
+
750
+ adapter.coordinator
751
+ .mutate(mutationEventEncoded as MutationEvent.AnyEncoded, { persisted: true })
752
+ .pipe(Effect.tapCauseLogPretty, Effect.runFork)
753
+ }
754
+ },
755
+ select: (queryStr, bindValues) => {
756
+ const stmt = adapter.mainDb.prepare(queryStr)
757
+ return stmt.select(bindValues)
758
+ },
759
+ txn: (callback) => {
760
+ try {
761
+ isInTxn = true
762
+ adapter.mainDb.execute('BEGIN', undefined)
763
+
764
+ callback()
756
765
 
757
- // TODO: we can't apply the schema at this point, we've already loaded persisted data!
758
- // Think about what to do about this case.
759
- // await applySchema(db, schema)
760
- return Store.createStore<TGraphQLContext, TSchema>(
761
- {
762
- adapter: adapter,
763
- schema,
764
- graphQLOptions,
765
- otelTracer,
766
- otelRootSpanContext,
767
- dbGraph,
768
- mutationEventSchema,
769
- disableDevtools,
766
+ adapter.mainDb.execute('COMMIT', undefined)
767
+
768
+ // adapter.coordinator.execute('BEGIN', undefined, undefined)
769
+ for (const [queryStr, bindValues] of txnExecuteStmnts) {
770
+ adapter.coordinator.execute(queryStr, bindValues).pipe(Effect.tapCauseLogPretty, Effect.runFork)
771
+ }
772
+ // adapter.coordinator.execute('COMMIT', undefined, undefined)
773
+ } catch (e: any) {
774
+ adapter.mainDb.execute('ROLLBACK', undefined)
775
+ throw e
776
+ } finally {
777
+ isInTxn = false
778
+ txnExecuteStmnts = []
779
+ }
770
780
  },
771
- span,
772
- )
773
- } finally {
774
- span.end()
781
+ }
782
+
783
+ const booting = boot(bootDbImpl, span)
784
+ // NOTE only awaiting if it's actually a promise to avoid unnecessary async/await
785
+ if (isPromise(booting)) {
786
+ yield* Effect.promise(() => booting)
787
+ }
775
788
  }
776
- })
789
+
790
+ // TODO: we can't apply the schema at this point, we've already loaded persisted data!
791
+ // Think about what to do about this case.
792
+ // await applySchema(db, schema)
793
+ return Store.createStore<TGraphQLContext, TSchema>(
794
+ {
795
+ adapter,
796
+ schema,
797
+ graphQLOptions,
798
+ otelOptions: { tracer: otelTracer, rootSpanContext: otelRootSpanContext },
799
+ reactivityGraph,
800
+ disableDevtools,
801
+ },
802
+ span,
803
+ )
804
+ }).pipe(
805
+ Effect.withSpan('createStore', {
806
+ parent: otelOptions?.rootSpanContext
807
+ ? OtelTracer.makeExternalSpan(otel.trace.getSpanContext(otelOptions.rootSpanContext)!)
808
+ : undefined,
809
+ }),
810
+ Effect.provide(TracingLive),
811
+ )
777
812
  }
778
813
 
779
814
  class ReferenceCountedSet<T> {
@@ -1,34 +0,0 @@
1
- /**
2
- * Creates a map that has a fixed number of entries.
3
- * Once hitting the bound, earliest insertions are removed
4
- */
5
- export default class BoundMap<K, V> {
6
- #private;
7
- constructor(sizeLimit: number);
8
- onEvict: ((key: K, value: V) => void) | undefined;
9
- set: (key: K, value: V) => void;
10
- get: (key: K) => V | undefined;
11
- delete: (key: K) => void;
12
- keys: () => IterableIterator<K>;
13
- }
14
- export declare class BoundSet<V> {
15
- #private;
16
- constructor(sizeLimit: number);
17
- onEvict: ((key: V) => void) | undefined;
18
- add: (v: V) => void;
19
- [Symbol.iterator]: () => IterableIterator<V>;
20
- }
21
- export declare class BoundArray<V> {
22
- #private;
23
- constructor(sizeLimit: number);
24
- onEvict: ((key: V) => void) | undefined;
25
- push: (v: V) => void;
26
- get: (index: number) => V | undefined;
27
- delete: (index: number) => void;
28
- get length(): number;
29
- [Symbol.iterator]: () => IterableIterator<V>;
30
- map: <T>(fn: (v: V) => T) => T[];
31
- clear: () => void;
32
- sort: (fn?: (a: V, b: V) => number) => V[];
33
- }
34
- //# sourceMappingURL=bounded-collections.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bounded-collections.d.ts","sourceRoot":"","sources":["../../src/utils/bounded-collections.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,QAAQ,CAAC,CAAC,EAAE,CAAC;;gBAIpB,SAAS,EAAE,MAAM;IAI7B,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAEjD,GAAG,QAAS,CAAC,SAAS,CAAC,UAWtB;IAED,GAAG,QAAS,CAAC,KAAG,CAAC,GAAG,SAAS,CAE5B;IAED,MAAM,QAAS,CAAC,UAEf;IAED,IAAI,4BAEH;CACF;AAED,qBAAa,QAAQ,CAAC,CAAC;;gBAGT,SAAS,EAAE,MAAM;IAW7B,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAEvC,GAAG,MAAO,CAAC,UAET;IAEF,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAEhB;CACF;AAED,qBAAa,UAAU,CAAC,CAAC;;gBAIX,SAAS,EAAE,MAAM;IAI7B,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAEvC,IAAI,MAAO,CAAC,UAQX;IAED,GAAG,UAAW,MAAM,KAAG,CAAC,GAAG,SAAS,CAEnC;IAED,MAAM,UAAW,MAAM,UAEtB;IAED,IAAI,MAAM,WAET;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAEhB;IAED,GAAG,UAAW,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAG,CAAC,EAAE,CAE9B;IAED,KAAK,aAEJ;IAED,IAAI,QAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,SAElC;CACF"}