@livestore/livestore 0.0.34 → 0.0.35

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 (71) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/react/fixture.d.ts +3 -1
  3. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  4. package/dist/__tests__/react/fixture.js +6 -3
  5. package/dist/__tests__/react/fixture.js.map +1 -1
  6. package/dist/__tests__/react/useRow.test.js +10 -10
  7. package/dist/__tests__/react/useRow.test.js.map +1 -1
  8. package/dist/__tests__/reactive.test.js +13 -2
  9. package/dist/__tests__/reactive.test.js.map +1 -1
  10. package/dist/global-state.d.ts +1 -4
  11. package/dist/global-state.d.ts.map +1 -1
  12. package/dist/global-state.js +2 -6
  13. package/dist/global-state.js.map +1 -1
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/react/LiveStoreProvider.d.ts.map +1 -1
  19. package/dist/react/LiveStoreProvider.js +9 -3
  20. package/dist/react/LiveStoreProvider.js.map +1 -1
  21. package/dist/react/useQuery.d.ts.map +1 -1
  22. package/dist/react/useQuery.js +1 -0
  23. package/dist/react/useQuery.js.map +1 -1
  24. package/dist/react/useRow.d.ts +7 -3
  25. package/dist/react/useRow.d.ts.map +1 -1
  26. package/dist/react/useRow.js +7 -5
  27. package/dist/react/useRow.js.map +1 -1
  28. package/dist/reactive.d.ts +21 -6
  29. package/dist/reactive.d.ts.map +1 -1
  30. package/dist/reactive.js +60 -12
  31. package/dist/reactive.js.map +1 -1
  32. package/dist/reactiveQueries/base-class.d.ts +5 -2
  33. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  34. package/dist/reactiveQueries/base-class.js +8 -3
  35. package/dist/reactiveQueries/base-class.js.map +1 -1
  36. package/dist/reactiveQueries/graphql.d.ts +6 -3
  37. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  38. package/dist/reactiveQueries/graphql.js +10 -7
  39. package/dist/reactiveQueries/graphql.js.map +1 -1
  40. package/dist/reactiveQueries/js.d.ts +6 -2
  41. package/dist/reactiveQueries/js.d.ts.map +1 -1
  42. package/dist/reactiveQueries/js.js +8 -5
  43. package/dist/reactiveQueries/js.js.map +1 -1
  44. package/dist/reactiveQueries/sql.d.ts +5 -2
  45. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  46. package/dist/reactiveQueries/sql.js +11 -6
  47. package/dist/reactiveQueries/sql.js.map +1 -1
  48. package/dist/row-query.d.ts +3 -0
  49. package/dist/row-query.d.ts.map +1 -1
  50. package/dist/row-query.js +11 -6
  51. package/dist/row-query.js.map +1 -1
  52. package/dist/store.d.ts +6 -3
  53. package/dist/store.d.ts.map +1 -1
  54. package/dist/store.js +29 -15
  55. package/dist/store.js.map +1 -1
  56. package/package.json +4 -4
  57. package/src/__tests__/react/fixture.tsx +8 -2
  58. package/src/__tests__/react/useRow.test.tsx +10 -10
  59. package/src/__tests__/reactive.test.ts +20 -2
  60. package/src/global-state.ts +2 -9
  61. package/src/index.ts +3 -3
  62. package/src/react/LiveStoreProvider.tsx +13 -4
  63. package/src/react/useQuery.ts +2 -0
  64. package/src/react/useRow.ts +17 -8
  65. package/src/reactive.ts +96 -16
  66. package/src/reactiveQueries/base-class.ts +15 -4
  67. package/src/reactiveQueries/graphql.ts +15 -8
  68. package/src/reactiveQueries/js.ts +14 -6
  69. package/src/reactiveQueries/sql.ts +15 -6
  70. package/src/row-query.ts +20 -7
  71. package/src/store.ts +38 -17
