@livestore/livestore 0.1.0-dev.9 → 0.2.0-dev.0
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 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/global-state.d.ts +1 -1
- package/dist/global-state.d.ts.map +1 -1
- package/dist/global-state.js +1 -1
- package/dist/global-state.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/live-queries/base-class.d.ts +64 -0
- package/dist/live-queries/base-class.d.ts.map +1 -0
- package/dist/live-queries/base-class.js +31 -0
- package/dist/live-queries/base-class.js.map +1 -0
- package/dist/live-queries/computed.d.ts +26 -0
- package/dist/live-queries/computed.d.ts.map +1 -0
- package/dist/{reactiveQueries/js.js → live-queries/computed.js} +7 -26
- package/dist/live-queries/computed.js.map +1 -0
- package/dist/live-queries/db.d.ts +66 -0
- package/dist/live-queries/db.d.ts.map +1 -0
- package/dist/live-queries/db.js +199 -0
- package/dist/live-queries/db.js.map +1 -0
- package/dist/live-queries/db.test.d.ts +2 -0
- package/dist/live-queries/db.test.d.ts.map +1 -0
- package/dist/live-queries/db.test.js +117 -0
- package/dist/live-queries/db.test.js.map +1 -0
- package/dist/live-queries/graphql.d.ts +49 -0
- package/dist/live-queries/graphql.d.ts.map +1 -0
- package/dist/live-queries/graphql.js +122 -0
- package/dist/live-queries/graphql.js.map +1 -0
- package/dist/live-queries/sql.d.ts +62 -0
- package/dist/live-queries/sql.d.ts.map +1 -0
- package/dist/live-queries/sql.js +175 -0
- package/dist/live-queries/sql.js.map +1 -0
- package/dist/live-queries/sql.test.d.ts +2 -0
- package/dist/live-queries/sql.test.d.ts.map +1 -0
- package/{src/reactiveQueries/sql.test.ts → dist/live-queries/sql.test.js} +68 -91
- package/dist/live-queries/sql.test.js.map +1 -0
- package/dist/reactiveQueries/base-class.d.ts +6 -2
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +2 -0
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/computed.d.ts +4 -13
- package/dist/reactiveQueries/computed.d.ts.map +1 -1
- package/dist/reactiveQueries/computed.js +2 -21
- package/dist/reactiveQueries/computed.js.map +1 -1
- package/dist/reactiveQueries/graphql.d.ts +4 -8
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +1 -15
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +36 -23
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +100 -55
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/reactiveQueries/sql.test.js +12 -11
- package/dist/reactiveQueries/sql.test.js.map +1 -1
- package/dist/row-query-utils.d.ts +17 -0
- package/dist/row-query-utils.d.ts.map +1 -0
- package/dist/row-query-utils.js +30 -0
- package/dist/row-query-utils.js.map +1 -0
- package/dist/row-query.d.ts +10 -27
- package/dist/row-query.d.ts.map +1 -1
- package/dist/row-query.js +16 -66
- package/dist/row-query.js.map +1 -1
- package/dist/store/create-store.d.ts +1 -1
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/devtools.d.ts +1 -1
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-types.d.ts +2 -2
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store.d.ts +8 -3
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +32 -4
- package/dist/store/store.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +168 -132
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/global-state.ts +1 -1
- package/src/index.ts +8 -5
- package/src/live-queries/__snapshots__/db.test.ts.snap +301 -0
- package/src/{reactiveQueries → live-queries}/base-class.ts +10 -5
- package/src/{reactiveQueries → live-queries}/computed.ts +5 -29
- package/src/live-queries/db.test.ts +153 -0
- package/src/live-queries/db.ts +350 -0
- package/src/{reactiveQueries → live-queries}/graphql.ts +6 -21
- package/src/row-query-utils.ts +65 -0
- package/src/store/create-store.ts +1 -1
- package/src/store/devtools.ts +1 -1
- package/src/store/store-types.ts +2 -2
- package/src/store/store.ts +44 -7
- package/dist/reactiveQueries/js.d.ts +0 -35
- package/dist/reactiveQueries/js.d.ts.map +0 -1
- package/dist/reactiveQueries/js.js.map +0 -1
- package/dist/store/store-context.d.ts +0 -26
- package/dist/store/store-context.d.ts.map +0 -1
- package/dist/store/store-context.js +0 -6
- package/dist/store/store-context.js.map +0 -1
- package/dist/store-context.d.ts +0 -26
- package/dist/store-context.d.ts.map +0 -1
- package/dist/store-context.js +0 -6
- package/dist/store-context.js.map +0 -1
- package/dist/store-devtools.d.ts +0 -19
- package/dist/store-devtools.d.ts.map +0 -1
- package/dist/store-devtools.js +0 -141
- package/dist/store-devtools.js.map +0 -1
- package/dist/store.d.ts +0 -175
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -509
- package/dist/store.js.map +0 -1
- package/src/reactiveQueries/sql.ts +0 -226
- package/src/row-query.ts +0 -196
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { type Bindable, prepareBindValues, type QueryInfo, type QueryInfoNone } from '@livestore/common'
|
|
2
|
-
import { shouldNeverHappen } from '@livestore/utils'
|
|
3
|
-
import { Schema, TreeFormatter } from '@livestore/utils/effect'
|
|
4
|
-
import * as otel from '@opentelemetry/api'
|
|
5
|
-
|
|
6
|
-
import { globalReactivityGraph } from '../global-state.js'
|
|
7
|
-
import type { Thunk } from '../reactive.js'
|
|
8
|
-
import { NOT_REFRESHED_YET } from '../reactive.js'
|
|
9
|
-
import type { RefreshReason } from '../store/store-types.js'
|
|
10
|
-
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
11
|
-
import type { GetAtomResult, LiveQuery, QueryContext, ReactivityGraph } from './base-class.js'
|
|
12
|
-
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* NOTE `querySQL` is only supposed to read data. Don't use it to insert/update/delete data but use mutations instead.
|
|
16
|
-
*/
|
|
17
|
-
export const querySQL = <TResultSchema, TResult = TResultSchema>(
|
|
18
|
-
query: string | ((get: GetAtomResult) => string),
|
|
19
|
-
options: {
|
|
20
|
-
schema: Schema.Schema<TResultSchema, ReadonlyArray<any>>
|
|
21
|
-
map?: (rows: TResultSchema) => TResult
|
|
22
|
-
/**
|
|
23
|
-
* Can be provided explicitly to slightly speed up initial query performance
|
|
24
|
-
*
|
|
25
|
-
* NOTE In the future we want to do this automatically at build time
|
|
26
|
-
*/
|
|
27
|
-
queriedTables?: Set<string>
|
|
28
|
-
bindValues?: Bindable
|
|
29
|
-
label?: string
|
|
30
|
-
reactivityGraph?: ReactivityGraph
|
|
31
|
-
},
|
|
32
|
-
): LiveQuery<TResult, QueryInfoNone> =>
|
|
33
|
-
new LiveStoreSQLQuery<TResultSchema, TResult, QueryInfoNone>({
|
|
34
|
-
label: options?.label,
|
|
35
|
-
genQueryString: query,
|
|
36
|
-
queriedTables: options?.queriedTables,
|
|
37
|
-
bindValues: options?.bindValues,
|
|
38
|
-
reactivityGraph: options?.reactivityGraph,
|
|
39
|
-
map: options?.map,
|
|
40
|
-
schema: options.schema,
|
|
41
|
-
queryInfo: { _tag: 'None' },
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
/* An object encapsulating a reactive SQL query */
|
|
45
|
-
export class LiveStoreSQLQuery<
|
|
46
|
-
TResultSchema,
|
|
47
|
-
TResult = TResultSchema,
|
|
48
|
-
TQueryInfo extends QueryInfo = QueryInfoNone,
|
|
49
|
-
> extends LiveStoreQueryBase<TResult, TQueryInfo> {
|
|
50
|
-
_tag: 'sql' = 'sql'
|
|
51
|
-
|
|
52
|
-
/** A reactive thunk representing the query text */
|
|
53
|
-
queryString$: Thunk<string, QueryContext, RefreshReason> | undefined
|
|
54
|
-
|
|
55
|
-
/** A reactive thunk representing the query results */
|
|
56
|
-
results$: Thunk<TResult, QueryContext, RefreshReason>
|
|
57
|
-
|
|
58
|
-
label: string
|
|
59
|
-
|
|
60
|
-
protected reactivityGraph
|
|
61
|
-
|
|
62
|
-
/** Currently only used by `rowQuery` for lazy table migrations and eager default row insertion */
|
|
63
|
-
private execBeforeFirstRun
|
|
64
|
-
|
|
65
|
-
private mapResult: (rows: TResultSchema) => TResult
|
|
66
|
-
private schema: Schema.Schema<TResultSchema, ReadonlyArray<any>>
|
|
67
|
-
|
|
68
|
-
queryInfo: TQueryInfo
|
|
69
|
-
|
|
70
|
-
constructor({
|
|
71
|
-
genQueryString,
|
|
72
|
-
queriedTables,
|
|
73
|
-
bindValues,
|
|
74
|
-
label = genQueryString.toString(),
|
|
75
|
-
reactivityGraph,
|
|
76
|
-
schema,
|
|
77
|
-
map,
|
|
78
|
-
execBeforeFirstRun,
|
|
79
|
-
queryInfo,
|
|
80
|
-
}: {
|
|
81
|
-
label?: string
|
|
82
|
-
genQueryString: string | ((get: GetAtomResult, ctx: QueryContext) => string)
|
|
83
|
-
queriedTables?: Set<string>
|
|
84
|
-
bindValues?: Bindable
|
|
85
|
-
reactivityGraph?: ReactivityGraph
|
|
86
|
-
schema: Schema.Schema<TResultSchema, ReadonlyArray<any>>
|
|
87
|
-
map?: (rows: TResultSchema) => TResult
|
|
88
|
-
execBeforeFirstRun?: (ctx: QueryContext) => void
|
|
89
|
-
queryInfo?: TQueryInfo
|
|
90
|
-
}) {
|
|
91
|
-
super()
|
|
92
|
-
|
|
93
|
-
this.label = `sql(${label})`
|
|
94
|
-
this.reactivityGraph = reactivityGraph ?? globalReactivityGraph
|
|
95
|
-
this.execBeforeFirstRun = execBeforeFirstRun
|
|
96
|
-
this.queryInfo = queryInfo ?? ({ _tag: 'None' } as TQueryInfo)
|
|
97
|
-
|
|
98
|
-
this.schema = schema
|
|
99
|
-
this.mapResult = map === undefined ? (rows: any) => rows as TResult : map
|
|
100
|
-
|
|
101
|
-
let queryString$OrQueryString: string | Thunk<string, QueryContext, RefreshReason>
|
|
102
|
-
if (typeof genQueryString === 'function') {
|
|
103
|
-
queryString$OrQueryString = this.reactivityGraph.makeThunk(
|
|
104
|
-
(get, setDebugInfo, ctx, otelContext) => {
|
|
105
|
-
const startMs = performance.now()
|
|
106
|
-
const queryString = genQueryString(makeGetAtomResult(get, otelContext ?? ctx.rootOtelContext), ctx)
|
|
107
|
-
const durationMs = performance.now() - startMs
|
|
108
|
-
setDebugInfo({ _tag: 'computed', label: `${label}:queryString`, query: queryString, durationMs })
|
|
109
|
-
return queryString
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
label: `${label}:queryString`,
|
|
113
|
-
meta: { liveStoreThunkType: 'sqlQueryString' },
|
|
114
|
-
equal: (a, b) => a === b,
|
|
115
|
-
},
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
this.queryString$ = queryString$OrQueryString
|
|
119
|
-
} else {
|
|
120
|
-
queryString$OrQueryString = genQueryString
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const queryLabel = `${label}:results`
|
|
124
|
-
|
|
125
|
-
const queriedTablesRef = { current: queriedTables }
|
|
126
|
-
|
|
127
|
-
const schemaEqual = Schema.equivalence(schema)
|
|
128
|
-
// TODO also support derived equality for `map` (probably will depend on having an easy way to transform a schema without an `encode` step)
|
|
129
|
-
// This would mean dropping the `map` option
|
|
130
|
-
const equal =
|
|
131
|
-
map === undefined
|
|
132
|
-
? (a: TResult, b: TResult) =>
|
|
133
|
-
a === NOT_REFRESHED_YET || b === NOT_REFRESHED_YET ? false : schemaEqual(a as any, b as any)
|
|
134
|
-
: undefined
|
|
135
|
-
|
|
136
|
-
const results$ = this.reactivityGraph.makeThunk<TResult>(
|
|
137
|
-
(get, setDebugInfo, { store, otelTracer, rootOtelContext }, otelContext) =>
|
|
138
|
-
otelTracer.startActiveSpan(
|
|
139
|
-
'sql:...', // NOTE span name will be overridden further down
|
|
140
|
-
{},
|
|
141
|
-
otelContext ?? rootOtelContext,
|
|
142
|
-
(span) => {
|
|
143
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
144
|
-
|
|
145
|
-
if (this.execBeforeFirstRun !== undefined) {
|
|
146
|
-
this.execBeforeFirstRun({ store, otelTracer, rootOtelContext, effectsWrapper: (run) => run() })
|
|
147
|
-
this.execBeforeFirstRun = undefined
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const sqlString =
|
|
151
|
-
typeof queryString$OrQueryString === 'string'
|
|
152
|
-
? queryString$OrQueryString
|
|
153
|
-
: get(queryString$OrQueryString, otelContext)
|
|
154
|
-
|
|
155
|
-
if (queriedTablesRef.current === undefined) {
|
|
156
|
-
queriedTablesRef.current = store.syncDbWrapper.getTablesUsed(sqlString)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Establish a reactive dependency on the tables used in the query
|
|
160
|
-
for (const tableName of queriedTablesRef.current) {
|
|
161
|
-
const tableRef = store.tableRefs[tableName] ?? shouldNeverHappen(`No table ref found for ${tableName}`)
|
|
162
|
-
get(tableRef, otelContext)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
span.setAttribute('sql.query', sqlString)
|
|
166
|
-
span.updateName(`sql:${sqlString.slice(0, 50)}`)
|
|
167
|
-
|
|
168
|
-
const rawResults = store.syncDbWrapper.select<any>(sqlString, {
|
|
169
|
-
queriedTables,
|
|
170
|
-
bindValues: bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
|
|
171
|
-
otelContext,
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
span.setAttribute('sql.rowsCount', rawResults.length)
|
|
175
|
-
|
|
176
|
-
const parsedResult = Schema.decodeEither(this.schema)(rawResults)
|
|
177
|
-
|
|
178
|
-
if (parsedResult._tag === 'Left') {
|
|
179
|
-
const parseErrorStr = TreeFormatter.formatErrorSync(parsedResult.left)
|
|
180
|
-
const expectedSchemaStr = String(this.schema.ast)
|
|
181
|
-
const bindValuesStr = bindValues === undefined ? '' : `\nBind values: ${JSON.stringify(bindValues)}`
|
|
182
|
-
|
|
183
|
-
console.error(
|
|
184
|
-
`\
|
|
185
|
-
Error parsing SQL query result.
|
|
186
|
-
|
|
187
|
-
Query: ${sqlString}\
|
|
188
|
-
${bindValuesStr}
|
|
189
|
-
|
|
190
|
-
Expected schema: ${expectedSchemaStr}
|
|
191
|
-
|
|
192
|
-
Error: ${parseErrorStr}
|
|
193
|
-
|
|
194
|
-
Result:`,
|
|
195
|
-
rawResults,
|
|
196
|
-
)
|
|
197
|
-
return shouldNeverHappen(`Error parsing SQL query result: ${parsedResult.left}`)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const result = this.mapResult(parsedResult.right)
|
|
201
|
-
|
|
202
|
-
span.end()
|
|
203
|
-
|
|
204
|
-
const durationMs = getDurationMsFromSpan(span)
|
|
205
|
-
|
|
206
|
-
this.executionTimes.push(durationMs)
|
|
207
|
-
|
|
208
|
-
setDebugInfo({ _tag: 'sql', label, query: sqlString, durationMs })
|
|
209
|
-
|
|
210
|
-
return result
|
|
211
|
-
},
|
|
212
|
-
),
|
|
213
|
-
{ label: queryLabel, equal },
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
this.results$ = results$
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
destroy = () => {
|
|
220
|
-
if (this.queryString$ !== undefined) {
|
|
221
|
-
this.reactivityGraph.destroyNode(this.queryString$)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
this.reactivityGraph.destroyNode(this.results$)
|
|
225
|
-
}
|
|
226
|
-
}
|
package/src/row-query.ts
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import type { QueryInfoCol, QueryInfoNone, QueryInfoRow } from '@livestore/common'
|
|
2
|
-
import { SessionIdSymbol, sql } from '@livestore/common'
|
|
3
|
-
import { DbSchema } from '@livestore/common/schema'
|
|
4
|
-
import type { SqliteDsl } from '@livestore/db-schema'
|
|
5
|
-
import type { GetValForKey } from '@livestore/utils'
|
|
6
|
-
import { shouldNeverHappen } from '@livestore/utils'
|
|
7
|
-
import { Schema } from '@livestore/utils/effect'
|
|
8
|
-
import type * as otel from '@opentelemetry/api'
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
GetAtomResult,
|
|
12
|
-
LiveQuery,
|
|
13
|
-
LiveQueryAny,
|
|
14
|
-
QueryContext,
|
|
15
|
-
ReactivityGraph,
|
|
16
|
-
} from './reactiveQueries/base-class.js'
|
|
17
|
-
import { computed } from './reactiveQueries/computed.js'
|
|
18
|
-
import { LiveStoreSQLQuery } from './reactiveQueries/sql.js'
|
|
19
|
-
import type { Store } from './store/store.js'
|
|
20
|
-
|
|
21
|
-
export type RowQueryOptions<TTableDef extends DbSchema.TableDef, TResult = RowResult<TTableDef>> = {
|
|
22
|
-
otelContext?: otel.Context
|
|
23
|
-
skipInsertDefaultRow?: boolean
|
|
24
|
-
reactivityGraph?: ReactivityGraph
|
|
25
|
-
map?: (result: RowResult<TTableDef>) => TResult
|
|
26
|
-
label?: string
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type RowQueryOptionsDefaulValues<TTableDef extends DbSchema.TableDef> = {
|
|
30
|
-
defaultValues?: Partial<RowResult<TTableDef>>
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type MakeRowQuery = {
|
|
34
|
-
<
|
|
35
|
-
TTableDef extends DbSchema.TableDef<
|
|
36
|
-
DbSchema.DefaultSqliteTableDef,
|
|
37
|
-
boolean,
|
|
38
|
-
DbSchema.TableOptions & { isSingleton: true }
|
|
39
|
-
>,
|
|
40
|
-
TResult = RowResult<TTableDef>,
|
|
41
|
-
>(
|
|
42
|
-
table: TTableDef,
|
|
43
|
-
options?: RowQueryOptions<TTableDef, TResult>,
|
|
44
|
-
): LiveQuery<RowResult<TTableDef>, QueryInfoRow<TTableDef>>
|
|
45
|
-
<
|
|
46
|
-
TTableDef extends DbSchema.TableDef<
|
|
47
|
-
DbSchema.DefaultSqliteTableDef,
|
|
48
|
-
boolean,
|
|
49
|
-
DbSchema.TableOptions & { isSingleton: false }
|
|
50
|
-
>,
|
|
51
|
-
TResult = RowResult<TTableDef>,
|
|
52
|
-
>(
|
|
53
|
-
table: TTableDef,
|
|
54
|
-
// TODO adjust so it works with arbitrary primary keys or unique constraints
|
|
55
|
-
id: string | SessionIdSymbol,
|
|
56
|
-
options?: RowQueryOptions<TTableDef, TResult> & RowQueryOptionsDefaulValues<TTableDef>,
|
|
57
|
-
): LiveQuery<TResult, QueryInfoRow<TTableDef>>
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// TODO also allow other where clauses and multiple rows
|
|
61
|
-
export const rowQuery: MakeRowQuery = <TTableDef extends DbSchema.TableDef>(
|
|
62
|
-
table: TTableDef,
|
|
63
|
-
idOrOptions?: string | SessionIdSymbol | RowQueryOptions<TTableDef, any>,
|
|
64
|
-
options_?: RowQueryOptions<TTableDef, any> & RowQueryOptionsDefaulValues<TTableDef>,
|
|
65
|
-
) => {
|
|
66
|
-
const id = typeof idOrOptions === 'string' || idOrOptions === SessionIdSymbol ? idOrOptions : undefined
|
|
67
|
-
const options = typeof idOrOptions === 'string' || idOrOptions === SessionIdSymbol ? options_ : idOrOptions
|
|
68
|
-
const defaultValues: Partial<RowResult<TTableDef>> | undefined = (options as any)?.defaultValues ?? {}
|
|
69
|
-
|
|
70
|
-
// Validate query args
|
|
71
|
-
if (table.options.isSingleton === true && id !== undefined && id !== SessionIdSymbol) {
|
|
72
|
-
shouldNeverHappen(`Cannot query state table ${table.sqliteDef.name} with id "${id}" as it is a singleton`)
|
|
73
|
-
} else if (table.options.isSingleton !== true && id === undefined) {
|
|
74
|
-
shouldNeverHappen(`Cannot query state table ${table.sqliteDef.name} without id`)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const tableSchema = table.sqliteDef
|
|
78
|
-
const tableName = tableSchema.name
|
|
79
|
-
|
|
80
|
-
const makeQueryString = (id: string | undefined) =>
|
|
81
|
-
sql`select * from ${tableName} ${id === undefined ? '' : `where id = '${id}'`} limit 1`
|
|
82
|
-
|
|
83
|
-
const genQueryString =
|
|
84
|
-
id === SessionIdSymbol
|
|
85
|
-
? (_: GetAtomResult, ctx: QueryContext) => makeQueryString(ctx.store.sessionId)
|
|
86
|
-
: makeQueryString(id)
|
|
87
|
-
|
|
88
|
-
const rowSchema = table.isSingleColumn === true ? table.schema.pipe(Schema.pluck('value' as any)) : table.schema
|
|
89
|
-
|
|
90
|
-
return new LiveStoreSQLQuery({
|
|
91
|
-
label:
|
|
92
|
-
options?.label ??
|
|
93
|
-
`rowQuery:query:${tableSchema.name}${id === undefined ? '' : id === SessionIdSymbol ? `:sessionId` : `:${id}`}`,
|
|
94
|
-
genQueryString,
|
|
95
|
-
queriedTables: new Set([tableName]),
|
|
96
|
-
reactivityGraph: options?.reactivityGraph,
|
|
97
|
-
// While this code-path is not needed for singleton tables, it's still needed for `useRow` with non-existing rows for a given ID
|
|
98
|
-
execBeforeFirstRun: makeExecBeforeFirstRun({
|
|
99
|
-
otelContext: options?.otelContext,
|
|
100
|
-
table,
|
|
101
|
-
defaultValues,
|
|
102
|
-
id,
|
|
103
|
-
skipInsertDefaultRow: options?.skipInsertDefaultRow,
|
|
104
|
-
}),
|
|
105
|
-
schema: rowSchema.pipe(Schema.Array, Schema.headOrElse()),
|
|
106
|
-
map: options?.map,
|
|
107
|
-
queryInfo: { _tag: 'Row', table, id: id ?? 'singleton' },
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export type RowResult<TTableDef extends DbSchema.TableDef> = TTableDef['isSingleColumn'] extends true
|
|
112
|
-
? GetValForKey<SqliteDsl.FromColumns.RowDecoded<TTableDef['sqliteDef']['columns']>, 'value'>
|
|
113
|
-
: SqliteDsl.FromColumns.RowDecoded<TTableDef['sqliteDef']['columns']>
|
|
114
|
-
|
|
115
|
-
export type RowResultEncoded<TTableDef extends DbSchema.TableDef> = TTableDef['isSingleColumn'] extends true
|
|
116
|
-
? GetValForKey<SqliteDsl.FromColumns.RowEncoded<TTableDef['sqliteDef']['columns']>, 'value'>
|
|
117
|
-
: SqliteDsl.FromColumns.RowEncoded<TTableDef['sqliteDef']['columns']>
|
|
118
|
-
|
|
119
|
-
export const deriveColQuery: {
|
|
120
|
-
<TQuery extends LiveQuery<any, QueryInfoNone>, TCol extends keyof TQuery['__result!'] & string>(
|
|
121
|
-
query$: TQuery,
|
|
122
|
-
colName: TCol,
|
|
123
|
-
): LiveQuery<TQuery['__result!'][TCol], QueryInfoNone>
|
|
124
|
-
<TQuery extends LiveQuery<any, QueryInfoRow<any>>, TCol extends keyof TQuery['__result!'] & string>(
|
|
125
|
-
query$: TQuery,
|
|
126
|
-
colName: TCol,
|
|
127
|
-
): LiveQuery<TQuery['__result!'][TCol], QueryInfoCol<TQuery['queryInfo']['table'], TCol>>
|
|
128
|
-
} = (query$: LiveQueryAny, colName: string) => {
|
|
129
|
-
return computed((get) => get(query$)[colName], {
|
|
130
|
-
label: `deriveColQuery:${query$.label}:${colName}`,
|
|
131
|
-
queryInfo:
|
|
132
|
-
query$.queryInfo._tag === 'Row'
|
|
133
|
-
? { _tag: 'Col', table: query$.queryInfo.table, column: colName, id: query$.queryInfo.id }
|
|
134
|
-
: undefined,
|
|
135
|
-
}) as any
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const makeExecBeforeFirstRun =
|
|
139
|
-
({
|
|
140
|
-
id,
|
|
141
|
-
defaultValues,
|
|
142
|
-
skipInsertDefaultRow,
|
|
143
|
-
otelContext: otelContext_,
|
|
144
|
-
table,
|
|
145
|
-
}: {
|
|
146
|
-
id?: string | SessionIdSymbol
|
|
147
|
-
defaultValues?: any
|
|
148
|
-
skipInsertDefaultRow?: boolean
|
|
149
|
-
otelContext?: otel.Context
|
|
150
|
-
table: DbSchema.TableDef
|
|
151
|
-
}) =>
|
|
152
|
-
({ store }: QueryContext) => {
|
|
153
|
-
const otelContext = otelContext_ ?? store.otel.queriesSpanContext
|
|
154
|
-
|
|
155
|
-
if (skipInsertDefaultRow !== true && table.options.isSingleton === false) {
|
|
156
|
-
insertRowWithDefaultValuesOrIgnore({
|
|
157
|
-
store,
|
|
158
|
-
id: id!,
|
|
159
|
-
table,
|
|
160
|
-
otelContext,
|
|
161
|
-
explicitDefaultValues: defaultValues,
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const insertRowWithDefaultValuesOrIgnore = ({
|
|
167
|
-
store,
|
|
168
|
-
id,
|
|
169
|
-
table,
|
|
170
|
-
otelContext,
|
|
171
|
-
explicitDefaultValues,
|
|
172
|
-
}: {
|
|
173
|
-
store: Store
|
|
174
|
-
id: string | SessionIdSymbol
|
|
175
|
-
table: DbSchema.TableDef
|
|
176
|
-
otelContext: otel.Context
|
|
177
|
-
explicitDefaultValues: Partial<RowResult<DbSchema.TableDef>> | undefined
|
|
178
|
-
}) => {
|
|
179
|
-
const idStr = id === SessionIdSymbol ? store.sessionId : id
|
|
180
|
-
const rowExists =
|
|
181
|
-
store.syncDbWrapper.select(`select 1 from ${table.sqliteDef.name} where id = '${idStr}'`).length === 1
|
|
182
|
-
|
|
183
|
-
if (rowExists) return
|
|
184
|
-
|
|
185
|
-
// const mutationDef = deriveCreateMutationDef(table)
|
|
186
|
-
if (DbSchema.tableHasDerivedMutations(table) === false) {
|
|
187
|
-
return shouldNeverHappen(
|
|
188
|
-
`Cannot insert row for table "${table.sqliteDef.name}" which does not have 'deriveMutations: true' set`,
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
// NOTE It's important that we only mutate and don't refresh here, as this function is called during a render
|
|
192
|
-
store.mutateWithoutRefresh(table.insert({ id, ...explicitDefaultValues }), {
|
|
193
|
-
otelContext,
|
|
194
|
-
coordinatorMode: 'default',
|
|
195
|
-
})
|
|
196
|
-
}
|