@livestore/livestore 0.0.37 → 0.0.39-dev.2
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/README.md +3 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/react/fixture.d.ts +97 -3
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +10 -7
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/__tests__/react/useQuery.test.js +12 -23
- package/dist/__tests__/react/useQuery.test.js.map +1 -1
- package/dist/__tests__/react/useRow.test.js +4 -4
- package/dist/__tests__/react/useRow.test.js.map +1 -1
- package/dist/__tests__/reactiveQueries/sql.test.js +32 -35
- package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +3 -2
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations.js +2 -2
- package/dist/migrations.js.map +1 -1
- package/dist/mutations.d.ts +3 -1
- package/dist/mutations.d.ts.map +1 -1
- package/dist/mutations.js +2 -2
- package/dist/mutations.js.map +1 -1
- package/dist/react/LiveStoreContext.d.ts +2 -2
- package/dist/react/LiveStoreContext.d.ts.map +1 -1
- package/dist/react/LiveStoreContext.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/useAtom.d.ts +5 -0
- package/dist/react/useAtom.d.ts.map +1 -0
- package/dist/react/useAtom.js +16 -0
- package/dist/react/useAtom.js.map +1 -0
- package/dist/react/useQuery.d.ts +3 -3
- package/dist/react/useQuery.d.ts.map +1 -1
- package/dist/react/useQuery.js.map +1 -1
- package/dist/react/useRow.d.ts +5 -5
- package/dist/react/useRow.d.ts.map +1 -1
- package/dist/react/useRow.js +16 -30
- package/dist/react/useRow.js.map +1 -1
- package/dist/react/useTemporaryQuery.d.ts +3 -3
- package/dist/react/useTemporaryQuery.d.ts.map +1 -1
- package/dist/react/useTemporaryQuery.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +12 -4
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +1 -0
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/graphql.d.ts +5 -5
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +11 -10
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +10 -7
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js +19 -11
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +21 -15
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +50 -28
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/row-query.d.ts +22 -21
- package/dist/row-query.d.ts.map +1 -1
- package/dist/row-query.js +62 -47
- package/dist/row-query.js.map +1 -1
- package/dist/schema/index.d.ts +3 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +3 -2
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/parse-utils.d.ts +6 -0
- package/dist/schema/parse-utils.d.ts.map +1 -0
- package/dist/schema/parse-utils.js +59 -0
- package/dist/schema/parse-utils.js.map +1 -0
- package/dist/schema/system-tables.d.ts +24 -8
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/table-def.d.ts +32 -7
- package/dist/schema/table-def.d.ts.map +1 -1
- package/dist/schema/table-def.js +18 -6
- package/dist/schema/table-def.js.map +1 -1
- package/dist/store.d.ts +4 -8
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +7 -8
- package/dist/store.js.map +1 -1
- package/dist/update-path.d.ts +52 -0
- package/dist/update-path.d.ts.map +1 -0
- package/dist/update-path.js +33 -0
- package/dist/update-path.js.map +1 -0
- package/dist/utils/util.d.ts +1 -0
- package/dist/utils/util.d.ts.map +1 -1
- package/dist/utils/util.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/react/fixture.tsx +13 -7
- package/src/__tests__/react/useQuery.test.tsx +12 -29
- package/src/__tests__/react/useRow.test.tsx +5 -7
- package/src/__tests__/reactiveQueries/sql.test.ts +33 -35
- package/src/effect/LiveStore.ts +3 -2
- package/src/index.ts +6 -6
- package/src/migrations.ts +2 -2
- package/src/mutations.ts +8 -3
- package/src/react/LiveStoreContext.ts +3 -2
- package/src/react/index.ts +1 -0
- package/src/react/useAtom.ts +25 -0
- package/src/react/useQuery.ts +7 -7
- package/src/react/useRow.ts +27 -47
- package/src/react/useTemporaryQuery.ts +4 -6
- package/src/reactiveQueries/base-class.ts +18 -4
- package/src/reactiveQueries/graphql.ts +16 -14
- package/src/reactiveQueries/js.ts +36 -15
- package/src/reactiveQueries/sql.ts +77 -37
- package/src/row-query.ts +155 -113
- package/src/schema/index.ts +5 -4
- package/src/schema/parse-utils.ts +84 -0
- package/src/schema/table-def.ts +80 -12
- package/src/store.ts +14 -29
- package/src/update-path.ts +102 -0
- package/src/utils/util.ts +2 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
|
|
3
3
|
import type { LiveStoreContext as LiveStoreContext_ } from '../effect/LiveStore.js'
|
|
4
|
-
import type {
|
|
4
|
+
import type { LiveQuery } from '../reactiveQueries/base-class.js'
|
|
5
5
|
|
|
6
|
+
// TODO remove this?
|
|
6
7
|
declare global {
|
|
7
8
|
// NOTE Can be extended
|
|
8
9
|
interface LiveStoreQueryTypes {
|
|
9
|
-
[key: string]:
|
|
10
|
+
[key: string]: LiveQuery<any>
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
13
|
|
package/src/react/index.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import type { LiveQuery } from '../reactiveQueries/base-class.js'
|
|
4
|
+
import { storeEventForUpdatePath, type UpdatePathDescCol, type UpdatePathDescRow } from '../update-path.js'
|
|
5
|
+
import { useStore } from './LiveStoreContext.js'
|
|
6
|
+
import { useQueryRef } from './useQuery.js'
|
|
7
|
+
import type { Dispatch, SetStateAction } from './useRow.js'
|
|
8
|
+
|
|
9
|
+
export const useAtom = <TQuery extends LiveQuery<any, UpdatePathDescRow<any> | UpdatePathDescCol<any, any>>>(
|
|
10
|
+
query$: TQuery,
|
|
11
|
+
): [value: TQuery['result!'], setValue: Dispatch<SetStateAction<TQuery['result!']>>] => {
|
|
12
|
+
const query$Ref = useQueryRef(query$)
|
|
13
|
+
|
|
14
|
+
const { store } = useStore()
|
|
15
|
+
|
|
16
|
+
const setValue = React.useMemo<Dispatch<SetStateAction<TQuery['result!']>>>(() => {
|
|
17
|
+
return (newValueOrFn: any) => {
|
|
18
|
+
const newValue = typeof newValueOrFn === 'function' ? newValueOrFn(query$Ref.current) : newValueOrFn
|
|
19
|
+
|
|
20
|
+
store.applyEvents([storeEventForUpdatePath(query$.updatePathDesc!, newValue)])
|
|
21
|
+
}
|
|
22
|
+
}, [query$.updatePathDesc, query$Ref, store])
|
|
23
|
+
|
|
24
|
+
return [query$Ref.current, setValue]
|
|
25
|
+
}
|
package/src/react/useQuery.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as otel from '@opentelemetry/api'
|
|
|
2
2
|
import { isEqual } from 'lodash-es'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { GetResult, LiveQuery, LiveQueryAny } from '../reactiveQueries/base-class.js'
|
|
6
6
|
import { useStore } from './LiveStoreContext.js'
|
|
7
7
|
import { extractStackInfoFromStackTrace, originalStackLimit } from './utils/stack-info.js'
|
|
8
8
|
import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInput.js'
|
|
@@ -12,14 +12,14 @@ import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInp
|
|
|
12
12
|
* so we need to "cache" the fact that we've already started a span for this component.
|
|
13
13
|
* The map entry is being removed again in the `React.useEffect` call below.
|
|
14
14
|
*/
|
|
15
|
-
const spanAlreadyStartedCache = new Map<
|
|
15
|
+
const spanAlreadyStartedCache = new Map<LiveQueryAny, { span: otel.Span; otelContext: otel.Context }>()
|
|
16
16
|
|
|
17
|
-
export const useQuery = <
|
|
17
|
+
export const useQuery = <TQuery extends LiveQueryAny>(query: TQuery): GetResult<TQuery> => useQueryRef(query).current
|
|
18
18
|
|
|
19
|
-
export const useQueryRef = <
|
|
20
|
-
query:
|
|
19
|
+
export const useQueryRef = <TQuery extends LiveQueryAny>(
|
|
20
|
+
query: TQuery,
|
|
21
21
|
parentOtelContext?: otel.Context,
|
|
22
|
-
): React.MutableRefObject<
|
|
22
|
+
): React.MutableRefObject<GetResult<TQuery>> => {
|
|
23
23
|
const { store } = useStore()
|
|
24
24
|
|
|
25
25
|
React.useDebugValue(`LiveStore:useQuery:${query.id}:${query.label}`)
|
|
@@ -62,7 +62,7 @@ export const useQueryRef = <TResult>(
|
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
// We know the query has a result by the time we use it; so we can synchronously populate a default state
|
|
65
|
-
const [valueRef, setValue] = useStateRefWithReactiveInput<
|
|
65
|
+
const [valueRef, setValue] = useStateRefWithReactiveInput<GetResult<TQuery>>(initialResult)
|
|
66
66
|
|
|
67
67
|
React.useEffect(
|
|
68
68
|
() => () => {
|
package/src/react/useRow.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { Schema } from '@livestore/utils/effect'
|
|
2
1
|
import * as otel from '@opentelemetry/api'
|
|
3
2
|
import type { SqliteDsl } from 'effect-db-schema'
|
|
4
3
|
import { mapValues } from 'lodash-es'
|
|
5
4
|
import React from 'react'
|
|
6
5
|
|
|
7
|
-
import type { DbGraph } from '../index.js'
|
|
8
|
-
import type {
|
|
9
|
-
import type { RowQueryArgs, RowResult } from '../row-query.js'
|
|
6
|
+
import type { DbGraph, LiveQuery } from '../index.js'
|
|
7
|
+
import type { RowResult } from '../row-query.js'
|
|
10
8
|
import { rowQuery } from '../row-query.js'
|
|
11
|
-
import type
|
|
9
|
+
import { type DefaultSqliteTableDef, type TableDef, tableIsSingleton, type TableOptions } from '../schema/table-def.js'
|
|
10
|
+
import type { UpdatePathDesc } from '../update-path.js'
|
|
11
|
+
import { storeEventForUpdatePath } from '../update-path.js'
|
|
12
12
|
import { useStore } from './LiveStoreContext.js'
|
|
13
13
|
import { useQueryRef } from './useQuery.js'
|
|
14
14
|
|
|
15
15
|
export type UseRowResult<TTableDef extends TableDef> = [
|
|
16
16
|
row: RowResult<TTableDef>,
|
|
17
17
|
setRow: StateSetters<TTableDef>,
|
|
18
|
-
query$:
|
|
18
|
+
query$: LiveQuery<RowResult<TTableDef>, UpdatePathDesc>,
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
export type UseRowOptionsDefaulValues<TTableDef extends TableDef> = {
|
|
@@ -31,7 +31,7 @@ export type UseRowOptionsBase = {
|
|
|
31
31
|
*
|
|
32
32
|
* - `row` is the current value of the row (fully decoded according to the table schema)
|
|
33
33
|
* - `setRow` is a function that can be used to update the row (values will be encoded according to the table schema)
|
|
34
|
-
* - `query$` is a `
|
|
34
|
+
* - `query$` is a `LiveQuery` that e.g. can be used to subscribe to changes to the row
|
|
35
35
|
*
|
|
36
36
|
* If the table is a singleton table, `useRow` can be called without an `id` argument. Otherwise, the `id` argument is required.
|
|
37
37
|
*/
|
|
@@ -51,12 +51,12 @@ export const useRow: {
|
|
|
51
51
|
idOrOptions?: string | UseRowOptionsBase,
|
|
52
52
|
options_?: UseRowOptionsBase & UseRowOptionsDefaulValues<TTableDef>,
|
|
53
53
|
): UseRowResult<TTableDef> => {
|
|
54
|
-
const sqliteTableDef = table.
|
|
54
|
+
const sqliteTableDef = table.sqliteDef
|
|
55
55
|
const id = typeof idOrOptions === 'string' ? idOrOptions : undefined
|
|
56
56
|
const options: (UseRowOptionsBase & UseRowOptionsDefaulValues<TTableDef>) | undefined =
|
|
57
57
|
typeof idOrOptions === 'string' ? options_ : idOrOptions
|
|
58
58
|
const { defaultValues, dbGraph } = options ?? {}
|
|
59
|
-
type TComponentState = SqliteDsl.FromColumns.RowDecoded<TTableDef['
|
|
59
|
+
type TComponentState = SqliteDsl.FromColumns.RowDecoded<TTableDef['sqliteDef']['columns']>
|
|
60
60
|
|
|
61
61
|
const { store } = useStore()
|
|
62
62
|
|
|
@@ -69,22 +69,26 @@ export const useRow: {
|
|
|
69
69
|
cachedItem.span.addEvent('new-subscriber', { reactId })
|
|
70
70
|
|
|
71
71
|
return {
|
|
72
|
-
query$: cachedItem.query$ as
|
|
72
|
+
query$: cachedItem.query$ as LiveQuery<RowResult<TTableDef>, UpdatePathDesc>,
|
|
73
73
|
otelContext: cachedItem.otelContext,
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
const span = store.otel.tracer.startSpan(
|
|
78
|
-
`LiveStore:useState:${table.
|
|
78
|
+
`LiveStore:useState:${table.sqliteDef.name}${id === undefined ? '' : `:${id}`}`,
|
|
79
79
|
{ attributes: { id } },
|
|
80
80
|
store.otel.queriesSpanContext,
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
84
84
|
|
|
85
|
-
const query$ = table
|
|
86
|
-
? rowQuery(
|
|
87
|
-
: rowQuery(
|
|
85
|
+
const query$ = tableIsSingleton(table)
|
|
86
|
+
? (rowQuery(table, { otelContext, dbGraph }) as LiveQuery<RowResult<TTableDef>, UpdatePathDesc>)
|
|
87
|
+
: (rowQuery(table as TTableDef & { options: { isSingleton: false } }, id!, {
|
|
88
|
+
otelContext,
|
|
89
|
+
defaultValues: defaultValues!,
|
|
90
|
+
dbGraph,
|
|
91
|
+
}) as any as LiveQuery<RowResult<TTableDef>, UpdatePathDesc>)
|
|
88
92
|
|
|
89
93
|
rcCache.set(table, id ?? 'singleton', query$, reactId, otelContext, span)
|
|
90
94
|
|
|
@@ -105,7 +109,7 @@ export const useRow: {
|
|
|
105
109
|
[table, id, reactId],
|
|
106
110
|
)
|
|
107
111
|
|
|
108
|
-
const query$Ref = useQueryRef(query$, otelContext)
|
|
112
|
+
const query$Ref = useQueryRef(query$, otelContext) as React.MutableRefObject<RowResult<TTableDef>>
|
|
109
113
|
|
|
110
114
|
const setState = React.useMemo<StateSetters<TTableDef>>(() => {
|
|
111
115
|
if (table.isSingleColumn) {
|
|
@@ -113,14 +117,7 @@ export const useRow: {
|
|
|
113
117
|
const newValue = typeof newValueOrFn === 'function' ? newValueOrFn(query$Ref.current) : newValueOrFn
|
|
114
118
|
if (query$Ref.current === newValue) return
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
store.applyEvent('livestore.UpdateComponentState', {
|
|
119
|
-
tableName: sqliteTableDef.name,
|
|
120
|
-
columnNames: ['value'],
|
|
121
|
-
id,
|
|
122
|
-
bindValues: { ['value']: encodedValue },
|
|
123
|
-
})
|
|
120
|
+
store.applyEvents([storeEventForUpdatePath(query$.updatePathDesc!, { value: newValue })])
|
|
124
121
|
}
|
|
125
122
|
} else {
|
|
126
123
|
const setState = // TODO: do we have a better type for the values that can go in SQLite?
|
|
@@ -133,14 +130,7 @@ export const useRow: {
|
|
|
133
130
|
// @ts-expect-error TODO fix typing
|
|
134
131
|
if (query$Ref.current[columnName] === newValue) return
|
|
135
132
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
store.applyEvent('livestore.UpdateComponentState', {
|
|
139
|
-
tableName: sqliteTableDef.name,
|
|
140
|
-
columnNames: [columnName],
|
|
141
|
-
id,
|
|
142
|
-
bindValues: { [columnName]: encodedValue },
|
|
143
|
-
})
|
|
133
|
+
store.applyEvents([storeEventForUpdatePath(query$.updatePathDesc!, { [columnName]: newValue })])
|
|
144
134
|
})
|
|
145
135
|
|
|
146
136
|
setState.setMany = (columnValuesOrFn: Partial<TComponentState>) => {
|
|
@@ -157,22 +147,12 @@ export const useRow: {
|
|
|
157
147
|
return
|
|
158
148
|
}
|
|
159
149
|
|
|
160
|
-
|
|
161
|
-
const bindValues = mapValues(columnValues, (value, columnName) =>
|
|
162
|
-
Schema.encodeSync(sqliteTableDef.columns[columnName]!.schema)(value),
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
store.applyEvent('livestore.UpdateComponentState', {
|
|
166
|
-
tableName: sqliteTableDef.name,
|
|
167
|
-
columnNames,
|
|
168
|
-
id,
|
|
169
|
-
bindValues,
|
|
170
|
-
})
|
|
150
|
+
store.applyEvents([storeEventForUpdatePath(query$.updatePathDesc!, { columnValues })])
|
|
171
151
|
}
|
|
172
152
|
|
|
173
153
|
return setState as any
|
|
174
154
|
}
|
|
175
|
-
}, [
|
|
155
|
+
}, [query$.updatePathDesc, query$Ref, sqliteTableDef.columns, store, table.isSingleColumn])
|
|
176
156
|
|
|
177
157
|
return [query$Ref.current, setState, query$]
|
|
178
158
|
}
|
|
@@ -198,11 +178,11 @@ class RCCache {
|
|
|
198
178
|
reactIds: Set<string>
|
|
199
179
|
span: otel.Span
|
|
200
180
|
otelContext: otel.Context
|
|
201
|
-
query$:
|
|
181
|
+
query$: LiveQuery<any, any>
|
|
202
182
|
}
|
|
203
183
|
>
|
|
204
184
|
>()
|
|
205
|
-
private reverseCache = new Map<
|
|
185
|
+
private reverseCache = new Map<LiveQuery<any, any>, [TableDef, string]>()
|
|
206
186
|
|
|
207
187
|
get = (table: TableDef, id: string) => {
|
|
208
188
|
const queries = this.cache.get(table)
|
|
@@ -213,7 +193,7 @@ class RCCache {
|
|
|
213
193
|
set = (
|
|
214
194
|
table: TableDef,
|
|
215
195
|
id: string,
|
|
216
|
-
query$:
|
|
196
|
+
query$: LiveQuery<any, any>,
|
|
217
197
|
reactId: string,
|
|
218
198
|
otelContext: otel.Context,
|
|
219
199
|
span: otel.Span,
|
|
@@ -227,7 +207,7 @@ class RCCache {
|
|
|
227
207
|
this.reverseCache.set(query$, [table, id])
|
|
228
208
|
}
|
|
229
209
|
|
|
230
|
-
delete = (query$:
|
|
210
|
+
delete = (query$: LiveQuery<any, any>) => {
|
|
231
211
|
const item = this.reverseCache.get(query$)
|
|
232
212
|
if (item === undefined) return
|
|
233
213
|
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { LiveQuery } from '../reactiveQueries/base-class.js'
|
|
4
4
|
import { useQueryRef } from './useQuery.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* This is needed because the `React.useMemo` call below, can sometimes be called multiple times 🤷.
|
|
8
8
|
* The map entry is being removed again in the `React.useEffect` call below.
|
|
9
9
|
*/
|
|
10
|
-
const queryCache = new Map<() =>
|
|
10
|
+
const queryCache = new Map<() => LiveQuery<any>, { reactIds: Set<string>; query$: LiveQuery<any> }>()
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Creates a query, subscribes and destroys it when the component unmounts.
|
|
14
14
|
*
|
|
15
15
|
* Make sure `makeQuery` is a memoized function.
|
|
16
16
|
*/
|
|
17
|
-
export const useTemporaryQuery = <TResult>(makeQuery: () =>
|
|
17
|
+
export const useTemporaryQuery = <TResult>(makeQuery: () => LiveQuery<TResult>): TResult =>
|
|
18
18
|
useTemporaryQueryRef(makeQuery).current
|
|
19
19
|
|
|
20
|
-
export const useTemporaryQueryRef = <TResult>(
|
|
21
|
-
makeQuery: () => ILiveStoreQuery<TResult>,
|
|
22
|
-
): React.MutableRefObject<TResult> => {
|
|
20
|
+
export const useTemporaryQueryRef = <TResult>(makeQuery: () => LiveQuery<TResult>): React.MutableRefObject<TResult> => {
|
|
23
21
|
const reactId = React.useId()
|
|
24
22
|
|
|
25
23
|
const query$ = React.useMemo(() => {
|
|
@@ -4,7 +4,7 @@ import ReactDOM from 'react-dom'
|
|
|
4
4
|
import type { StackInfo } from '../react/utils/stack-info.js'
|
|
5
5
|
import { type Atom, type GetAtom, ReactiveGraph, throwContextNotSetError, type Thunk } from '../reactive.js'
|
|
6
6
|
import type { QueryDebugInfo, RefreshReason, Store } from '../store.js'
|
|
7
|
-
import type {
|
|
7
|
+
import type { UpdatePathDesc, UpdatePathDescNone } from '../update-path.js'
|
|
8
8
|
|
|
9
9
|
export type DbGraph = ReactiveGraph<RefreshReason, QueryDebugInfo, DbContext>
|
|
10
10
|
|
|
@@ -22,10 +22,16 @@ export type DbContext = {
|
|
|
22
22
|
|
|
23
23
|
export type UnsubscribeQuery = () => void
|
|
24
24
|
|
|
25
|
+
export type GetResult<TQuery extends LiveQueryAny> = TQuery extends LiveQuery<infer TResult> ? TResult : unknown
|
|
26
|
+
|
|
25
27
|
let queryIdCounter = 0
|
|
26
28
|
|
|
27
|
-
export
|
|
29
|
+
export type LiveQueryAny = LiveQuery<any, UpdatePathDesc>
|
|
30
|
+
|
|
31
|
+
export interface LiveQuery<TResult, TUpdatePath extends UpdatePathDesc = UpdatePathDescNone> {
|
|
28
32
|
id: number
|
|
33
|
+
_tag: 'js' | 'sql' | 'graphql'
|
|
34
|
+
'result!': TResult
|
|
29
35
|
|
|
30
36
|
/** A reactive thunk representing the query results */
|
|
31
37
|
results$: Thunk<TResult, DbContext, RefreshReason>
|
|
@@ -37,10 +43,16 @@ export interface ILiveStoreQuery<TResult> {
|
|
|
37
43
|
destroy(): void
|
|
38
44
|
|
|
39
45
|
activeSubscriptions: Set<StackInfo>
|
|
46
|
+
|
|
47
|
+
updatePathDesc: TUpdatePath
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
export abstract class LiveStoreQueryBase<TResult
|
|
50
|
+
export abstract class LiveStoreQueryBase<TResult, TUpdatePath extends UpdatePathDesc>
|
|
51
|
+
implements LiveQuery<TResult, TUpdatePath>
|
|
52
|
+
{
|
|
53
|
+
'result!'!: TResult
|
|
43
54
|
id = queryIdCounter++
|
|
55
|
+
abstract _tag: 'js' | 'sql' | 'graphql'
|
|
44
56
|
|
|
45
57
|
/** Human-readable label for the query for debugging */
|
|
46
58
|
abstract label: string
|
|
@@ -51,6 +63,8 @@ export abstract class LiveStoreQueryBase<TResult> implements ILiveStoreQuery<TRe
|
|
|
51
63
|
|
|
52
64
|
protected abstract dbGraph: DbGraph
|
|
53
65
|
|
|
66
|
+
abstract updatePathDesc: TUpdatePath
|
|
67
|
+
|
|
54
68
|
get runs() {
|
|
55
69
|
return this.results$.recomputations
|
|
56
70
|
}
|
|
@@ -75,7 +89,7 @@ export abstract class LiveStoreQueryBase<TResult> implements ILiveStoreQuery<TRe
|
|
|
75
89
|
throwContextNotSetError(this.dbGraph)
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
export type GetAtomResult = <T>(atom: Atom<T, any, RefreshReason> |
|
|
92
|
+
export type GetAtomResult = <T>(atom: Atom<T, any, RefreshReason> | LiveQuery<T, any>) => T
|
|
79
93
|
|
|
80
94
|
export const makeGetAtomResult = (get: GetAtom, otelContext: otel.Context) => {
|
|
81
95
|
const getAtom: GetAtomResult = (atom) => {
|
|
@@ -6,22 +6,22 @@ import * as graphql from 'graphql'
|
|
|
6
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
|
+
import type { UpdatePathDescNone } from '../update-path.js'
|
|
9
10
|
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
10
|
-
import type { DbContext, DbGraph, GetAtomResult } from './base-class.js'
|
|
11
|
+
import type { DbContext, DbGraph, GetAtomResult, LiveQuery } from './base-class.js'
|
|
11
12
|
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
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
17
|
{ label, dbGraph }: { label?: string; dbGraph?: DbGraph } = {},
|
|
18
|
-
) => new LiveStoreGraphQLQuery({ document, genVariableValues, label, dbGraph })
|
|
18
|
+
): LiveQuery<TResult, UpdatePathDescNone> => new LiveStoreGraphQLQuery({ document, genVariableValues, label, dbGraph })
|
|
19
19
|
|
|
20
20
|
export class LiveStoreGraphQLQuery<
|
|
21
21
|
TResult extends Record<string, any>,
|
|
22
22
|
TVariableValues extends Record<string, any>,
|
|
23
23
|
TContext extends BaseGraphQLContext,
|
|
24
|
-
> extends LiveStoreQueryBase<TResult> {
|
|
24
|
+
> extends LiveStoreQueryBase<TResult, UpdatePathDescNone> {
|
|
25
25
|
_tag: 'graphql' = 'graphql'
|
|
26
26
|
|
|
27
27
|
/** The abstract GraphQL query */
|
|
@@ -36,6 +36,8 @@ export class LiveStoreGraphQLQuery<
|
|
|
36
36
|
|
|
37
37
|
protected dbGraph: DbGraph
|
|
38
38
|
|
|
39
|
+
updatePathDesc: UpdatePathDescNone = { _tag: 'None' }
|
|
40
|
+
|
|
39
41
|
constructor({
|
|
40
42
|
document,
|
|
41
43
|
label,
|
|
@@ -102,16 +104,16 @@ export class LiveStoreGraphQLQuery<
|
|
|
102
104
|
* Returns a new reactive query that contains the result of
|
|
103
105
|
* running an arbitrary JS computation on the results of this SQL query.
|
|
104
106
|
*/
|
|
105
|
-
pipe = <U>(fn: (result: TResult, get: GetAtomResult) => U): LiveStoreJSQuery<U> =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
// pipe = <U>(fn: (result: TResult, get: GetAtomResult) => U): LiveStoreJSQuery<U> =>
|
|
108
|
+
// new LiveStoreJSQuery({
|
|
109
|
+
// fn: (get) => {
|
|
110
|
+
// const results = get(this.results$)
|
|
111
|
+
// return fn(results, get)
|
|
112
|
+
// },
|
|
113
|
+
// label: `${this.label}:js`,
|
|
114
|
+
// onDestroy: () => this.destroy(),
|
|
115
|
+
// dbGraph: this.dbGraph,
|
|
116
|
+
// })
|
|
115
117
|
|
|
116
118
|
queryOnce = ({
|
|
117
119
|
document,
|
|
@@ -3,14 +3,30 @@ import * as otel from '@opentelemetry/api'
|
|
|
3
3
|
import { globalDbGraph } from '../global-state.js'
|
|
4
4
|
import type { Thunk } from '../reactive.js'
|
|
5
5
|
import type { RefreshReason } from '../store.js'
|
|
6
|
+
import type { UpdatePathDesc, UpdatePathDescNone } from '../update-path.js'
|
|
6
7
|
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
7
|
-
import type { DbContext, DbGraph, GetAtomResult } from './base-class.js'
|
|
8
|
+
import type { DbContext, DbGraph, GetAtomResult, LiveQuery } from './base-class.js'
|
|
8
9
|
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
9
10
|
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export const computed = <TResult, TUpdatePath extends UpdatePathDesc = UpdatePathDescNone>(
|
|
12
|
+
fn: (get: GetAtomResult) => TResult,
|
|
13
|
+
options?: {
|
|
14
|
+
label: string
|
|
15
|
+
dbGraph?: DbGraph
|
|
16
|
+
updatePathDesc?: TUpdatePath
|
|
17
|
+
},
|
|
18
|
+
): LiveQuery<TResult, TUpdatePath> =>
|
|
19
|
+
new LiveStoreJSQuery<TResult, TUpdatePath>({
|
|
20
|
+
fn,
|
|
21
|
+
label: options?.label ?? fn.toString(),
|
|
22
|
+
dbGraph: options?.dbGraph,
|
|
23
|
+
updatePathDesc: options?.updatePathDesc,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export class LiveStoreJSQuery<
|
|
27
|
+
TResult,
|
|
28
|
+
TUpdatePath extends UpdatePathDesc = UpdatePathDescNone,
|
|
29
|
+
> extends LiveStoreQueryBase<TResult, TUpdatePath> {
|
|
14
30
|
_tag: 'js' = 'js'
|
|
15
31
|
|
|
16
32
|
/** A reactive thunk representing the query results */
|
|
@@ -20,6 +36,8 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
|
|
|
20
36
|
|
|
21
37
|
protected dbGraph: DbGraph
|
|
22
38
|
|
|
39
|
+
updatePathDesc: TUpdatePath
|
|
40
|
+
|
|
23
41
|
/**
|
|
24
42
|
* Currently only used for "nested destruction" of piped queries
|
|
25
43
|
*
|
|
@@ -33,12 +51,14 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
|
|
|
33
51
|
label,
|
|
34
52
|
onDestroy,
|
|
35
53
|
dbGraph,
|
|
54
|
+
updatePathDesc,
|
|
36
55
|
}: {
|
|
37
56
|
label: string
|
|
38
57
|
fn: (get: GetAtomResult) => TResult
|
|
39
58
|
/** Currently only used for "nested destruction" of piped queries */
|
|
40
59
|
onDestroy?: () => void
|
|
41
60
|
dbGraph?: DbGraph
|
|
61
|
+
updatePathDesc?: TUpdatePath
|
|
42
62
|
}) {
|
|
43
63
|
super()
|
|
44
64
|
|
|
@@ -46,6 +66,7 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
|
|
|
46
66
|
this.label = label
|
|
47
67
|
|
|
48
68
|
this.dbGraph = dbGraph ?? globalDbGraph
|
|
69
|
+
this.updatePathDesc = updatePathDesc ?? ({ _tag: 'None' } as TUpdatePath)
|
|
49
70
|
|
|
50
71
|
const queryLabel = `${label}:results`
|
|
51
72
|
|
|
@@ -67,16 +88,16 @@ export class LiveStoreJSQuery<TResult> extends LiveStoreQueryBase<TResult> {
|
|
|
67
88
|
)
|
|
68
89
|
}
|
|
69
90
|
|
|
70
|
-
pipe = <U>(fn: (result: TResult, get: GetAtomResult) => U): LiveStoreJSQuery<U> =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
// pipe = <U>(fn: (result: TResult, get: GetAtomResult) => U): LiveStoreJSQuery<U> =>
|
|
92
|
+
// new LiveStoreJSQuery({
|
|
93
|
+
// fn: (get) => {
|
|
94
|
+
// const results = get(this.results$)
|
|
95
|
+
// return fn(results, get)
|
|
96
|
+
// },
|
|
97
|
+
// label: `${this.label}:js`,
|
|
98
|
+
// onDestroy: () => this.destroy(),
|
|
99
|
+
// dbGraph: this.dbGraph,
|
|
100
|
+
// })
|
|
80
101
|
|
|
81
102
|
destroy = () => {
|
|
82
103
|
this.dbGraph.destroyNode(this.results$)
|