@@ -3,19 +3,19 @@ import { assertNever, shouldNeverHappen } from '@livestore/utils'
3
3
  import * as otel from '@opentelemetry/api'
4
4
  import * as graphql from 'graphql'
5
5
 
6
- import { dbGraph } from '../global-state.js'
6
+ import { globalDbGraph } from '../global-state.js'
7
7
  import type { Thunk } from '../reactive.js'
8
8
  import type { BaseGraphQLContext, RefreshReason, Store } from '../store.js'
9
9
  import { getDurationMsFromSpan } from '../utils/otel.js'
10
- import type { DbContext, GetAtomResult } from './base-class.js'
10
+ import type { DbContext, DbGraph, GetAtomResult } from './base-class.js'
11
11
  import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
12
12
  import { LiveStoreJSQuery } from './js.js'
13
13
 
14
14
  export const queryGraphQL = <TResult extends Record<string, any>, TVariableValues extends Record<string, any>>(
15
15
  document: DocumentNode<TResult, TVariableValues>,
16
16
  genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues),
17
- { label }: { label?: string } = {},
18
- ) => new LiveStoreGraphQLQuery({ document, genVariableValues, label })
17
+ { label, dbGraph }: { label?: string; dbGraph?: DbGraph } = {},
18
+ ) => new LiveStoreGraphQLQuery({ document, genVariableValues, label, dbGraph })
19
19
 
20
20
  export class LiveStoreGraphQLQuery<
21
21
  TResult extends Record<string, any>,
@@ -34,14 +34,18 @@ export class LiveStoreGraphQLQuery<
34
34
 
35
35
  label: string
36
36
 
