@livestore/livestore 0.3.0-dev.4 → 0.3.0-dev.40
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/QueryCache.d.ts.map +1 -1
- package/dist/SqliteDbWrapper.d.ts +60 -0
- package/dist/SqliteDbWrapper.d.ts.map +1 -0
- package/dist/{SynchronousDatabaseWrapper.js → SqliteDbWrapper.js} +69 -34
- package/dist/SqliteDbWrapper.js.map +1 -0
- package/dist/effect/LiveStore.d.ts +6 -34
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +10 -12
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/effect/mod.d.ts +3 -0
- package/dist/effect/mod.d.ts.map +1 -0
- package/dist/effect/mod.js +3 -0
- package/dist/effect/mod.js.map +1 -0
- package/dist/internal/mod.d.ts +3 -0
- package/dist/internal/mod.d.ts.map +1 -0
- package/dist/internal/mod.js +3 -0
- package/dist/internal/mod.js.map +1 -0
- package/dist/live-queries/base-class.d.ts +65 -27
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +54 -13
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/client-document-get-query.d.ts +12 -0
- package/dist/live-queries/client-document-get-query.d.ts.map +1 -0
- package/dist/live-queries/client-document-get-query.js +18 -0
- package/dist/live-queries/client-document-get-query.js.map +1 -0
- package/dist/live-queries/computed.d.ts +12 -14
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +37 -15
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/db-query.d.ts +64 -0
- package/dist/live-queries/db-query.d.ts.map +1 -0
- package/dist/live-queries/{db.js → db-query.js} +83 -41
- 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 +133 -0
- package/dist/live-queries/db-query.test.js.map +1 -0
- package/dist/live-queries/mod.d.ts +5 -0
- package/dist/live-queries/mod.d.ts.map +1 -0
- package/dist/live-queries/mod.js +5 -0
- package/dist/live-queries/mod.js.map +1 -0
- package/dist/live-queries/signal.d.ts +20 -0
- package/dist/live-queries/signal.d.ts.map +1 -0
- package/dist/live-queries/signal.js +33 -0
- package/dist/live-queries/signal.js.map +1 -0
- package/dist/live-queries/signal.test.d.ts +2 -0
- package/dist/live-queries/signal.test.d.ts.map +1 -0
- package/dist/live-queries/signal.test.js +17 -0
- package/dist/live-queries/signal.test.js.map +1 -0
- package/dist/mod.d.ts +14 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +13 -0
- package/dist/mod.js.map +1 -0
- package/dist/reactive.d.ts +23 -17
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +23 -19
- package/dist/reactive.js.map +1 -1
- package/dist/reactive.test.js +1 -1
- package/dist/reactive.test.js.map +1 -1
- package/dist/store/create-store.d.ts +70 -12
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +69 -19
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts +5 -4
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +103 -47
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-types.d.ts +32 -42
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js +2 -5
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store.d.ts +104 -39
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +261 -214
- package/dist/store/store.js.map +1 -1
- package/dist/utils/data-structures.d.ts.map +1 -1
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +6 -1
- package/dist/utils/dev.js.map +1 -1
- 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 +59 -216
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/fixture.js +23 -18
- 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.map +1 -1
- package/dist/utils/tests/otel.js +8 -3
- package/dist/utils/tests/otel.js.map +1 -1
- package/package.json +29 -26
- package/src/{SynchronousDatabaseWrapper.ts → SqliteDbWrapper.ts} +92 -42
- package/src/effect/LiveStore.ts +27 -64
- package/src/effect/{index.ts → mod.ts} +2 -3
- package/src/internal/mod.ts +2 -0
- package/src/live-queries/__snapshots__/{db.test.ts.snap → db-query.test.ts.snap} +220 -45
- package/src/live-queries/base-class.ts +152 -50
- package/src/live-queries/client-document-get-query.ts +52 -0
- package/src/live-queries/computed.ts +51 -33
- package/src/live-queries/db-query.test.ts +192 -0
- package/src/live-queries/{db.ts → db-query.ts} +140 -82
- package/src/live-queries/mod.ts +4 -0
- package/src/live-queries/signal.test.ts +25 -0
- package/src/live-queries/signal.ts +47 -0
- package/src/mod.ts +42 -0
- package/src/reactive.test.ts +1 -1
- package/src/reactive.ts +66 -43
- package/src/store/create-store.ts +187 -59
- package/src/store/devtools.ts +136 -54
- package/src/store/store-types.ts +31 -43
- package/src/store/store.ts +385 -309
- package/src/utils/dev.ts +6 -1
- 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 +22 -31
- package/src/utils/tests/mod.ts +1 -0
- package/src/utils/tests/otel.ts +10 -3
- package/dist/SynchronousDatabaseWrapper.d.ts +0 -41
- package/dist/SynchronousDatabaseWrapper.d.ts.map +0 -1
- package/dist/SynchronousDatabaseWrapper.js.map +0 -1
- package/dist/effect/index.d.ts +0 -2
- package/dist/effect/index.d.ts.map +0 -1
- package/dist/effect/index.js +0 -2
- package/dist/effect/index.js.map +0 -1
- package/dist/global-state.d.ts +0 -14
- package/dist/global-state.d.ts.map +0 -1
- package/dist/global-state.js +0 -16
- package/dist/global-state.js.map +0 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -16
- package/dist/index.js.map +0 -1
- package/dist/live-queries/db.d.ts +0 -66
- package/dist/live-queries/db.d.ts.map +0 -1
- package/dist/live-queries/db.js.map +0 -1
- package/dist/live-queries/db.test.d.ts +0 -2
- package/dist/live-queries/db.test.d.ts.map +0 -1
- package/dist/live-queries/db.test.js +0 -118
- package/dist/live-queries/db.test.js.map +0 -1
- package/dist/live-queries/graphql.d.ts +0 -49
- package/dist/live-queries/graphql.d.ts.map +0 -1
- package/dist/live-queries/graphql.js +0 -122
- package/dist/live-queries/graphql.js.map +0 -1
- package/dist/row-query-utils.d.ts +0 -17
- package/dist/row-query-utils.d.ts.map +0 -1
- package/dist/row-query-utils.js +0 -31
- package/dist/row-query-utils.js.map +0 -1
- package/dist/utils/otel.d.ts +0 -4
- package/dist/utils/otel.d.ts.map +0 -1
- package/dist/utils/otel.js +0 -6
- package/dist/utils/otel.js.map +0 -1
- package/src/global-state.ts +0 -20
- package/src/index.ts +0 -66
- package/src/live-queries/db.test.ts +0 -154
- package/src/live-queries/graphql.ts +0 -219
- package/src/row-query-utils.ts +0 -66
- package/src/utils/otel.ts +0 -9
- package/tsconfig.json +0 -18
- package/vitest.config.js +0 -9
@@ -1,26 +1,27 @@
|
|
1
|
-
import type { Bindable, QueryBuilder
|
1
|
+
import type { Bindable, QueryBuilder } from '@livestore/common'
|
2
2
|
import {
|
3
|
+
getDurationMsFromSpan,
|
3
4
|
getResultSchema,
|
4
5
|
isQueryBuilder,
|
5
6
|
prepareBindValues,
|
6
7
|
QueryBuilderAstSymbol,
|
7
8
|
replaceSessionIdSymbol,
|
9
|
+
SessionIdSymbol,
|
8
10
|
UnexpectedError,
|
9
11
|
} from '@livestore/common'
|
10
12
|
import { deepEqual, shouldNeverHappen } from '@livestore/utils'
|
11
13
|
import { Predicate, Schema, TreeFormatter } from '@livestore/utils/effect'
|
12
14
|
import * as otel from '@opentelemetry/api'
|
13
15
|
|
14
|
-
import { globalReactivityGraph } from '../global-state.js'
|
15
16
|
import type { Thunk } from '../reactive.js'
|
16
17
|
import { isThunk, NOT_REFRESHED_YET } from '../reactive.js'
|
17
|
-
import { makeExecBeforeFirstRun, rowQueryLabel } from '../row-query-utils.js'
|
18
18
|
import type { RefreshReason } from '../store/store-types.js'
|
19
|
-
import {
|
20
|
-
import type {
|
21
|
-
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
19
|
+
import { isValidFunctionString } from '../utils/function-string.js'
|
20
|
+
import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
|
21
|
+
import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
|
22
|
+
import { makeExecBeforeFirstRun, rowQueryLabel } from './client-document-get-query.js'
|
22
23
|
|
23
|
-
export type QueryInputRaw<TDecoded, TEncoded
|
24
|
+
export type QueryInputRaw<TDecoded, TEncoded> = {
|
24
25
|
query: string
|
25
26
|
schema: Schema.Schema<TDecoded, TEncoded>
|
26
27
|
bindValues?: Bindable
|
@@ -30,104 +31,149 @@ export type QueryInputRaw<TDecoded, TEncoded, TQueryInfo extends QueryInfo> = {
|
|
30
31
|
* NOTE In the future we want to do this automatically at build time
|
31
32
|
*/
|
32
33
|
queriedTables?: Set<string>
|
33
|
-
|
34
|
-
execBeforeFirstRun?: (ctx: QueryContext) => void
|
34
|
+
execBeforeFirstRun?: (ctx: ReactivityGraphContext) => void
|
35
35
|
}
|
36
36
|
|
37
|
-
export
|
38
|
-
|
39
|
-
|
37
|
+
export const isQueryInputRaw = (value: unknown): value is QueryInputRaw<any, any> =>
|
38
|
+
Predicate.hasProperty(value, 'query') && Predicate.hasProperty(value, 'schema')
|
39
|
+
|
40
|
+
export type QueryInput<TDecoded, TEncoded> = QueryInputRaw<TDecoded, TEncoded> | QueryBuilder<TDecoded, any, any>
|
40
41
|
|
41
42
|
/**
|
42
|
-
* NOTE `
|
43
|
+
* NOTE `queryDb` is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
|
43
44
|
*/
|
44
45
|
export const queryDb: {
|
45
|
-
<TResultSchema, TResult = TResultSchema
|
46
|
-
queryInput:
|
47
|
-
| QueryInputRaw<TResultSchema, ReadonlyArray<any>, TQueryInfo>
|
48
|
-
| QueryBuilder<TResultSchema, any, any, TQueryInfo>,
|
46
|
+
<TResultSchema, TResult = TResultSchema>(
|
47
|
+
queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>,
|
49
48
|
options?: {
|
50
49
|
map?: (rows: TResultSchema) => TResult
|
51
50
|
/**
|
52
51
|
* Used for debugging / devtools
|
53
52
|
*/
|
54
53
|
label?: string
|
55
|
-
|
56
|
-
otelContext?: otel.Context
|
54
|
+
deps?: DepKey
|
57
55
|
},
|
58
|
-
):
|
56
|
+
): LiveQueryDef<TResult>
|
59
57
|
// NOTE in this "thunk case", we can't directly derive label/queryInfo from the queryInput,
|
60
58
|
// so the caller needs to provide them explicitly otherwise queryInfo will be set to `None`,
|
61
59
|
// and label will be set during the query execution
|
62
|
-
<TResultSchema, TResult = TResultSchema
|
60
|
+
<TResultSchema, TResult = TResultSchema>(
|
63
61
|
queryInput:
|
64
|
-
| ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any
|
65
|
-
| ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any
|
62
|
+
| ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>)
|
63
|
+
| ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>),
|
66
64
|
options?: {
|
67
65
|
map?: (rows: TResultSchema) => TResult
|
68
66
|
/**
|
69
67
|
* Used for debugging / devtools
|
70
68
|
*/
|
71
69
|
label?: string
|
72
|
-
|
73
|
-
queryInfo?: TQueryInfo
|
74
|
-
otelContext?: otel.Context
|
70
|
+
deps?: DepKey
|
75
71
|
},
|
76
|
-
):
|
77
|
-
} = (queryInput, options) =>
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
72
|
+
): LiveQueryDef<TResult>
|
73
|
+
} = (queryInput, options) => {
|
74
|
+
const { queryString, extraDeps } = getQueryStringAndExtraDeps(queryInput)
|
75
|
+
|
76
|
+
const hash =
|
77
|
+
(options?.deps ? queryString + '-' + depsToString(options.deps) : queryString) + '-' + depsToString(extraDeps)
|
78
|
+
if (isValidFunctionString(hash)._tag === 'invalid') {
|
79
|
+
throw new Error(`On Expo/React Native, db queries must provide a \`deps\` option`)
|
80
|
+
}
|
81
|
+
|
82
|
+
if (hash.trim() === '') {
|
83
|
+
return shouldNeverHappen(`Invalid query hash for query: ${queryInput}`)
|
84
|
+
}
|
85
|
+
|
86
|
+
const label = options?.label ?? queryString
|
87
|
+
|
88
|
+
const def: LiveQueryDef.Any = {
|
89
|
+
_tag: 'def',
|
90
|
+
make: withRCMap(hash, (ctx, otelContext) => {
|
91
|
+
// TODO onDestroy
|
92
|
+
return new LiveStoreDbQuery({
|
93
|
+
reactivityGraph: ctx.reactivityGraph.deref()!,
|
94
|
+
queryInput,
|
95
|
+
label,
|
96
|
+
map: options?.map,
|
97
|
+
otelContext,
|
98
|
+
def,
|
99
|
+
})
|
100
|
+
}),
|
101
|
+
label,
|
102
|
+
hash,
|
103
|
+
}
|
104
|
+
|
105
|
+
return def
|
106
|
+
}
|
107
|
+
|
108
|
+
const bindValuesToDepKey = (bindValues: Bindable | undefined): DepKey => {
|
109
|
+
if (bindValues === undefined) {
|
110
|
+
return []
|
111
|
+
}
|
112
|
+
|
113
|
+
return Object.entries(bindValues)
|
114
|
+
.map(([key, value]: [string, any]) => `${key}:${value === SessionIdSymbol ? 'SessionIdSymbol' : value}`)
|
115
|
+
.join(',')
|
116
|
+
}
|
117
|
+
|
118
|
+
const getQueryStringAndExtraDeps = (
|
119
|
+
queryInput: QueryInput<any, any> | ((get: GetAtomResult) => QueryInput<any, any>),
|
120
|
+
): { queryString: string; extraDeps: DepKey } => {
|
121
|
+
if (isQueryBuilder(queryInput)) {
|
122
|
+
const { query, bindValues } = queryInput.asSql()
|
123
|
+
return { queryString: query, extraDeps: bindValuesToDepKey(bindValues) }
|
124
|
+
}
|
125
|
+
|
126
|
+
if (isQueryInputRaw(queryInput)) {
|
127
|
+
return { queryString: queryInput.query, extraDeps: bindValuesToDepKey(queryInput.bindValues) }
|
128
|
+
}
|
129
|
+
|
130
|
+
if (typeof queryInput === 'function') {
|
131
|
+
return { queryString: queryInput.toString(), extraDeps: [] }
|
132
|
+
}
|
133
|
+
|
134
|
+
return shouldNeverHappen(`Invalid query input: ${queryInput}`)
|
135
|
+
}
|
86
136
|
|
87
137
|
/* An object encapsulating a reactive SQL query */
|
88
|
-
export class LiveStoreDbQuery<
|
89
|
-
TResultSchema,
|
90
|
-
TResult = TResultSchema,
|
91
|
-
TQueryInfo extends QueryInfo = QueryInfo.None,
|
92
|
-
> extends LiveStoreQueryBase<TResult, TQueryInfo> {
|
138
|
+
export class LiveStoreDbQuery<TResultSchema, TResult = TResultSchema> extends LiveStoreQueryBase<TResult> {
|
93
139
|
_tag: 'db' = 'db'
|
94
140
|
|
95
141
|
/** A reactive thunk representing the query text */
|
96
|
-
queryInput$: Thunk<
|
142
|
+
queryInput$: Thunk<QueryInputRaw<any, any>, ReactivityGraphContext, RefreshReason> | undefined
|
97
143
|
|
98
144
|
/** A reactive thunk representing the query results */
|
99
|
-
results$: Thunk<TResult,
|
145
|
+
results$: Thunk<TResult, ReactivityGraphContext, RefreshReason>
|
100
146
|
|
101
147
|
label: string
|
102
148
|
|
103
|
-
|
104
|
-
|
105
|
-
protected reactivityGraph
|
149
|
+
readonly reactivityGraph
|
106
150
|
|
107
151
|
private mapResult: (rows: TResultSchema) => TResult
|
152
|
+
def: LiveQueryDef<TResult>
|
108
153
|
|
109
154
|
constructor({
|
110
155
|
queryInput,
|
111
156
|
label: inputLabel,
|
112
157
|
reactivityGraph,
|
113
158
|
map,
|
114
|
-
queryInfo: inputQueryInfo,
|
115
159
|
otelContext,
|
160
|
+
def,
|
116
161
|
}: {
|
117
162
|
label?: string
|
118
163
|
queryInput:
|
119
|
-
| QueryInput<TResultSchema, ReadonlyArray<any
|
120
|
-
| ((get: GetAtomResult, ctx:
|
121
|
-
reactivityGraph
|
164
|
+
| QueryInput<TResultSchema, ReadonlyArray<any>>
|
165
|
+
| ((get: GetAtomResult, ctx: ReactivityGraphContext) => QueryInput<TResultSchema, ReadonlyArray<any>>)
|
166
|
+
reactivityGraph: ReactivityGraph
|
122
167
|
map?: (rows: TResultSchema) => TResult
|
123
|
-
|
168
|
+
/** Only used for the initial query execution */
|
124
169
|
otelContext?: otel.Context
|
170
|
+
def: LiveQueryDef<TResult>
|
125
171
|
}) {
|
126
172
|
super()
|
127
173
|
|
128
174
|
let label = inputLabel ?? 'db(unknown)'
|
129
|
-
|
130
|
-
this.
|
175
|
+
this.reactivityGraph = reactivityGraph
|
176
|
+
this.def = def
|
131
177
|
|
132
178
|
this.mapResult = map === undefined ? (rows: any) => rows as TResult : map
|
133
179
|
|
@@ -136,13 +182,15 @@ export class LiveStoreDbQuery<
|
|
136
182
|
typeof queryInput === 'function' ? undefined : isQueryBuilder(queryInput) ? undefined : queryInput.schema,
|
137
183
|
}
|
138
184
|
|
139
|
-
const execBeforeFirstRunRef: {
|
185
|
+
const execBeforeFirstRunRef: {
|
186
|
+
current: ((ctx: ReactivityGraphContext, otelContext: otel.Context) => void) | undefined
|
187
|
+
} = {
|
140
188
|
current: undefined,
|
141
189
|
}
|
142
190
|
|
143
|
-
type TQueryInputRaw = QueryInputRaw<any, any
|
191
|
+
type TQueryInputRaw = QueryInputRaw<any, any>
|
144
192
|
|
145
|
-
let queryInputRaw$OrQueryInputRaw: TQueryInputRaw | Thunk<TQueryInputRaw,
|
193
|
+
let queryInputRaw$OrQueryInputRaw: TQueryInputRaw | Thunk<TQueryInputRaw, ReactivityGraphContext, RefreshReason>
|
146
194
|
|
147
195
|
const fromQueryBuilder = (qb: QueryBuilder.Any, otelContext: otel.Context | undefined) => {
|
148
196
|
try {
|
@@ -156,14 +204,13 @@ export class LiveStoreDbQuery<
|
|
156
204
|
schema,
|
157
205
|
bindValues: qbRes.bindValues,
|
158
206
|
queriedTables: new Set([ast.tableDef.sqliteDef.name]),
|
159
|
-
queryInfo: ast._tag === 'RowQuery' ? { _tag: 'Row', table: ast.tableDef, id: ast.id } : { _tag: 'None' },
|
160
207
|
} satisfies TQueryInputRaw,
|
161
208
|
label: ast._tag === 'RowQuery' ? rowQueryLabel(ast.tableDef, ast.id) : qb.toString(),
|
162
209
|
execBeforeFirstRun:
|
163
210
|
ast._tag === 'RowQuery'
|
164
211
|
? makeExecBeforeFirstRun({
|
165
212
|
table: ast.tableDef,
|
166
|
-
|
213
|
+
explicitDefaultValues: ast.explicitDefaultValues,
|
167
214
|
id: ast.id,
|
168
215
|
otelContext,
|
169
216
|
})
|
@@ -178,7 +225,10 @@ export class LiveStoreDbQuery<
|
|
178
225
|
queryInputRaw$OrQueryInputRaw = this.reactivityGraph.makeThunk(
|
179
226
|
(get, setDebugInfo, ctx, otelContext) => {
|
180
227
|
const startMs = performance.now()
|
181
|
-
const queryInputResult = queryInput(
|
228
|
+
const queryInputResult = queryInput(
|
229
|
+
makeGetAtomResult(get, ctx, otelContext ?? ctx.rootOtelContext, this.dependencyQueriesRef),
|
230
|
+
ctx,
|
231
|
+
)
|
182
232
|
const durationMs = performance.now() - startMs
|
183
233
|
|
184
234
|
let queryInputRaw: TQueryInputRaw
|
@@ -197,10 +247,6 @@ export class LiveStoreDbQuery<
|
|
197
247
|
|
198
248
|
schemaRef.current = queryInputRaw.schema
|
199
249
|
|
200
|
-
if (inputQueryInfo === undefined && queryInputRaw.queryInfo !== undefined) {
|
201
|
-
queryInfo = queryInputRaw.queryInfo as TQueryInfo
|
202
|
-
}
|
203
|
-
|
204
250
|
return queryInputRaw
|
205
251
|
},
|
206
252
|
{
|
@@ -210,6 +256,8 @@ export class LiveStoreDbQuery<
|
|
210
256
|
equal: (a, b) => a.query === b.query && deepEqual(a.bindValues, b.bindValues),
|
211
257
|
},
|
212
258
|
)
|
259
|
+
|
260
|
+
this.queryInput$ = queryInputRaw$OrQueryInputRaw
|
213
261
|
} else {
|
214
262
|
let queryInputRaw: TQueryInputRaw
|
215
263
|
if (isQueryBuilder(queryInput)) {
|
@@ -231,10 +279,6 @@ export class LiveStoreDbQuery<
|
|
231
279
|
label = `db(${rowQueryLabel(ast.tableDef, ast.id)})`
|
232
280
|
}
|
233
281
|
}
|
234
|
-
|
235
|
-
if (inputQueryInfo === undefined && queryInputRaw.queryInfo !== undefined) {
|
236
|
-
queryInfo = queryInputRaw.queryInfo as TQueryInfo
|
237
|
-
}
|
238
282
|
}
|
239
283
|
|
240
284
|
const queriedTablesRef: { current: Set<string> | undefined } = { current: undefined }
|
@@ -253,10 +297,16 @@ export class LiveStoreDbQuery<
|
|
253
297
|
: undefined
|
254
298
|
|
255
299
|
const results$ = this.reactivityGraph.makeThunk<TResult>(
|
256
|
-
(get, setDebugInfo, queryContext, otelContext) =>
|
300
|
+
(get, setDebugInfo, queryContext, otelContext, debugRefreshReason) =>
|
257
301
|
queryContext.otelTracer.startActiveSpan(
|
258
302
|
'db:...', // NOTE span name will be overridden further down
|
259
|
-
{
|
303
|
+
{
|
304
|
+
attributes: {
|
305
|
+
'livestore.debugRefreshReason': Predicate.hasProperty(debugRefreshReason, 'label')
|
306
|
+
? (debugRefreshReason.label as string)
|
307
|
+
: debugRefreshReason?._tag,
|
308
|
+
},
|
309
|
+
},
|
260
310
|
otelContext ?? queryContext.rootOtelContext,
|
261
311
|
(span) => {
|
262
312
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
@@ -268,34 +318,37 @@ export class LiveStoreDbQuery<
|
|
268
318
|
}
|
269
319
|
|
270
320
|
const queryInputResult = isThunk(queryInputRaw$OrQueryInputRaw)
|
271
|
-
? (get(queryInputRaw$OrQueryInputRaw, otelContext) as TQueryInputRaw)
|
321
|
+
? (get(queryInputRaw$OrQueryInputRaw, otelContext, debugRefreshReason) as TQueryInputRaw)
|
272
322
|
: (queryInputRaw$OrQueryInputRaw as TQueryInputRaw)
|
273
323
|
|
274
324
|
const sqlString = queryInputResult.query
|
275
325
|
const bindValues = queryInputResult.bindValues
|
276
326
|
|
277
327
|
if (queriedTablesRef.current === undefined) {
|
278
|
-
queriedTablesRef.current = store.
|
328
|
+
queriedTablesRef.current = store.sqliteDbWrapper.getTablesUsed(sqlString)
|
279
329
|
}
|
280
330
|
|
281
331
|
if (bindValues !== undefined) {
|
282
|
-
replaceSessionIdSymbol(bindValues, store.clientSession.
|
332
|
+
replaceSessionIdSymbol(bindValues, store.clientSession.sessionId)
|
283
333
|
}
|
284
334
|
|
285
335
|
// Establish a reactive dependency on the tables used in the query
|
286
336
|
for (const tableName of queriedTablesRef.current) {
|
287
337
|
const tableRef = store.tableRefs[tableName] ?? shouldNeverHappen(`No table ref found for ${tableName}`)
|
288
|
-
get(tableRef, otelContext)
|
338
|
+
get(tableRef, otelContext, debugRefreshReason)
|
289
339
|
}
|
290
340
|
|
291
341
|
span.setAttribute('sql.query', sqlString)
|
292
342
|
span.updateName(`db:${sqlString.slice(0, 50)}`)
|
293
343
|
|
294
|
-
const rawDbResults = store.
|
295
|
-
|
296
|
-
bindValues
|
297
|
-
|
298
|
-
|
344
|
+
const rawDbResults = store.sqliteDbWrapper.select<any>(
|
345
|
+
sqlString,
|
346
|
+
bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
|
347
|
+
{
|
348
|
+
queriedTables: queriedTablesRef.current,
|
349
|
+
otelContext,
|
350
|
+
},
|
351
|
+
)
|
299
352
|
|
300
353
|
span.setAttribute('sql.rowsCount', rawDbResults.length)
|
301
354
|
|
@@ -306,9 +359,9 @@ export class LiveStoreDbQuery<
|
|
306
359
|
const expectedSchemaStr = String(schemaRef.current!.ast)
|
307
360
|
const bindValuesStr = bindValues === undefined ? '' : `\nBind values: ${JSON.stringify(bindValues)}`
|
308
361
|
|
309
|
-
|
362
|
+
return shouldNeverHappen(
|
310
363
|
`\
|
311
|
-
Error parsing SQL query result.
|
364
|
+
Error parsing SQL query result (${label}).
|
312
365
|
|
313
366
|
Query: ${sqlString}\
|
314
367
|
${bindValuesStr}
|
@@ -319,8 +372,8 @@ Error: ${parseErrorStr}
|
|
319
372
|
|
320
373
|
Result:`,
|
321
374
|
rawDbResults,
|
375
|
+
'\n',
|
322
376
|
)
|
323
|
-
return shouldNeverHappen(`Error parsing SQL query result: ${parsedResult.left}`)
|
324
377
|
}
|
325
378
|
|
326
379
|
const result = this.mapResult(parsedResult.right)
|
@@ -342,14 +395,19 @@ Result:`,
|
|
342
395
|
this.results$ = results$
|
343
396
|
|
344
397
|
this.label = label
|
345
|
-
this.queryInfo = queryInfo
|
346
398
|
}
|
347
399
|
|
348
400
|
destroy = () => {
|
401
|
+
this.isDestroyed = true
|
402
|
+
|
349
403
|
if (this.queryInput$ !== undefined) {
|
350
404
|
this.reactivityGraph.destroyNode(this.queryInput$)
|
351
405
|
}
|
352
406
|
|
353
407
|
this.reactivityGraph.destroyNode(this.results$)
|
408
|
+
|
409
|
+
for (const query of this.dependencyQueriesRef) {
|
410
|
+
query.deref()
|
411
|
+
}
|
354
412
|
}
|
355
413
|
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Effect } from '@livestore/utils/effect'
|
2
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
3
|
+
import { expect } from 'vitest'
|
4
|
+
|
5
|
+
import { makeTodoMvc } from '../utils/tests/fixture.js'
|
6
|
+
import { computed } from './computed.js'
|
7
|
+
import { signal } from './signal.js'
|
8
|
+
|
9
|
+
Vitest.describe('signal', () => {
|
10
|
+
Vitest.scopedLive('should be able to create a signal', () =>
|
11
|
+
Effect.gen(function* () {
|
12
|
+
const num$ = signal(0, { label: 'num$' })
|
13
|
+
|
14
|
+
const duplicated$ = computed((get) => get(num$) * 2, { label: 'duplicated$' })
|
15
|
+
|
16
|
+
const store = yield* makeTodoMvc({})
|
17
|
+
|
18
|
+
expect(store.query(duplicated$)).toBe(0)
|
19
|
+
|
20
|
+
store.setSignal(num$, 1)
|
21
|
+
|
22
|
+
expect(store.query(duplicated$)).toBe(2)
|
23
|
+
}),
|
24
|
+
)
|
25
|
+
})
|
@@ -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 { ISignal, ReactivityGraph, ReactivityGraphContext, SignalDef } from './base-class.js'
|
6
|
+
import { withRCMap } from './base-class.js'
|
7
|
+
|
8
|
+
export const signal = <T>(
|
9
|
+
defaultValue: T,
|
10
|
+
options?: {
|
11
|
+
label?: string
|
12
|
+
},
|
13
|
+
): SignalDef<T> => {
|
14
|
+
const id = nanoid()
|
15
|
+
return {
|
16
|
+
_tag: 'signal-def',
|
17
|
+
defaultValue,
|
18
|
+
make: withRCMap(id, (ctx) => new Signal(defaultValue, ctx.reactivityGraph.deref()!, options)),
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
export class Signal<T> implements ISignal<T> {
|
23
|
+
_tag = 'signal' 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/mod.ts
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
export { Store } from './store/store.js'
|
2
|
+
export { createStore, createStorePromise, type CreateStoreOptions } from './store/create-store.js'
|
3
|
+
export type { QueryDebugInfo, RefreshReason, OtelOptions } from './store/store-types.js'
|
4
|
+
// We're re-exporting `Schema` from `effect` for convenience
|
5
|
+
export { Schema } from '@livestore/utils/effect'
|
6
|
+
|
7
|
+
export {
|
8
|
+
type LiveStoreContext,
|
9
|
+
type LiveStoreContextRunning,
|
10
|
+
type ShutdownDeferred,
|
11
|
+
makeShutdownDeferred,
|
12
|
+
} from './store/store-types.js'
|
13
|
+
|
14
|
+
export { SqliteDbWrapper, emptyDebugInfo } from './SqliteDbWrapper.js'
|
15
|
+
|
16
|
+
export { queryDb, computed, signal, type LiveQuery, type LiveQueryDef } from './live-queries/mod.js'
|
17
|
+
|
18
|
+
export * from '@livestore/common/schema'
|
19
|
+
export {
|
20
|
+
sql,
|
21
|
+
SessionIdSymbol,
|
22
|
+
type BootStatus,
|
23
|
+
type SqliteDb,
|
24
|
+
type DebugInfo,
|
25
|
+
type MutableDebugInfo,
|
26
|
+
prepareBindValues,
|
27
|
+
type Bindable,
|
28
|
+
type PreparedBindValues,
|
29
|
+
type QueryBuilderAst,
|
30
|
+
type QueryBuilder,
|
31
|
+
type RowQuery,
|
32
|
+
StoreInterrupted,
|
33
|
+
IntentionalShutdownCause,
|
34
|
+
provideOtel,
|
35
|
+
} from '@livestore/common'
|
36
|
+
|
37
|
+
export { deepEqual } from '@livestore/utils'
|
38
|
+
export { nanoid } from '@livestore/utils/nanoid'
|
39
|
+
|
40
|
+
export * from './utils/stack-info.js'
|
41
|
+
|
42
|
+
export type { ClientSession, Adapter, PreparedStatement } from '@livestore/common'
|
package/src/reactive.test.ts
CHANGED
@@ -245,7 +245,7 @@ describe('a trivial graph', () => {
|
|
245
245
|
expect(e.isDirty).toBe(true)
|
246
246
|
|
247
247
|
expect(() => c.computeResult()).toThrowErrorMatchingInlineSnapshot(
|
248
|
-
`[Error: This should never happen: LiveStore Error: Attempted to compute destroyed ref (node-
|
248
|
+
`[Error: This should never happen: LiveStore Error: Attempted to compute destroyed ref (node-2): b]`,
|
249
249
|
)
|
250
250
|
})
|
251
251
|
})
|