@livestore/livestore 0.2.0 → 0.3.0-dev.11
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/SqliteDbWrapper.d.ts +54 -0
- package/dist/SqliteDbWrapper.d.ts.map +1 -0
- package/dist/SqliteDbWrapper.js +212 -0
- package/dist/SqliteDbWrapper.js.map +1 -0
- package/dist/SynchronousDatabaseWrapper.d.ts +20 -6
- package/dist/SynchronousDatabaseWrapper.d.ts.map +1 -1
- package/dist/SynchronousDatabaseWrapper.js +38 -6
- package/dist/SynchronousDatabaseWrapper.js.map +1 -1
- package/dist/__tests__/fixture.d.ts +252 -0
- package/dist/__tests__/fixture.d.ts.map +1 -0
- package/dist/__tests__/fixture.js +18 -0
- package/dist/__tests__/fixture.js.map +1 -0
- package/dist/effect/LiveStore.d.ts +16 -12
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +14 -14
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/live-queries/base-class.d.ts +64 -21
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +56 -13
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/computed.d.ts +7 -7
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +35 -11
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/{sql.d.ts → db-query.d.ts} +19 -14
- package/dist/live-queries/db-query.d.ts.map +1 -0
- package/dist/live-queries/db-query.js +244 -0
- package/dist/live-queries/db-query.js.map +1 -0
- package/dist/live-queries/db-query.test.d.ts +2 -0
- package/dist/live-queries/db-query.test.d.ts.map +1 -0
- package/dist/live-queries/db-query.test.js +123 -0
- package/dist/live-queries/db-query.test.js.map +1 -0
- package/dist/live-queries/db.d.ts +12 -15
- package/dist/live-queries/db.d.ts.map +1 -1
- package/dist/live-queries/db.js +72 -48
- package/dist/live-queries/db.js.map +1 -1
- package/dist/live-queries/db.test.js +18 -15
- package/dist/live-queries/db.test.js.map +1 -1
- package/dist/live-queries/graphql.d.ts +8 -8
- package/dist/live-queries/graphql.d.ts.map +1 -1
- package/dist/live-queries/graphql.js +35 -9
- package/dist/live-queries/graphql.js.map +1 -1
- package/dist/live-queries/make-ref.d.ts +20 -0
- package/dist/live-queries/make-ref.d.ts.map +1 -0
- package/dist/live-queries/make-ref.js +33 -0
- package/dist/live-queries/make-ref.js.map +1 -0
- package/dist/reactive.d.ts +15 -13
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +15 -9
- package/dist/reactive.js.map +1 -1
- package/dist/row-query-utils.d.ts +4 -4
- package/dist/row-query-utils.d.ts.map +1 -1
- package/dist/row-query-utils.js +14 -10
- package/dist/row-query-utils.js.map +1 -1
- package/dist/store/create-store.d.ts +13 -12
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +27 -33
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts +3 -3
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +56 -34
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-types.d.ts +18 -18
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store.d.ts +57 -38
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +225 -188
- package/dist/store/store.js.map +1 -1
- package/dist/store/store.test.d.ts +2 -0
- package/dist/store/store.test.d.ts.map +1 -0
- package/dist/store/store.test.js +27 -0
- package/dist/store/store.test.js.map +1 -0
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +3 -2
- package/dist/utils/dev.js.map +1 -1
- package/dist/utils/expo.d.ts +2 -0
- package/dist/utils/expo.d.ts.map +1 -0
- package/dist/utils/expo.js +8 -0
- package/dist/utils/expo.js.map +1 -0
- package/dist/utils/function-string.d.ts +7 -0
- package/dist/utils/function-string.d.ts.map +1 -0
- package/dist/utils/function-string.js +9 -0
- package/dist/utils/function-string.js.map +1 -0
- package/dist/utils/stack-info.d.ts.map +1 -1
- package/dist/utils/stack-info.js +6 -1
- package/dist/utils/stack-info.js.map +1 -1
- package/dist/utils/stack-info.test.js +54 -1
- package/dist/utils/stack-info.test.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +2 -6
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/fixture.js +7 -13
- package/dist/utils/tests/fixture.js.map +1 -1
- package/dist/utils/tests/mod.d.ts +1 -0
- package/dist/utils/tests/mod.d.ts.map +1 -1
- package/dist/utils/tests/mod.js +1 -0
- package/dist/utils/tests/mod.js.map +1 -1
- package/dist/utils/tests/otel.d.ts +60 -1
- package/dist/utils/tests/otel.d.ts.map +1 -1
- package/dist/utils/tests/otel.js +65 -4
- package/dist/utils/tests/otel.js.map +1 -1
- package/package.json +12 -12
- package/src/{SynchronousDatabaseWrapper.ts → SqliteDbWrapper.ts} +59 -13
- package/src/ambient.d.ts +1 -1
- package/src/effect/LiveStore.ts +32 -33
- package/src/index.ts +14 -7
- package/src/live-queries/__snapshots__/{db.test.ts.snap → db-query.test.ts.snap} +220 -69
- package/src/live-queries/base-class.ts +160 -40
- package/src/live-queries/computed.ts +45 -19
- package/src/live-queries/{db.test.ts → db-query.test.ts} +23 -12
- package/src/live-queries/{db.ts → db-query.ts} +124 -61
- package/src/live-queries/graphql.ts +47 -21
- package/src/live-queries/make-ref.ts +47 -0
- package/src/reactive.ts +52 -27
- package/src/row-query-utils.ts +29 -18
- package/src/store/create-store.ts +106 -113
- package/src/store/devtools.ts +65 -39
- package/src/store/store-types.ts +20 -18
- package/src/store/store.ts +361 -290
- package/src/utils/dev.ts +4 -2
- package/src/utils/function-string.ts +12 -0
- package/src/utils/stack-info.test.ts +58 -1
- package/src/utils/stack-info.ts +6 -1
- package/src/utils/tests/fixture.ts +6 -16
- package/src/utils/tests/mod.ts +1 -0
- package/src/utils/tests/otel.ts +71 -5
- package/dist/live-queries/sql.d.ts.map +0 -1
- package/dist/live-queries/sql.js +0 -175
- package/dist/live-queries/sql.js.map +0 -1
- package/dist/live-queries/sql.test.d.ts +0 -2
- package/dist/live-queries/sql.test.d.ts.map +0 -1
- package/dist/live-queries/sql.test.js +0 -285
- package/dist/live-queries/sql.test.js.map +0 -1
- package/dist/reactiveQueries/base-class.d.ts +0 -64
- package/dist/reactiveQueries/base-class.d.ts.map +0 -1
- package/dist/reactiveQueries/base-class.js +0 -31
- package/dist/reactiveQueries/base-class.js.map +0 -1
- package/dist/reactiveQueries/computed.d.ts +0 -26
- package/dist/reactiveQueries/computed.d.ts.map +0 -1
- package/dist/reactiveQueries/computed.js +0 -38
- package/dist/reactiveQueries/computed.js.map +0 -1
- package/dist/reactiveQueries/graphql.d.ts +0 -49
- package/dist/reactiveQueries/graphql.d.ts.map +0 -1
- package/dist/reactiveQueries/graphql.js +0 -122
- package/dist/reactiveQueries/graphql.js.map +0 -1
- package/dist/reactiveQueries/sql.d.ts +0 -62
- package/dist/reactiveQueries/sql.d.ts.map +0 -1
- package/dist/reactiveQueries/sql.js +0 -175
- package/dist/reactiveQueries/sql.js.map +0 -1
- package/dist/reactiveQueries/sql.test.d.ts +0 -2
- package/dist/reactiveQueries/sql.test.d.ts.map +0 -1
- package/dist/reactiveQueries/sql.test.js +0 -285
- package/dist/reactiveQueries/sql.test.js.map +0 -1
- package/dist/row-query.d.ts +0 -16
- package/dist/row-query.d.ts.map +0 -1
- package/dist/row-query.js +0 -30
- package/dist/row-query.js.map +0 -1
- package/src/global-state.ts +0 -20
|
@@ -5,13 +5,12 @@ import { Schema, TreeFormatter } from '@livestore/utils/effect'
|
|
|
5
5
|
import * as otel from '@opentelemetry/api'
|
|
6
6
|
import * as graphql from 'graphql'
|
|
7
7
|
|
|
8
|
-
import { globalReactivityGraph } from '../global-state.js'
|
|
9
8
|
import { isThunk, type Thunk } from '../reactive.js'
|
|
10
9
|
import type { Store } from '../store/store.js'
|
|
11
10
|
import type { BaseGraphQLContext, RefreshReason } from '../store/store-types.js'
|
|
12
11
|
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
13
|
-
import type {
|
|
14
|
-
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
12
|
+
import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
|
|
13
|
+
import { defCounterRef, depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
|
|
15
14
|
|
|
16
15
|
export type MapResult<To, From> = ((res: From, get: GetAtomResult) => To) | Schema.Schema<To, From>
|
|
17
16
|
|
|
@@ -22,17 +21,37 @@ export const queryGraphQL = <
|
|
|
22
21
|
>(
|
|
23
22
|
document: DocumentNode<TResult, TVariableValues>,
|
|
24
23
|
genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues),
|
|
25
|
-
{
|
|
26
|
-
label,
|
|
27
|
-
reactivityGraph,
|
|
28
|
-
map,
|
|
29
|
-
}: {
|
|
24
|
+
options: {
|
|
30
25
|
label?: string
|
|
31
|
-
reactivityGraph?: ReactivityGraph
|
|
26
|
+
// reactivityGraph?: ReactivityGraph
|
|
32
27
|
map?: MapResult<TResultMapped, TResult>
|
|
28
|
+
deps?: DepKey
|
|
33
29
|
} = {},
|
|
34
|
-
):
|
|
35
|
-
|
|
30
|
+
): LiveQueryDef<TResultMapped, QueryInfo.None> => {
|
|
31
|
+
const documentName = graphql.getOperationAST(document)?.name?.value
|
|
32
|
+
const hash = options.deps
|
|
33
|
+
? depsToString(options.deps)
|
|
34
|
+
: (documentName ?? shouldNeverHappen('No document name found and no deps provided'))
|
|
35
|
+
const label = options.label ?? documentName ?? 'graphql'
|
|
36
|
+
const map = options.map
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
_tag: 'def',
|
|
40
|
+
id: ++defCounterRef.current,
|
|
41
|
+
make: withRCMap(hash, (ctx, _otelContext) => {
|
|
42
|
+
return new LiveStoreGraphQLQuery({
|
|
43
|
+
document,
|
|
44
|
+
genVariableValues,
|
|
45
|
+
label,
|
|
46
|
+
map,
|
|
47
|
+
reactivityGraph: ctx.reactivityGraph.deref()!,
|
|
48
|
+
})
|
|
49
|
+
}),
|
|
50
|
+
label,
|
|
51
|
+
hash,
|
|
52
|
+
queryInfo: { _tag: 'None' },
|
|
53
|
+
}
|
|
54
|
+
}
|
|
36
55
|
|
|
37
56
|
export class LiveStoreGraphQLQuery<
|
|
38
57
|
TResult extends Record<string, any>,
|
|
@@ -46,13 +65,13 @@ export class LiveStoreGraphQLQuery<
|
|
|
46
65
|
document: DocumentNode<TResult, TVariableValues>
|
|
47
66
|
|
|
48
67
|
/** A reactive thunk representing the query results */
|
|
49
|
-
results$: Thunk<TResultMapped,
|
|
68
|
+
results$: Thunk<TResultMapped, ReactivityGraphContext, RefreshReason>
|
|
50
69
|
|
|
51
|
-
variableValues$: Thunk<TVariableValues,
|
|
70
|
+
variableValues$: Thunk<TVariableValues, ReactivityGraphContext, RefreshReason> | undefined
|
|
52
71
|
|
|
53
72
|
label: string
|
|
54
73
|
|
|
55
|
-
|
|
74
|
+
reactivityGraph: ReactivityGraph
|
|
56
75
|
|
|
57
76
|
queryInfo: QueryInfo.None = { _tag: 'None' }
|
|
58
77
|
|
|
@@ -68,7 +87,7 @@ export class LiveStoreGraphQLQuery<
|
|
|
68
87
|
document: DocumentNode<TResult, TVariableValues>
|
|
69
88
|
genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues)
|
|
70
89
|
label?: string
|
|
71
|
-
reactivityGraph
|
|
90
|
+
reactivityGraph: ReactivityGraph
|
|
72
91
|
map?: MapResult<TResultMapped, TResult>
|
|
73
92
|
}) {
|
|
74
93
|
super()
|
|
@@ -78,7 +97,7 @@ export class LiveStoreGraphQLQuery<
|
|
|
78
97
|
this.label = labelWithDefault
|
|
79
98
|
this.document = document
|
|
80
99
|
|
|
81
|
-
this.reactivityGraph = reactivityGraph
|
|
100
|
+
this.reactivityGraph = reactivityGraph
|
|
82
101
|
|
|
83
102
|
this.mapResult =
|
|
84
103
|
map === undefined
|
|
@@ -102,8 +121,10 @@ export class LiveStoreGraphQLQuery<
|
|
|
102
121
|
|
|
103
122
|
if (typeof genVariableValues === 'function') {
|
|
104
123
|
variableValues$OrvariableValues = this.reactivityGraph.makeThunk(
|
|
105
|
-
(get, _setDebugInfo,
|
|
106
|
-
return genVariableValues(
|
|
124
|
+
(get, _setDebugInfo, ctx, otelContext) => {
|
|
125
|
+
return genVariableValues(
|
|
126
|
+
makeGetAtomResult(get, ctx, otelContext ?? ctx.rootOtelContext, this.dependencyQueriesRef),
|
|
127
|
+
)
|
|
107
128
|
},
|
|
108
129
|
{ label: `${labelWithDefault}:variableValues`, meta: { liveStoreThunkType: 'graphql.variables' } },
|
|
109
130
|
)
|
|
@@ -114,9 +135,10 @@ export class LiveStoreGraphQLQuery<
|
|
|
114
135
|
|
|
115
136
|
const resultsLabel = `${labelWithDefault}:results`
|
|
116
137
|
this.results$ = this.reactivityGraph.makeThunk<TResultMapped>(
|
|
117
|
-
(get, setDebugInfo,
|
|
138
|
+
(get, setDebugInfo, ctx, otelContext, debugRefreshReason) => {
|
|
139
|
+
const { store, otelTracer, rootOtelContext } = ctx
|
|
118
140
|
const variableValues = isThunk(variableValues$OrvariableValues)
|
|
119
|
-
? (get(variableValues$OrvariableValues) as TVariableValues)
|
|
141
|
+
? (get(variableValues$OrvariableValues, otelContext, debugRefreshReason) as TVariableValues)
|
|
120
142
|
: (variableValues$OrvariableValues as TVariableValues)
|
|
121
143
|
const { result, queriedTables, durationMs } = this.queryOnce({
|
|
122
144
|
document,
|
|
@@ -124,7 +146,7 @@ export class LiveStoreGraphQLQuery<
|
|
|
124
146
|
otelContext: otelContext ?? rootOtelContext,
|
|
125
147
|
otelTracer,
|
|
126
148
|
store: store as Store<TContext>,
|
|
127
|
-
get: makeGetAtomResult(get, otelContext ?? rootOtelContext),
|
|
149
|
+
get: makeGetAtomResult(get, ctx, otelContext ?? rootOtelContext, this.dependencyQueriesRef),
|
|
128
150
|
})
|
|
129
151
|
|
|
130
152
|
// Add dependencies on any tables that were used
|
|
@@ -215,5 +237,9 @@ export class LiveStoreGraphQLQuery<
|
|
|
215
237
|
}
|
|
216
238
|
|
|
217
239
|
this.reactivityGraph.destroyNode(this.results$)
|
|
240
|
+
|
|
241
|
+
for (const query of this.dependencyQueriesRef) {
|
|
242
|
+
query.deref()
|
|
243
|
+
}
|
|
218
244
|
}
|
|
219
245
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { nanoid } from '@livestore/utils/nanoid'
|
|
2
|
+
|
|
3
|
+
import type * as RG from '../reactive.js'
|
|
4
|
+
import type { RefreshReason } from '../store/store-types.js'
|
|
5
|
+
import type { ILiveQueryRef, ILiveQueryRefDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
|
|
6
|
+
import { withRCMap } from './base-class.js'
|
|
7
|
+
|
|
8
|
+
export const makeRef = <T>(
|
|
9
|
+
defaultValue: T,
|
|
10
|
+
options?: {
|
|
11
|
+
label?: string
|
|
12
|
+
},
|
|
13
|
+
): ILiveQueryRefDef<T> => {
|
|
14
|
+
const id = nanoid()
|
|
15
|
+
return {
|
|
16
|
+
_tag: 'live-ref-def',
|
|
17
|
+
defaultValue,
|
|
18
|
+
make: withRCMap(id, (ctx) => new LiveQueryRef(defaultValue, ctx.reactivityGraph.deref()!, options)),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class LiveQueryRef<T> implements ILiveQueryRef<T> {
|
|
23
|
+
_tag = 'live-ref' as const
|
|
24
|
+
readonly ref: RG.Ref<T, ReactivityGraphContext, RefreshReason>
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private defaultValue: T,
|
|
28
|
+
readonly reactivityGraph: ReactivityGraph,
|
|
29
|
+
private options?: {
|
|
30
|
+
label?: string
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
this.ref = reactivityGraph.makeRef(defaultValue, { label: options?.label })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
set = (value: T) => {
|
|
37
|
+
this.reactivityGraph.setRef(this.ref, value)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get = () => {
|
|
41
|
+
return this.ref.computeResult()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
destroy = () => {
|
|
45
|
+
this.reactivityGraph.destroyNode(this.ref)
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/reactive.ts
CHANGED
|
@@ -32,7 +32,11 @@ import type * as otel from '@opentelemetry/api'
|
|
|
32
32
|
export const NOT_REFRESHED_YET = Symbol.for('NOT_REFRESHED_YET')
|
|
33
33
|
export type NOT_REFRESHED_YET = typeof NOT_REFRESHED_YET
|
|
34
34
|
|
|
35
|
-
export type GetAtom = <T>(
|
|
35
|
+
export type GetAtom = <T>(
|
|
36
|
+
atom: Atom<T, any, any>,
|
|
37
|
+
otelContext?: otel.Context | undefined,
|
|
38
|
+
debugRefreshReason?: TODO | undefined,
|
|
39
|
+
) => T
|
|
36
40
|
|
|
37
41
|
export type Ref<T, TContext, TDebugRefreshReason extends DebugRefreshReason> = {
|
|
38
42
|
_tag: 'ref'
|
|
@@ -42,7 +46,7 @@ export type Ref<T, TContext, TDebugRefreshReason extends DebugRefreshReason> = {
|
|
|
42
46
|
previousResult: T
|
|
43
47
|
computeResult: () => T
|
|
44
48
|
sub: Set<Atom<any, TContext, TDebugRefreshReason>> // always empty
|
|
45
|
-
super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect
|
|
49
|
+
super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>>
|
|
46
50
|
label?: string
|
|
47
51
|
/** Container for meta information (e.g. the LiveStore Store) */
|
|
48
52
|
meta?: any
|
|
@@ -58,7 +62,7 @@ export type Thunk<TResult, TContext, TDebugRefreshReason extends DebugRefreshRea
|
|
|
58
62
|
computeResult: (otelContext?: otel.Context, debugRefreshReason?: TDebugRefreshReason) => TResult
|
|
59
63
|
previousResult: TResult | NOT_REFRESHED_YET
|
|
60
64
|
sub: Set<Atom<any, TContext, TDebugRefreshReason>>
|
|
61
|
-
super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect
|
|
65
|
+
super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>>
|
|
62
66
|
label?: string
|
|
63
67
|
/** Container for meta information (e.g. the LiveStore Store) */
|
|
64
68
|
meta?: any
|
|
@@ -72,11 +76,11 @@ export type Atom<T, TContext, TDebugRefreshReason extends DebugRefreshReason> =
|
|
|
72
76
|
| Ref<T, TContext, TDebugRefreshReason>
|
|
73
77
|
| Thunk<T, TContext, TDebugRefreshReason>
|
|
74
78
|
|
|
75
|
-
export type Effect = {
|
|
79
|
+
export type Effect<TDebugRefreshReason extends DebugRefreshReason> = {
|
|
76
80
|
_tag: 'effect'
|
|
77
81
|
id: string
|
|
78
82
|
isDestroyed: boolean
|
|
79
|
-
doEffect: (otelContext?: otel.Context) => void
|
|
83
|
+
doEffect: (otelContext?: otel.Context | undefined, debugRefreshReason?: TDebugRefreshReason | undefined) => void
|
|
80
84
|
sub: Set<Atom<any, TODO, TODO>>
|
|
81
85
|
label?: string
|
|
82
86
|
invocations: number
|
|
@@ -84,7 +88,7 @@ export type Effect = {
|
|
|
84
88
|
|
|
85
89
|
export type Node<T, TContext, TDebugRefreshReason extends DebugRefreshReason> =
|
|
86
90
|
| Atom<T, TContext, TDebugRefreshReason>
|
|
87
|
-
| Effect
|
|
91
|
+
| Effect<TDebugRefreshReason>
|
|
88
92
|
|
|
89
93
|
export const isThunk = <T, TContext, TDebugRefreshReason extends DebugRefreshReason>(
|
|
90
94
|
obj: unknown,
|
|
@@ -166,7 +170,7 @@ export type SerializedThunk = Readonly<
|
|
|
166
170
|
|
|
167
171
|
export type SerializedEffect = Readonly<
|
|
168
172
|
PrettifyFlat<
|
|
169
|
-
Pick<Effect
|
|
173
|
+
Pick<Effect<any>, '_tag' | 'id' | 'label' | 'invocations' | 'isDestroyed'> & {
|
|
170
174
|
sub: ReadonlyArray<string>
|
|
171
175
|
}
|
|
172
176
|
>
|
|
@@ -187,6 +191,13 @@ const uniqueRefreshInfoId = () => `refresh-info-${++refreshInfoIdCounter}`
|
|
|
187
191
|
let globalGraphIdCounter = 0
|
|
188
192
|
const uniqueGraphId = () => `graph-${++globalGraphIdCounter}`
|
|
189
193
|
|
|
194
|
+
/** Used for testing */
|
|
195
|
+
export const __resetIds = () => {
|
|
196
|
+
nodeIdCounter = 0
|
|
197
|
+
refreshInfoIdCounter = 0
|
|
198
|
+
globalGraphIdCounter = 0
|
|
199
|
+
}
|
|
200
|
+
|
|
190
201
|
export class ReactiveGraph<
|
|
191
202
|
TDebugRefreshReason extends DebugRefreshReason,
|
|
192
203
|
TDebugThunkInfo extends DebugThunkInfo,
|
|
@@ -195,7 +206,7 @@ export class ReactiveGraph<
|
|
|
195
206
|
id = uniqueGraphId()
|
|
196
207
|
|
|
197
208
|
readonly atoms: Set<Atom<any, TContext, TDebugRefreshReason>> = new Set()
|
|
198
|
-
readonly effects: Set<Effect
|
|
209
|
+
readonly effects: Set<Effect<TDebugRefreshReason>> = new Set()
|
|
199
210
|
|
|
200
211
|
context: TContext | undefined
|
|
201
212
|
|
|
@@ -205,7 +216,7 @@ export class ReactiveGraph<
|
|
|
205
216
|
| { refreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[]; startMs: DOMHighResTimeStamp }
|
|
206
217
|
| undefined
|
|
207
218
|
|
|
208
|
-
private deferredEffects: Map<Effect
|
|
219
|
+
private deferredEffects: Map<Effect<TDebugRefreshReason>, Set<TDebugRefreshReason>> = new Map()
|
|
209
220
|
|
|
210
221
|
private refreshCallbacks: Set<() => void> = new Set()
|
|
211
222
|
|
|
@@ -239,6 +250,7 @@ export class ReactiveGraph<
|
|
|
239
250
|
setDebugInfo: (debugInfo: TDebugThunkInfo) => void,
|
|
240
251
|
ctx: TContext,
|
|
241
252
|
otelContext: otel.Context | undefined,
|
|
253
|
+
debugRefreshReason: TDebugRefreshReason | undefined,
|
|
242
254
|
) => T,
|
|
243
255
|
options?:
|
|
244
256
|
| {
|
|
@@ -266,7 +278,7 @@ export class ReactiveGraph<
|
|
|
266
278
|
|
|
267
279
|
const getAtom = (atom: Atom<T, TContext, TDebugRefreshReason>, otelContext: otel.Context) => {
|
|
268
280
|
this.addEdge(thunk, atom)
|
|
269
|
-
return compute(atom, otelContext)
|
|
281
|
+
return compute(atom, otelContext, debugRefreshReason)
|
|
270
282
|
}
|
|
271
283
|
|
|
272
284
|
let debugInfo: TDebugThunkInfo | undefined = undefined
|
|
@@ -279,6 +291,7 @@ export class ReactiveGraph<
|
|
|
279
291
|
setDebugInfo,
|
|
280
292
|
this.context ?? throwContextNotSetError(this),
|
|
281
293
|
otelContext,
|
|
294
|
+
debugRefreshReason,
|
|
282
295
|
)
|
|
283
296
|
|
|
284
297
|
const resultChanged = thunk.equal(thunk.previousResult as T, result) === false
|
|
@@ -365,14 +378,18 @@ export class ReactiveGraph<
|
|
|
365
378
|
}
|
|
366
379
|
|
|
367
380
|
makeEffect(
|
|
368
|
-
doEffect: (
|
|
381
|
+
doEffect: (
|
|
382
|
+
get: GetAtom,
|
|
383
|
+
otelContext: otel.Context | undefined,
|
|
384
|
+
debugRefreshReason: DebugRefreshReason | undefined,
|
|
385
|
+
) => void,
|
|
369
386
|
options?: { label?: string } | undefined,
|
|
370
|
-
): Effect {
|
|
371
|
-
const effect: Effect = {
|
|
387
|
+
): Effect<TDebugRefreshReason> {
|
|
388
|
+
const effect: Effect<TDebugRefreshReason> = {
|
|
372
389
|
_tag: 'effect',
|
|
373
390
|
id: uniqueNodeId(),
|
|
374
391
|
isDestroyed: false,
|
|
375
|
-
doEffect: (otelContext) => {
|
|
392
|
+
doEffect: (otelContext, debugRefreshReason) => {
|
|
376
393
|
effect.invocations++
|
|
377
394
|
|
|
378
395
|
// NOTE we're not tracking any debug refresh info for effects as they're tracked by the thunks they depend on
|
|
@@ -380,12 +397,16 @@ export class ReactiveGraph<
|
|
|
380
397
|
// Reset previous subcomputations as we're about to re-add them as part of the `doEffect` call below
|
|
381
398
|
effect.sub = new Set()
|
|
382
399
|
|
|
383
|
-
const getAtom = (
|
|
400
|
+
const getAtom = (
|
|
401
|
+
atom: Atom<any, TContext, TDebugRefreshReason>,
|
|
402
|
+
otelContext: otel.Context,
|
|
403
|
+
debugRefreshReason: DebugRefreshReason | undefined,
|
|
404
|
+
) => {
|
|
384
405
|
this.addEdge(effect, atom)
|
|
385
|
-
return compute(atom, otelContext)
|
|
406
|
+
return compute(atom, otelContext, debugRefreshReason)
|
|
386
407
|
}
|
|
387
408
|
|
|
388
|
-
doEffect(getAtom as GetAtom, otelContext)
|
|
409
|
+
doEffect(getAtom as GetAtom, otelContext, debugRefreshReason)
|
|
389
410
|
},
|
|
390
411
|
sub: new Set(),
|
|
391
412
|
label: options?.label,
|
|
@@ -421,7 +442,7 @@ export class ReactiveGraph<
|
|
|
421
442
|
}
|
|
422
443
|
| undefined,
|
|
423
444
|
) {
|
|
424
|
-
const effectsToRefresh = new Set<Effect
|
|
445
|
+
const effectsToRefresh = new Set<Effect<TDebugRefreshReason>>()
|
|
425
446
|
for (const [ref, val] of refs) {
|
|
426
447
|
ref.previousResult = val
|
|
427
448
|
ref.refreshes++
|
|
@@ -448,7 +469,7 @@ export class ReactiveGraph<
|
|
|
448
469
|
}
|
|
449
470
|
|
|
450
471
|
private runEffects = (
|
|
451
|
-
effectsToRefresh: Set<Effect
|
|
472
|
+
effectsToRefresh: Set<Effect<TDebugRefreshReason>>,
|
|
452
473
|
options: {
|
|
453
474
|
debugRefreshReason: TDebugRefreshReason
|
|
454
475
|
otelContext?: otel.Context
|
|
@@ -459,7 +480,7 @@ export class ReactiveGraph<
|
|
|
459
480
|
this.currentDebugRefresh = { refreshedAtoms: [], startMs: performance.now() }
|
|
460
481
|
|
|
461
482
|
for (const effect of effectsToRefresh) {
|
|
462
|
-
effect.doEffect(options?.otelContext)
|
|
483
|
+
effect.doEffect(options?.otelContext, options.debugRefreshReason)
|
|
463
484
|
}
|
|
464
485
|
|
|
465
486
|
const refreshedAtoms = this.currentDebugRefresh.refreshedAtoms
|
|
@@ -504,7 +525,7 @@ export class ReactiveGraph<
|
|
|
504
525
|
}
|
|
505
526
|
|
|
506
527
|
addEdge(
|
|
507
|
-
superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect
|
|
528
|
+
superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>,
|
|
508
529
|
subComp: Atom<any, TContext, TDebugRefreshReason>,
|
|
509
530
|
) {
|
|
510
531
|
superComp.sub.add(subComp)
|
|
@@ -516,11 +537,11 @@ export class ReactiveGraph<
|
|
|
516
537
|
}
|
|
517
538
|
|
|
518
539
|
removeEdge(
|
|
519
|
-
superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect
|
|
540
|
+
superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>,
|
|
520
541
|
subComp: Atom<any, TContext, TDebugRefreshReason>,
|
|
521
542
|
) {
|
|
522
543
|
superComp.sub.delete(subComp)
|
|
523
|
-
const effectsToRefresh = new Set<Effect
|
|
544
|
+
const effectsToRefresh = new Set<Effect<TDebugRefreshReason>>()
|
|
524
545
|
markSuperCompDirtyRec(subComp, effectsToRefresh)
|
|
525
546
|
|
|
526
547
|
for (const effect of effectsToRefresh) {
|
|
@@ -563,7 +584,11 @@ export class ReactiveGraph<
|
|
|
563
584
|
}
|
|
564
585
|
}
|
|
565
586
|
|
|
566
|
-
const compute = <T>(
|
|
587
|
+
const compute = <T>(
|
|
588
|
+
atom: Atom<T, unknown, any>,
|
|
589
|
+
otelContext: otel.Context,
|
|
590
|
+
debugRefreshReason: DebugRefreshReason | undefined,
|
|
591
|
+
): T => {
|
|
567
592
|
// const __getResult = atom._tag === 'thunk' ? atom.__getResult.toString() : ''
|
|
568
593
|
if (atom.isDestroyed) {
|
|
569
594
|
shouldNeverHappen(`LiveStore Error: Attempted to compute destroyed ${atom._tag} (${atom.id}): ${atom.label ?? ''}`)
|
|
@@ -571,7 +596,7 @@ const compute = <T>(atom: Atom<T, unknown, any>, otelContext: otel.Context): T =
|
|
|
571
596
|
|
|
572
597
|
if (atom.isDirty) {
|
|
573
598
|
// console.log('atom is dirty', atom.id, atom.label ?? '', atom._tag, __getResult)
|
|
574
|
-
const result = atom.computeResult(otelContext)
|
|
599
|
+
const result = atom.computeResult(otelContext, debugRefreshReason)
|
|
575
600
|
atom.isDirty = false
|
|
576
601
|
atom.previousResult = result
|
|
577
602
|
return result
|
|
@@ -581,7 +606,7 @@ const compute = <T>(atom: Atom<T, unknown, any>, otelContext: otel.Context): T =
|
|
|
581
606
|
}
|
|
582
607
|
}
|
|
583
608
|
|
|
584
|
-
const markSuperCompDirtyRec = <T>(atom: Atom<T, unknown, any>, effectsToRefresh: Set<Effect
|
|
609
|
+
const markSuperCompDirtyRec = <T>(atom: Atom<T, unknown, any>, effectsToRefresh: Set<Effect<any>>) => {
|
|
585
610
|
for (const superComp of atom.super) {
|
|
586
611
|
if (superComp._tag === 'thunk') {
|
|
587
612
|
superComp.isDirty = true
|
|
@@ -644,7 +669,7 @@ const serializeAtom = (atom: Atom<any, unknown, any>, includeResult: boolean): S
|
|
|
644
669
|
}
|
|
645
670
|
|
|
646
671
|
// NOTE This function is performance-optimized (i.e. not using `pick` and `Array.from`)
|
|
647
|
-
const serializeEffect = (effect: Effect): SerializedEffect => {
|
|
672
|
+
const serializeEffect = (effect: Effect<any>): SerializedEffect => {
|
|
648
673
|
const sub: string[] = []
|
|
649
674
|
for (const a of effect.sub) {
|
|
650
675
|
sub.push(a.id)
|
package/src/row-query-utils.ts
CHANGED
|
@@ -4,28 +4,33 @@ import { DbSchema } from '@livestore/common/schema'
|
|
|
4
4
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
5
5
|
import type * as otel from '@opentelemetry/api'
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type { GetResult, LiveQueryDef, ReactivityGraphContext } from './live-queries/base-class.js'
|
|
8
8
|
import { computed } from './live-queries/computed.js'
|
|
9
9
|
|
|
10
10
|
export const rowQueryLabel = (table: DbSchema.TableDefBase, id: string | SessionIdSymbol | undefined) =>
|
|
11
11
|
`row:${table.sqliteDef.name}${id === undefined ? '' : id === SessionIdSymbol ? `:sessionId` : `:${id}`}`
|
|
12
12
|
|
|
13
13
|
export const deriveColQuery: {
|
|
14
|
-
<
|
|
15
|
-
|
|
14
|
+
<TQueryDef extends LiveQueryDef<any, QueryInfo.None>, TCol extends keyof GetResult<TQueryDef> & string>(
|
|
15
|
+
queryDef: TQueryDef,
|
|
16
16
|
colName: TCol,
|
|
17
|
-
):
|
|
18
|
-
<
|
|
19
|
-
|
|
17
|
+
): LiveQueryDef<GetResult<TQueryDef>[TCol], QueryInfo.None>
|
|
18
|
+
<TQueryDef extends LiveQueryDef<any, QueryInfo.Row>, TCol extends keyof GetResult<TQueryDef> & string>(
|
|
19
|
+
queryDef: TQueryDef,
|
|
20
20
|
colName: TCol,
|
|
21
|
-
):
|
|
22
|
-
} = (
|
|
23
|
-
return computed((get) => get(
|
|
24
|
-
label: `deriveColQuery:${
|
|
21
|
+
): LiveQueryDef<GetResult<TQueryDef>[TCol], QueryInfo.Col>
|
|
22
|
+
} = (queryDef: LiveQueryDef<any, QueryInfo.Row | QueryInfo.Col>, colName: string) => {
|
|
23
|
+
return computed((get) => get(queryDef)[colName], {
|
|
24
|
+
label: `deriveColQuery:${queryDef.label}:${colName}`,
|
|
25
25
|
queryInfo:
|
|
26
|
-
|
|
27
|
-
? { _tag: 'Col', table:
|
|
26
|
+
queryDef.queryInfo._tag === 'Row'
|
|
27
|
+
? { _tag: 'Col', table: queryDef.queryInfo.table, column: colName, id: queryDef.queryInfo.id }
|
|
28
28
|
: undefined,
|
|
29
|
+
deps: [
|
|
30
|
+
queryDef.queryInfo.table.sqliteDef.name,
|
|
31
|
+
queryDef.queryInfo.id === SessionIdSymbol ? 'sessionId' : queryDef.queryInfo.id,
|
|
32
|
+
queryDef.queryInfo._tag === 'Col' ? queryDef.queryInfo.column : undefined,
|
|
33
|
+
],
|
|
29
34
|
}) as any
|
|
30
35
|
}
|
|
31
36
|
|
|
@@ -41,15 +46,17 @@ export const makeExecBeforeFirstRun =
|
|
|
41
46
|
table: DbSchema.TableDefBase
|
|
42
47
|
otelContext: otel.Context | undefined
|
|
43
48
|
}) =>
|
|
44
|
-
({ store }:
|
|
49
|
+
({ store }: ReactivityGraphContext) => {
|
|
45
50
|
const otelContext = otelContext_ ?? store.otel.queriesSpanContext
|
|
46
51
|
|
|
47
52
|
if (table.options.isSingleton === false) {
|
|
48
53
|
const idStr = id === SessionIdSymbol ? store.sessionId : id!
|
|
49
54
|
const rowExists =
|
|
50
|
-
store.
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
store.sqliteDbWrapper.select(
|
|
56
|
+
`SELECT 1 FROM '${table.sqliteDef.name}' WHERE id = ?`,
|
|
57
|
+
[idStr] as any as PreparedBindValues,
|
|
58
|
+
{ otelContext },
|
|
59
|
+
).length === 1
|
|
53
60
|
|
|
54
61
|
if (rowExists) return
|
|
55
62
|
|
|
@@ -59,7 +66,11 @@ export const makeExecBeforeFirstRun =
|
|
|
59
66
|
)
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
//
|
|
63
|
-
|
|
69
|
+
// It's important that we only mutate and don't refresh here, as this function might be called during a render
|
|
70
|
+
// and otherwise we might end up in a "reactive loop"
|
|
71
|
+
store.mutate(
|
|
72
|
+
{ otelContext, skipRefresh: true, label: `rowQuery:${table.sqliteDef.name}:${idStr}` },
|
|
73
|
+
table.insert({ id, ...insertValues }),
|
|
74
|
+
)
|
|
64
75
|
}
|
|
65
76
|
}
|