37
+ protected dbGraph: DbGraph
38
+
37
39
  constructor({
38
40
  document,
39
41
  label,
40
42
  genVariableValues,
43
+ dbGraph,
41
44
  }: {
42
45
  document: DocumentNode<TResult, TVariableValues>
43
46
  genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues)
44
47
  label?: string
48
+ dbGraph?: DbGraph
45
49
  }) {
46
50
  super()
47
51
 
@@ -50,8 +54,10 @@ export class LiveStoreGraphQLQuery<
50
54
  this.label = labelWithDefault
51
55
  this.document = document
52
56
 
57
+ this.dbGraph = dbGraph ?? globalDbGraph
58
+
53
59
  // TODO don't even create a thunk if variables are static
54
- const variableValues$ = dbGraph.makeThunk(
60
+ const variableValues$ = this.dbGraph.makeThunk(
55
61
  (get, _setDebugInfo, { rootOtelContext }, otelContext) => {
56
62
  if (typeof genVariableValues === 'function') {
57
63
  return genVariableValues(makeGetAtomResult(get, otelContext ?? rootOtelContext))
@@ -65,7 +71,7 @@ export class LiveStoreGraphQLQuery<
65
71
  this.variableValues$ = variableValues$
66
72
 
67
73
  const resultsLabel = `${labelWithDefault}:results`
68
- this.results$ = dbGraph.makeThunk<TResult>(
74
+ this.results$ = this.dbGraph.makeThunk<TResult>(
69
75
  (get, setDebugInfo, { store, otelTracer, rootOtelContext }, otelContext) => {
70
76
  const variableValues = get(variableValues$)
71
77
  const { result, queriedTables, durationMs } = this.queryOnce({
@@ -104,6 +110,7 @@ export class LiveStoreGraphQLQuery<
104
110
  },
105
111
  label: `${this.label}:js`,
106
112
  onDestroy: () => this.destroy(),
113
+ dbGraph: this.dbGraph,
107
114
  })
108
115
 
109
116
  queryOnce = ({
@@ -163,7 +170,7 @@ export class LiveStoreGraphQLQuery<
163
170
  }
164
171
 
165
172
  destroy = () => {
166
- dbGraph.destroy(this.variableValues$)
167
- dbGraph.destroy(this.results$)
173
+ this.dbGraph.destroyNode(this.variableValues$)
174
+ this.dbGraph.destroyNode(this.results$)
168
175
  }
169
176
  }
@@ -1,13 +1,14 @@
1
1
  import * as otel from '@opentelemetry/api'
2
2
 
3
- import { dbGraph } from '../global-state.js'
3
+ import { globalDbGraph } from '../global-state.js'
4
4
  import type { Thunk } from '../reactive.js'
5
5
  import type { RefreshReason } from '../store.js'
6
6
  import { getDurationMsFromSpan } from '../utils/otel.js'
7
- import { type DbContext, type GetAtomResult, LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
7
+ import type { DbContext, DbGraph, GetAtomResult } from './base-class.js'
8
+ import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
8
9
 
9
- export const queryJS = <TResult>(fn: (get: GetAtomResult) => TResult, options: { label: string }) =>
10
- new LiveStoreJSQuery<TResult>({ fn, label: options.label })
10
+ export const queryJS = <TResult>(fn: (get: GetAtomResult) => TResult, options: { label: string; dbGraph?: DbGraph }) =>
11
+ new LiveStoreJSQuery<TResult>({ fn, label: options.label, dbGraph: options.dbGraph })
11
12
 
12
13
  export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
13
14
  _tag: 'js' = 'js'
@@ -17,6 +18,8 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
17
18
 
18
19
  label: string
19
20
 
21
+ protected dbGraph: DbGraph
22
+
20
23
  /**
21
24
  * Currently only used for "nested destruction" of piped queries
22
25
  *
@@ -29,20 +32,24 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
29
32
  fn,
30
33
  label,
31
34
  onDestroy,
35
+ dbGraph,
32
36
  }: {
33
37
  label: string
34
38
  fn: (get: GetAtomResult) => TResult
35
39
  /** Currently only used for "nested destruction" of piped queries */
36
40
  onDestroy?: () => void
41
+ dbGraph?: DbGraph
37
42
  }) {
38
43
  super()
39
44
 
40
45
  this.onDestroy = onDestroy
41
46
  this.label = label
42
47
 
48
+ this.dbGraph = dbGraph ?? globalDbGraph
49
+
43
50
  const queryLabel = `${label}:results`
44
51
 
45
- this.results$ = dbGraph.makeThunk(
52
+ this.results$ = this.dbGraph.makeThunk(
46
53
  (get, setDebugInfo, { otelTracer, rootOtelContext }, otelContext) =>
47
54
  otelTracer.startActiveSpan(`js:${label}`, {}, otelContext ?? rootOtelContext, (span) => {
48
55
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
@@ -68,10 +75,11 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
68
75
  },
69
76
  label: `${this.label}:js`,
70
77
  onDestroy: () => this.destroy(),
78
+ dbGraph: this.dbGraph,
71
79
  })
72
80
 
73
81
  destroy = () => {
74
- dbGraph.destroy(this.results$)
82
+ this.dbGraph.destroyNode(this.results$)
75
83
  this.onDestroy?.()
76
84
  }
77
85
  }
@@ -1,13 +1,13 @@
1
1
  import { shouldNeverHappen } from '@livestore/utils'
2
2
  import * as otel from '@opentelemetry/api'
3
3
 
4
- import { dbGraph } from '../global-state.js'
4
+ import { globalDbGraph } from '../global-state.js'
5
5
  import type { Thunk } from '../reactive.js'
6
6
  import type { RefreshReason } from '../store.js'
7
7
  import { getDurationMsFromSpan } from '../utils/otel.js'
8
8
  import type { Bindable } from '../utils/util.js'
9
9
  import { prepareBindValues } from '../utils/util.js'
10
- import type { DbContext, GetAtomResult } from './base-class.js'
10
+ import type { DbContext, DbGraph, GetAtomResult } from './base-class.js'
11
11
  import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
12
12
  import { LiveStoreJSQuery } from './js.js'
13
13
 
@@ -22,6 +22,7 @@ export const querySQL = <Row>(
22
22
  queriedTables?: Set<string>
23
23
  bindValues?: Bindable
24
24
  label?: string
25
+ dbGraph?: DbGraph
25
26
  },
26
27
  ) =>
27
28
  new LiveStoreSQLQuery<Row>({
@@ -29,6 +30,7 @@ export const querySQL = <Row>(
29
30
  genQueryString: query,
30
31
  queriedTables: options?.queriedTables,
31
32
  bindValues: options?.bindValues,
33
+ dbGraph: options?.dbGraph,
32
34
  })
33
35
 
34
36
  /* An object encapsulating a reactive SQL query */
@@ -43,24 +45,29 @@ export class LiveStoreSQLQuery<Row> extends LiveStoreQueryBase<ReadonlyArray<Row
43
45
 
44
46
  label: string
45
47
 
48
+ protected dbGraph: DbGraph
49
+
46
50
  constructor({
47
51
  genQueryString,
48
52
  queriedTables,
49
53
  bindValues,
50
54
  label: label_,
55
+ dbGraph,
51
56
  }: {
52
57
  label?: string
53
58
  genQueryString: string | ((get: GetAtomResult) => string)
54
59
  queriedTables?: Set<string>
55
60
  bindValues?: Bindable
61
+ dbGraph?: DbGraph
56
62
  }) {
57
63
  super()
58
64
 
59
65
  const label = label_ ?? genQueryString.toString()
60
66
  this.label = `sql(${label})`
67
+ this.dbGraph = dbGraph ?? globalDbGraph
61
68
 
62
69
  // TODO don't even create a thunk if query string is static
63
- const queryString$ = dbGraph.makeThunk(
70
+ const queryString$ = this.dbGraph.makeThunk(
64
71
  (get, setDebugInfo, { rootOtelContext }, otelContext) => {
65
72
  if (typeof genQueryString === 'function') {
66
73
  const startMs = performance.now()
@@ -81,7 +88,7 @@ export class LiveStoreSQLQuery<Row> extends LiveStoreQueryBase<ReadonlyArray<Row
81
88
 
82
89
  const queriedTablesRef = { current: queriedTables }
83
90
 
84
- const results$ = dbGraph.makeThunk<ReadonlyArray<Row>>(
91
+ const results$ = this.dbGraph.makeThunk<ReadonlyArray<Row>>(
85
92
  (get, setDebugInfo, { store, otelTracer, rootOtelContext }, otelContext) =>
86
93
  otelTracer.startActiveSpan(
87
94
  'sql:...', // NOTE span name will be overridden further down
@@ -140,6 +147,7 @@ export class LiveStoreSQLQuery<Row> extends LiveStoreQueryBase<ReadonlyArray<Row
140
147
  },
141
148
  label: `${this.label}:js`,
142
149
  onDestroy: () => this.destroy(),
150
+ dbGraph: this.dbGraph,
143
151
  })
144
152
 
145
153
  /** Returns a reactive query */
@@ -156,10 +164,11 @@ export class LiveStoreSQLQuery<Row> extends LiveStoreQueryBase<ReadonlyArray<Row
156
164
  },
157
165
  label: `${this.label}:first`,
158
166
  onDestroy: () => this.destroy(),
167
+ dbGraph: this.dbGraph,
159
168
  })
160
169
 
161
170
  destroy = () => {
162
- dbGraph.destroy(this.queryString$)
163
- dbGraph.destroy(this.results$)
171
+ this.dbGraph.destroyNode(this.queryString$)
172
+ this.dbGraph.destroyNode(this.results$)
164
173
  }
165
174
  }
package/src/row-query.ts CHANGED
@@ -5,11 +5,13 @@ import { SqliteAst, SqliteDsl } from 'effect-db-schema'
5
5
 
6
6
  import type { InMemoryDatabase } from './inMemoryDatabase.js'
7
7
  import { migrateTable } from './migrations.js'
8
+ import type { Ref } from './reactive.js'
9
+ import type { DbContext, DbGraph } from './reactiveQueries/base-class.js'
8
10
  import type { LiveStoreJSQuery } from './reactiveQueries/js.js'
9
11
  import { LiveStoreSQLQuery } from './reactiveQueries/sql.js'
10
12
  import { SCHEMA_META_TABLE } from './schema/index.js'
11
13
  import type { TableDef } from './schema/table-def.js'
12
- import type { Store } from './store.js'
14
+ import type { RefreshReason, Store } from './store.js'
13
15
  import { prepareBindValues, sql } from './utils/util.js'
14
16
 
15
17
  export type RowQueryArgs<TTableDef extends TableDef> = TTableDef['options']['isSingleton'] extends true
@@ -19,6 +21,7 @@ export type RowQueryArgs<TTableDef extends TableDef> = TTableDef['options']['isS
19
21
  otelContext?: otel.Context
20
22
  defaultValues: Partial<RowResult<TTableDef>>
21
23
  skipInsertDefaultRow?: boolean
24
+ dbGraph?: DbGraph
22
25
  }
23
26
  : {
24
27
  table: TTableDef
@@ -27,13 +30,14 @@ export type RowQueryArgs<TTableDef extends TableDef> = TTableDef['options']['isS
27
30
  id: string
28
31
  defaultValues: Partial<RowResult<TTableDef>>
29
32
  skipInsertDefaultRow?: boolean
33
+ dbGraph?: DbGraph
30
34
  }
31
35
 
32
36
  // TODO also allow other where clauses and multiple rows
33
37
  export const rowQuery = <TTableDef extends TableDef>(
34
38
  args: RowQueryArgs<TTableDef>,
35
39
  ): LiveStoreJSQuery<RowResult<TTableDef>> => {
36
- const { table, store, defaultValues, skipInsertDefaultRow } = args
40
+ const { table, store, defaultValues, skipInsertDefaultRow, dbGraph } = args
37
41
  const otelContext = args.otelContext ?? store.otel.queriesSpanContext
38
42
  const id: string | undefined = (args as any).id
39
43
 
@@ -64,11 +68,19 @@ export const rowQuery = <TTableDef extends TableDef>(
64
68
  })
65
69
  }
66
70
 
67
- store.tableRefs[componentTableName] = store.graph.makeRef(null, {
68
- equal: () => false,
69
- label: componentTableName,
70
- meta: { liveStoreRefType: 'table' },
71
- })
71
+ const label = `tableRef:${componentTableName}`
72
+
73
+ const existingTableRefFromGraph = Array.from(store.graph.atoms.values()).find(
74
+ (_) => _._tag === 'ref' && _.label === label,
75
+ ) as Ref<null, DbContext, RefreshReason> | undefined
76
+
77
+ store.tableRefs[componentTableName] =
78
+ existingTableRefFromGraph ??
79
+ store.graph.makeRef(null, {
80
+ equal: () => false,
81
+ label,
82
+ meta: { liveStoreRefType: 'table' },
83
+ })
72
84
  }
73
85
 
74
86
  if (skipInsertDefaultRow !== true) {
@@ -89,6 +101,7 @@ export const rowQuery = <TTableDef extends TableDef>(
89
101
  label: `localState:query:${stateSchema.name}${id === undefined ? '' : `:${id}`}`,
90
102
  genQueryString: queryStr,
91
103
  queriedTables: new Set([componentTableName]),
104
+ dbGraph,
92
105
  }).pipe<TComponentState>((results) => {
93
106
  if (results.length === 0) return shouldNeverHappen(`No results for query ${queryStr}`)
94
107
 
package/src/store.ts CHANGED
@@ -6,12 +6,12 @@ import type * as Sqlite from 'sqlite-esm'
6
6
  import { v4 as uuid } from 'uuid'
7
7
 
8
8
  import type { LiveStoreEvent } from './events.js'
9
- import { dbGraph, dynamicallyRegisteredTables } from './global-state.js'
9
+ import { dynamicallyRegisteredTables, globalDbGraph } from './global-state.js'
10
10
  import { InMemoryDatabase } from './inMemoryDatabase.js'
11
11
  import { migrateDb } from './migrations.js'
12
12
  import type { StackInfo } from './react/utils/stack-info.js'
13
13
  import type { DebugRefreshReasonBase, ReactiveGraph, Ref } from './reactive.js'
14
- import type { DbContext, ILiveStoreQuery } from './reactiveQueries/base-class.js'
14
+ import type { DbContext, DbGraph, ILiveStoreQuery } from './reactiveQueries/base-class.js'
15
15
  import type { LiveStoreGraphQLQuery } from './reactiveQueries/graphql.js'
16
16
  import type { LiveStoreJSQuery } from './reactiveQueries/js.js'
17
17
  import type { LiveStoreSQLQuery } from './reactiveQueries/sql.js'
@@ -54,6 +54,7 @@ export type StoreOptions<TGraphQLContext extends BaseGraphQLContext> = {
54
54
  graphQLOptions?: GraphQLOptions<TGraphQLContext>
55
55
  otelTracer: otel.Tracer
56
56
  otelRootSpanContext: otel.Context
57
+ dbGraph?: DbGraph
57
58
  }
58
59
 
59
60
  export type RefreshReason =
@@ -99,7 +100,11 @@ export type StoreOtel = {
99
100
  queriesSpanContext: otel.Context
100
101
  }
101
102
 
103
+ let storeCount = 0
104
+ const uniqueStoreId = () => `store-${++storeCount}`
105
+
102
106
  export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLContext> {
107
+ id = uniqueStoreId()
103
108
  graph: ReactiveGraph<RefreshReason, QueryDebugInfo, DbContext>
104
109
  inMemoryDB: InMemoryDatabase
105
110
  // TODO refactor
@@ -124,6 +129,7 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
124
129
  schema,
125
130
  storage,
126
131
  graphQLOptions,
132
+ dbGraph,
127
133
  otelTracer,
128
134
  otelRootSpanContext,
129
135
  }: StoreOptions<TGraphQLContext>) {
@@ -141,8 +147,7 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
141
147
  const queriesSpan = otelTracer.startSpan('LiveStore:queries', {}, otelRootSpanContext)
142
148
  const otelQueriesSpanContext = otel.trace.setSpan(otel.context.active(), queriesSpan)
143
149
 
144
- // TODO allow passing in a custom graph
145
- this.graph = dbGraph
150
+ this.graph = dbGraph ?? globalDbGraph
146
151
  this.graph.context = { store: this, otelTracer, rootOtelContext: otelQueriesSpanContext }
147
152
 
148
153
  this.otel = {
@@ -157,12 +162,19 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
157
162
  // TODO activate dynamic tables
158
163
  ...Array.from(dynamicallyRegisteredTables.values()).map((_) => _.schema.name),
159
164
  ])
165
+ const existingTableRefs = new Map(
166
+ Array.from(this.graph.atoms.values())
167
+ .filter((_): _ is Ref<any, any, any> => _._tag === 'ref' && _.label?.startsWith('tableRef:') === true)
168
+ .map((_) => [_.label!.slice('tableRef:'.length), _] as const),
169
+ )
160
170
  for (const tableName of allTableNames) {
161
- this.tableRefs[tableName] = this.graph.makeRef(null, {
162
- equal: () => false,
163
- label: tableName,
164
- meta: { liveStoreRefType: 'table' },
165
- })
171
+ this.tableRefs[tableName] =
172
+ existingTableRefs.get(tableName) ??
173
+ this.graph.makeRef(null, {
174
+ equal: () => false,
175
+ label: `tableRef:${tableName}`,
176
+ meta: { liveStoreRefType: 'table' },
177
+ })
166
178
  }
167
179
 
168
180
  if (graphQLOptions) {
@@ -214,7 +226,7 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
214
226
 
215
227
  const unsubscribe = () => {
216
228
  try {
217
- this.graph.destroy(effect)
229
+ this.graph.destroyNode(effect)
218
230
  this.activeQueries.remove(query as LiveStoreQuery)
219
231
  onUnsubsubscribe?.()
220
232
  } finally {
@@ -232,12 +244,14 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
232
244
  * Currently only used when shutting down the app for debugging purposes (e.g. to close Otel spans).
233
245
  */
234
246
  destroy = () => {
235
- Object.values(this.tableRefs).forEach((tableRef) => this.graph.destroy(tableRef))
247
+ for (const tableRef of Object.values(this.tableRefs)) {
248
+ for (const superComp of tableRef.super) {
249
+ this.graph.removeEdge(superComp, tableRef)
250
+ }
251
+ }
236
252
 
237
253
  otel.trace.getSpan(this.otel.applyEventsSpanContext)!.end()
238
254
  otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
239
-
240
- // TODO destroy active subscriptions
241
255
  }
242
256
 
243
257
  /* Apply a single write event to the store, and refresh all queries in response */
@@ -533,16 +547,18 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
533
547
  graphQLOptions,
534
548
  otelTracer = makeNoopTracer(),
535
549
  otelRootSpanContext = otel.context.active(),
536
- boot,
537
550
  sqlite3,
551
+ boot,
552
+ dbGraph,
538
553
  }: {
539
554
  schema: LiveStoreSchema
540
555
  loadStorage: () => StorageInit | Promise<StorageInit>
541
556
  graphQLOptions?: GraphQLOptions<TGraphQLContext>
542
557
  otelTracer?: otel.Tracer
543
558
  otelRootSpanContext?: otel.Context
544
- boot?: (db: InMemoryDatabase, parentSpan: otel.Span) => unknown | Promise<unknown>
545
559
  sqlite3: Sqlite.Sqlite3Static
560
+ boot?: (db: InMemoryDatabase, parentSpan: otel.Span) => unknown | Promise<unknown>
561
+ dbGraph?: DbGraph
546
562
  }): Promise<Store<TGraphQLContext>> => {
547
563
  return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
548
564
  try {
@@ -573,12 +589,17 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
573
589
 
574
590
  const db = InMemoryDatabase.load({ data: persistedData, otelTracer, otelRootSpanContext, sqlite3 })
575
591
 
592
+ const txnKeywords = ['begin transaction;', 'commit;', 'rollback;']
593
+ const isTxnQuery = (query: string) => txnKeywords.some((_) => query.toLowerCase().startsWith(_))
594
+
576
595
  // Proxy to `db` that also mirrors `execute` calls to `storage`
577
596
  const dbProxy = new Proxy(db, {
578
597
  get: (db, prop, receiver) => {
579
598
  if (prop === 'execute') {
580
599
  const execute: InMemoryDatabase['execute'] = (query, bindValues, writeTables, options) => {
581
- storage.execute(query, bindValues, span)
600
+ if (isTxnQuery(query) === false) {
601
+ storage.execute(query, bindValues, span)
602
+ }
582
603
  return db.execute(query, bindValues, writeTables, options)
583
604
  }
584
605
  return execute
@@ -617,7 +638,7 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
617
638
  // Think about what to do about this case.
618
639
  // await applySchema(db, schema)
619
640
  return Store.createStore<TGraphQLContext>(
620
- { db, dbProxy, schema, storage, graphQLOptions, otelTracer, otelRootSpanContext },
641
+ { db, dbProxy, schema, storage, graphQLOptions, otelTracer, otelRootSpanContext, dbGraph },
621
642
  span,
622
643
  )
623
644
  } finally {