@livestore/livestore 0.0.34 → 0.0.36
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/react/fixture.d.ts +3 -1
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +6 -3
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/__tests__/react/useRow.test.js +10 -10
- package/dist/__tests__/react/useRow.test.js.map +1 -1
- package/dist/__tests__/reactive.test.js +13 -2
- package/dist/__tests__/reactive.test.js.map +1 -1
- package/dist/global-state.d.ts +1 -4
- package/dist/global-state.d.ts.map +1 -1
- package/dist/global-state.js +2 -6
- package/dist/global-state.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +9 -3
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/useQuery.d.ts.map +1 -1
- package/dist/react/useQuery.js +1 -0
- package/dist/react/useQuery.js.map +1 -1
- package/dist/react/useRow.d.ts +7 -3
- package/dist/react/useRow.d.ts.map +1 -1
- package/dist/react/useRow.js +7 -5
- package/dist/react/useRow.js.map +1 -1
- package/dist/reactive.d.ts +21 -6
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +60 -12
- package/dist/reactive.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +5 -2
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +8 -3
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/graphql.d.ts +6 -3
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +10 -7
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +6 -2
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js +8 -5
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +5 -2
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +11 -6
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/row-query.d.ts +3 -0
- package/dist/row-query.d.ts.map +1 -1
- package/dist/row-query.js +11 -6
- package/dist/row-query.js.map +1 -1
- package/dist/store.d.ts +6 -3
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +29 -15
- package/dist/store.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/react/fixture.tsx +8 -2
- package/src/__tests__/react/useRow.test.tsx +10 -10
- package/src/__tests__/reactive.test.ts +20 -2
- package/src/global-state.ts +2 -9
- package/src/index.ts +3 -3
- package/src/react/LiveStoreProvider.tsx +13 -4
- package/src/react/useQuery.ts +2 -0
- package/src/react/useRow.ts +17 -8
- package/src/reactive.ts +96 -16
- package/src/reactiveQueries/base-class.ts +15 -4
- package/src/reactiveQueries/graphql.ts +15 -8
- package/src/reactiveQueries/js.ts +14 -6
- package/src/reactiveQueries/sql.ts +15 -6
- package/src/row-query.ts +20 -7
- 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 {
|
|
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.
|
|
167
|
-
dbGraph.
|
|
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 {
|
|
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 {
|
|
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.
|
|
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 {
|
|
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.
|
|
163
|
-
dbGraph.
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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 {
|
|
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
|
-
|
|
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] =
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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.
|
|
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)
|
|
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
|
-
|
|
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 {
|