@livestore/livestore 0.0.25 → 0.0.28
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 +1 -1
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js +50 -60
- package/dist/QueryCache.js.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +21 -6
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +13 -14
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/__tests__/react/useQuery.test.js +5 -5
- package/dist/__tests__/react/useQuery.test.js.map +1 -1
- package/dist/__tests__/react/useRow.test.d.ts +2 -0
- package/dist/__tests__/react/useRow.test.d.ts.map +1 -0
- package/dist/__tests__/react/{useComponentState.test.js → useRow.test.js} +22 -27
- package/dist/__tests__/react/useRow.test.js.map +1 -0
- package/dist/__tests__/react/utils/stack-info.test.js +32 -0
- package/dist/__tests__/react/utils/stack-info.test.js.map +1 -1
- package/dist/__tests__/reactive.test.js +1 -1
- package/dist/__tests__/reactive.test.js.map +1 -1
- package/dist/__tests__/reactiveQueries/sql.test.js +10 -10
- package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +3 -3
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +1 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/global-state.d.ts +19 -0
- package/dist/global-state.d.ts.map +1 -0
- package/dist/global-state.js +20 -0
- package/dist/global-state.js.map +1 -0
- package/dist/inMemoryDatabase.d.ts +3 -3
- package/dist/inMemoryDatabase.d.ts.map +1 -1
- package/dist/inMemoryDatabase.js +14 -7
- package/dist/inMemoryDatabase.js.map +1 -1
- package/dist/index.d.ts +5 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/migrations.d.ts +4 -4
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +34 -28
- package/dist/migrations.js.map +1 -1
- package/dist/react/LiveStoreContext.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +2 -2
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/index.d.ts +1 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/useQuery.d.ts +3 -0
- package/dist/react/useQuery.d.ts.map +1 -1
- package/dist/react/useQuery.js +5 -4
- package/dist/react/useQuery.js.map +1 -1
- package/dist/react/useRow.d.ts +36 -0
- package/dist/react/useRow.d.ts.map +1 -0
- package/dist/react/useRow.js +143 -0
- package/dist/react/useRow.js.map +1 -0
- package/dist/react/useTemporaryQuery.d.ts +2 -0
- package/dist/react/useTemporaryQuery.d.ts.map +1 -1
- package/dist/react/useTemporaryQuery.js +28 -11
- package/dist/react/useTemporaryQuery.js.map +1 -1
- package/dist/react/utils/stack-info.d.ts.map +1 -1
- package/dist/react/utils/stack-info.js +3 -2
- package/dist/react/utils/stack-info.js.map +1 -1
- package/dist/react/utils/useStateRefWithReactiveInput.js.map +1 -1
- package/dist/reactive.d.ts +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +47 -44
- package/dist/reactive.js.map +1 -1
- 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 +10 -12
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/graphql.d.ts +2 -2
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +56 -50
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +1 -2
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js +25 -15
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +3 -3
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +39 -34
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/row-query.d.ts +23 -0
- package/dist/row-query.d.ts.map +1 -0
- package/dist/row-query.js +78 -0
- package/dist/row-query.js.map +1 -0
- package/dist/schema/action.d.ts +30 -0
- package/dist/schema/action.d.ts.map +1 -0
- package/dist/schema/action.js +3 -0
- package/dist/schema/action.js.map +1 -0
- package/dist/schema/index.d.ts +28 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +26 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/system-tables.d.ts +24 -0
- package/dist/schema/system-tables.d.ts.map +1 -0
- package/dist/schema/system-tables.js +11 -0
- package/dist/schema/system-tables.js.map +1 -0
- package/dist/schema/table-def.d.ts +161 -0
- package/dist/schema/table-def.d.ts.map +1 -0
- package/dist/schema/table-def.js +50 -0
- package/dist/schema/table-def.js.map +1 -0
- package/dist/storage/in-memory/index.d.ts +1 -1
- package/dist/storage/in-memory/index.d.ts.map +1 -1
- package/dist/storage/in-memory/index.js +6 -7
- package/dist/storage/in-memory/index.js.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/tauri/index.d.ts +1 -1
- package/dist/storage/tauri/index.d.ts.map +1 -1
- package/dist/storage/tauri/index.js +25 -23
- package/dist/storage/tauri/index.js.map +1 -1
- package/dist/storage/utils/idb.js +3 -1
- package/dist/storage/utils/idb.js.map +1 -1
- package/dist/storage/web-worker/index.d.ts +1 -1
- package/dist/storage/web-worker/index.d.ts.map +1 -1
- package/dist/storage/web-worker/index.js +38 -34
- package/dist/storage/web-worker/index.js.map +1 -1
- package/dist/storage/web-worker/worker.d.ts +1 -1
- package/dist/storage/web-worker/worker.d.ts.map +1 -1
- package/dist/storage/web-worker/worker.js +1 -1
- package/dist/storage/web-worker/worker.js.map +1 -1
- package/dist/store.d.ts +10 -10
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +285 -265
- package/dist/store.js.map +1 -1
- package/dist/utils/bounded-collections.d.ts.map +1 -0
- package/dist/utils/bounded-collections.js +90 -0
- package/dist/utils/bounded-collections.js.map +1 -0
- package/dist/utils/otel.d.ts.map +1 -0
- package/dist/{otel.js → utils/otel.js} +1 -1
- package/dist/utils/otel.js.map +1 -0
- package/dist/utils/util.d.ts.map +1 -0
- package/dist/utils/util.js.map +1 -0
- package/package.json +15 -15
- package/src/QueryCache.ts +2 -2
- package/src/__tests__/react/fixture.tsx +15 -15
- package/src/__tests__/react/useQuery.test.tsx +5 -5
- package/src/__tests__/react/{useComponentState.test.tsx → useRow.test.tsx} +28 -29
- package/src/__tests__/react/utils/stack-info.test.ts +34 -0
- package/src/__tests__/reactive.test.ts +1 -1
- package/src/__tests__/reactiveQueries/sql.test.ts +10 -10
- package/src/effect/LiveStore.ts +6 -6
- package/src/global-state.ts +26 -0
- package/src/inMemoryDatabase.ts +9 -4
- package/src/index.ts +18 -17
- package/src/migrations.ts +41 -35
- package/src/react/LiveStoreProvider.tsx +2 -2
- package/src/react/index.ts +7 -9
- package/src/react/useQuery.ts +10 -4
- package/src/react/useRow.ts +239 -0
- package/src/react/useTemporaryQuery.ts +43 -11
- package/src/react/utils/stack-info.ts +4 -2
- package/src/reactive.ts +1 -1
- package/src/reactiveQueries/base-class.ts +8 -2
- package/src/reactiveQueries/graphql.ts +4 -3
- package/src/reactiveQueries/js.ts +3 -4
- package/src/reactiveQueries/sql.ts +6 -6
- package/src/row-query.ts +148 -0
- package/src/schema/action.ts +41 -0
- package/src/schema/index.ts +63 -0
- package/src/schema/system-tables.ts +21 -0
- package/src/schema/table-def.ts +197 -0
- package/src/storage/in-memory/index.ts +1 -1
- package/src/storage/index.ts +2 -1
- package/src/storage/tauri/index.ts +2 -2
- package/src/storage/web-worker/index.ts +1 -1
- package/src/storage/web-worker/worker.ts +2 -2
- package/src/store.ts +43 -27
- package/dist/__tests__/react/useComponentState.test.d.ts +0 -2
- package/dist/__tests__/react/useComponentState.test.d.ts.map +0 -1
- package/dist/__tests__/react/useComponentState.test.js.map +0 -1
- package/dist/bounded-collections.d.ts.map +0 -1
- package/dist/bounded-collections.js +0 -103
- package/dist/bounded-collections.js.map +0 -1
- package/dist/componentKey.d.ts +0 -20
- package/dist/componentKey.d.ts.map +0 -1
- package/dist/componentKey.js +0 -3
- package/dist/componentKey.js.map +0 -1
- package/dist/otel.d.ts.map +0 -1
- package/dist/otel.js.map +0 -1
- package/dist/react/useComponentState.d.ts +0 -50
- package/dist/react/useComponentState.d.ts.map +0 -1
- package/dist/react/useComponentState.js +0 -226
- package/dist/react/useComponentState.js.map +0 -1
- package/dist/reactiveQueries/graph.d.ts +0 -10
- package/dist/reactiveQueries/graph.d.ts.map +0 -1
- package/dist/reactiveQueries/graph.js +0 -6
- package/dist/reactiveQueries/graph.js.map +0 -1
- package/dist/schema.d.ts +0 -81
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -46
- package/dist/schema.js.map +0 -1
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js.map +0 -1
- package/src/componentKey.ts +0 -9
- package/src/react/useComponentState.ts +0 -385
- package/src/reactiveQueries/graph.ts +0 -15
- package/src/schema.ts +0 -143
- /package/dist/{bounded-collections.d.ts → utils/bounded-collections.d.ts} +0 -0
- /package/dist/{otel.d.ts → utils/otel.d.ts} +0 -0
- /package/dist/{util.d.ts → utils/util.d.ts} +0 -0
- /package/dist/{util.js → utils/util.js} +0 -0
- /package/src/{bounded-collections.ts → utils/bounded-collections.ts} +0 -0
- /package/src/{otel.ts → utils/otel.ts} +0 -0
- /package/src/{util.ts → utils/util.ts} +0 -0
package/src/index.ts
CHANGED
|
@@ -3,33 +3,34 @@ export type { LiveStoreQuery, BaseGraphQLContext, QueryResult, QueryDebugInfo, R
|
|
|
3
3
|
|
|
4
4
|
export type { QueryDefinition, LiveStoreCreateStoreOptions, LiveStoreContext } from './effect/LiveStore.js'
|
|
5
5
|
|
|
6
|
-
export {
|
|
7
|
-
defineComponentStateSchema,
|
|
8
|
-
defineAction,
|
|
9
|
-
defineActions,
|
|
10
|
-
defineTables,
|
|
11
|
-
defineMaterializedViews,
|
|
12
|
-
makeSchema,
|
|
13
|
-
} from './schema.js'
|
|
14
6
|
export { InMemoryDatabase, type DebugInfo, emptyDebugInfo } from './inMemoryDatabase.js'
|
|
7
|
+
|
|
15
8
|
export type { Storage, StorageType, StorageInit } from './storage/index.js'
|
|
9
|
+
|
|
16
10
|
export type { GetAtom, AtomDebugInfo, RefreshDebugInfo, SerializedAtom, Atom } from './reactive.js'
|
|
17
11
|
export { LiveStoreJSQuery, queryJS } from './reactiveQueries/js.js'
|
|
18
12
|
export { LiveStoreSQLQuery, querySQL } from './reactiveQueries/sql.js'
|
|
19
13
|
export { LiveStoreGraphQLQuery, queryGraphQL } from './reactiveQueries/graphql.js'
|
|
20
14
|
export { type GetAtomResult } from './reactiveQueries/base-class.js'
|
|
21
|
-
export { dbGraph } from './reactiveQueries/graph.js'
|
|
22
15
|
|
|
23
|
-
export {
|
|
24
|
-
export type { ComponentKey } from './componentKey.js'
|
|
25
|
-
export type { Schema, GetActionArgs, GetApplyEventArgs, Index, ActionDefinition, ActionDefinitions } from './schema.js'
|
|
16
|
+
export { dbGraph } from './global-state.js'
|
|
26
17
|
|
|
27
|
-
export {
|
|
18
|
+
export { type RowResult, type RowResultEncoded, type RowQueryArgs, rowQuery } from './row-query.js'
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
export type TableDefinition = SqliteAst.Table
|
|
20
|
+
export { defineAction, defineActions, makeSchema, DbSchema } from './schema/index.js'
|
|
31
21
|
|
|
32
|
-
export {
|
|
22
|
+
export type {
|
|
23
|
+
LiveStoreSchema,
|
|
24
|
+
InputSchema,
|
|
25
|
+
GetActionArgs,
|
|
26
|
+
GetApplyEventArgs,
|
|
27
|
+
ActionDefinition,
|
|
28
|
+
ActionDefinitions,
|
|
29
|
+
SQLWriteStatement,
|
|
30
|
+
SchemaMetaRow,
|
|
31
|
+
} from './schema/index.js'
|
|
32
|
+
|
|
33
|
+
export { SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
33
34
|
|
|
34
|
-
export { prepareBindValues, sql, type Bindable, type PreparedBindValues } from './util.js'
|
|
35
|
+
export { prepareBindValues, sql, type Bindable, type PreparedBindValues } from './utils/util.js'
|
|
35
36
|
export { isEqual } from 'lodash-es'
|
package/src/migrations.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { Schema as EffectSchema } from '@livestore/utils/effect'
|
|
1
2
|
import type * as otel from '@opentelemetry/api'
|
|
2
3
|
import { SqliteAst } from 'effect-db-schema'
|
|
3
|
-
import { memoize
|
|
4
|
+
import { memoize } from 'lodash-es'
|
|
4
5
|
|
|
6
|
+
import { dynamicallyRegisteredTables } from './global-state.js'
|
|
5
7
|
import type { InMemoryDatabase } from './index.js'
|
|
6
|
-
import type {
|
|
7
|
-
import {
|
|
8
|
-
import type { PreparedBindValues } from './util.js'
|
|
9
|
-
import { sql } from './util.js'
|
|
8
|
+
import type { LiveStoreSchema, SchemaMetaRow } from './schema/index.js'
|
|
9
|
+
import { SCHEMA_META_TABLE, systemTables } from './schema/index.js'
|
|
10
|
+
import type { PreparedBindValues } from './utils/util.js'
|
|
11
|
+
import { sql } from './utils/util.js'
|
|
10
12
|
|
|
11
13
|
const getMemoizedTimestamp = memoize(() => new Date().toISOString())
|
|
12
14
|
|
|
@@ -18,7 +20,7 @@ export const migrateDb = ({
|
|
|
18
20
|
}: {
|
|
19
21
|
db: InMemoryDatabase
|
|
20
22
|
otelContext: otel.Context
|
|
21
|
-
schema:
|
|
23
|
+
schema: LiveStoreSchema
|
|
22
24
|
}) => {
|
|
23
25
|
db.execute(
|
|
24
26
|
// TODO use schema migration definition from schema.ts instead
|
|
@@ -34,16 +36,18 @@ export const migrateDb = ({
|
|
|
34
36
|
schemaMetaRows.map(({ tableName, schemaHash }) => [tableName, schemaHash]),
|
|
35
37
|
)
|
|
36
38
|
|
|
37
|
-
const tableDefs =
|
|
39
|
+
const tableDefs = new Set([
|
|
38
40
|
// NOTE it's important the `SCHEMA_META_TABLE` comes first since we're writing to it below
|
|
39
|
-
|
|
40
|
-
...
|
|
41
|
-
...
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
for (const
|
|
41
|
+
...systemTables,
|
|
42
|
+
...Array.from(schema.tables.values()).filter((_) => _.schema.name !== SCHEMA_META_TABLE),
|
|
43
|
+
...dynamicallyRegisteredTables.values(),
|
|
44
|
+
])
|
|
45
|
+
|
|
46
|
+
for (const tableDef of tableDefs) {
|
|
47
|
+
const tableAst = tableDef.schema.ast
|
|
48
|
+
const tableName = tableAst.name
|
|
45
49
|
const dbSchemaHash = dbSchemaHashByTable[tableName]
|
|
46
|
-
const schemaHash = SqliteAst.hash(
|
|
50
|
+
const schemaHash = SqliteAst.hash(tableAst)
|
|
47
51
|
if (schemaHash !== dbSchemaHash) {
|
|
48
52
|
if (import.meta.env.VITE_LIVESTORE_SKIP_MIGRATIONS) {
|
|
49
53
|
console.log(
|
|
@@ -54,7 +58,7 @@ export const migrateDb = ({
|
|
|
54
58
|
`Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
|
|
55
59
|
)
|
|
56
60
|
|
|
57
|
-
migrateTable({ db,
|
|
61
|
+
migrateTable({ db, tableAst, otelContext, schemaHash })
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
@@ -62,24 +66,24 @@ export const migrateDb = ({
|
|
|
62
66
|
|
|
63
67
|
export const migrateTable = ({
|
|
64
68
|
db,
|
|
65
|
-
|
|
69
|
+
tableAst,
|
|
66
70
|
otelContext,
|
|
67
71
|
schemaHash,
|
|
68
72
|
}: {
|
|
69
73
|
db: InMemoryDatabase
|
|
70
|
-
|
|
74
|
+
tableAst: SqliteAst.Table
|
|
71
75
|
otelContext: otel.Context
|
|
72
76
|
schemaHash: number
|
|
73
77
|
}) => {
|
|
74
|
-
console.log(`Migrating table '${
|
|
75
|
-
const tableName =
|
|
76
|
-
const columnSpec = makeColumnSpec(
|
|
78
|
+
console.log(`Migrating table '${tableAst.name}'...`)
|
|
79
|
+
const tableName = tableAst.name
|
|
80
|
+
const columnSpec = makeColumnSpec(tableAst)
|
|
77
81
|
|
|
78
82
|
// TODO need to possibly handle cascading deletes due to foreign keys
|
|
79
83
|
db.execute(sql`drop table if exists ${tableName}`, undefined, [], { otelContext })
|
|
80
84
|
db.execute(sql`create table if not exists ${tableName} (${columnSpec});`, undefined, [], { otelContext })
|
|
81
85
|
|
|
82
|
-
for (const index of
|
|
86
|
+
for (const index of tableAst.indexes) {
|
|
83
87
|
db.execute(createIndexFromDefinition(tableName, index), undefined, [], { otelContext })
|
|
84
88
|
}
|
|
85
89
|
|
|
@@ -100,9 +104,9 @@ const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) =>
|
|
|
100
104
|
return sql`create ${uniqueStr} index ${index.name} on ${tableName} (${index.columns.join(', ')})`
|
|
101
105
|
}
|
|
102
106
|
|
|
103
|
-
const makeColumnSpec = (
|
|
104
|
-
const primaryKeys =
|
|
105
|
-
const columnDefStrs =
|
|
107
|
+
const makeColumnSpec = (tableAst: SqliteAst.Table) => {
|
|
108
|
+
const primaryKeys = tableAst.columns.filter((_) => _.primaryKey).map((_) => _.name)
|
|
109
|
+
const columnDefStrs = tableAst.columns.map(toSqliteColumnSpec)
|
|
106
110
|
if (primaryKeys.length > 0) {
|
|
107
111
|
columnDefStrs.push(`PRIMARY KEY (${primaryKeys.join(', ')})`)
|
|
108
112
|
}
|
|
@@ -110,16 +114,18 @@ const makeColumnSpec = (tableDef: SqliteAst.Table) => {
|
|
|
110
114
|
return columnDefStrs.join(', ')
|
|
111
115
|
}
|
|
112
116
|
|
|
117
|
+
/** NOTE primary keys are applied on a table level not on a column level to account for multi-column primary keys */
|
|
113
118
|
const toSqliteColumnSpec = (column: SqliteAst.Column) => {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
const columnTypeStr = column.type._tag
|
|
120
|
+
const nullableStr = column.nullable === false ? 'not null' : ''
|
|
121
|
+
const defaultValueStr = (() => {
|
|
122
|
+
if (column.default === undefined) return ''
|
|
123
|
+
|
|
124
|
+
const encodeValue = EffectSchema.encodeSync(column.codec)
|
|
125
|
+
const encodedDefaultValue = encodeValue(column.default ?? null)
|
|
126
|
+
|
|
127
|
+
return columnTypeStr === 'text' ? `default '${encodedDefaultValue}'` : `default ${encodedDefaultValue}`
|
|
128
|
+
})()
|
|
129
|
+
|
|
130
|
+
return `${column.name} ${columnTypeStr} ${nullableStr} ${defaultValueStr}`
|
|
125
131
|
}
|
|
@@ -6,7 +6,7 @@ import initSqlite3Wasm from 'sqlite-esm'
|
|
|
6
6
|
// TODO refactor so the `react` module doesn't depend on `effect` module
|
|
7
7
|
import type { LiveStoreContext as StoreContext_, LiveStoreCreateStoreOptions } from '../effect/LiveStore.js'
|
|
8
8
|
import type { InMemoryDatabase } from '../inMemoryDatabase.js'
|
|
9
|
-
import type {
|
|
9
|
+
import type { LiveStoreSchema } from '../schema/index.js'
|
|
10
10
|
import type { StorageInit } from '../storage/index.js'
|
|
11
11
|
import type { BaseGraphQLContext, GraphQLOptions } from '../store.js'
|
|
12
12
|
import { createStore } from '../store.js'
|
|
@@ -20,7 +20,7 @@ const sqlite3Promise = initSqlite3Wasm({
|
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
interface LiveStoreProviderProps<GraphQLContext> {
|
|
23
|
-
schema:
|
|
23
|
+
schema: LiveStoreSchema
|
|
24
24
|
loadStorage: () => StorageInit | Promise<StorageInit>
|
|
25
25
|
boot?: (db: InMemoryDatabase, parentSpan: otel.Span) => unknown | Promise<unknown>
|
|
26
26
|
graphQLOptions?: GraphQLOptions<GraphQLContext>
|
package/src/react/index.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
Setters,
|
|
3
|
-
ComponentKeyConfig,
|
|
4
|
-
QueryDefinitions,
|
|
5
|
-
ComponentColumns,
|
|
6
|
-
GetStateType,
|
|
7
|
-
GetStateTypeEncoded,
|
|
8
|
-
} from './useComponentState.js'
|
|
9
1
|
export { LiveStoreContext, useStore } from './LiveStoreContext.js'
|
|
10
2
|
export { LiveStoreProvider } from './LiveStoreProvider.js'
|
|
11
|
-
export { useComponentState } from './useComponentState.js'
|
|
12
3
|
export { useQuery } from './useQuery.js'
|
|
13
4
|
export { useTemporaryQuery } from './useTemporaryQuery.js'
|
|
14
5
|
export { useStackInfo } from './utils/stack-info.js'
|
|
6
|
+
export {
|
|
7
|
+
useRow,
|
|
8
|
+
type StateSetters,
|
|
9
|
+
type SetStateAction,
|
|
10
|
+
type Dispatch,
|
|
11
|
+
type UseRowResult as UseStateResult,
|
|
12
|
+
} from './useRow.js'
|
|
15
13
|
|
|
16
14
|
// Needed to make TS happy
|
|
17
15
|
export type { TypedDocumentNode } from '@graphql-typed-document-node/core'
|
package/src/react/useQuery.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { ILiveStoreQuery } 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'
|
|
9
|
+
|
|
9
10
|
/**
|
|
10
11
|
* This is needed because the `React.useMemo` call below, can sometimes be called multiple times 🤷,
|
|
11
12
|
* so we need to "cache" the fact that we've already started a span for this component.
|
|
@@ -13,7 +14,12 @@ import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInp
|
|
|
13
14
|
*/
|
|
14
15
|
const spanAlreadyStartedCache = new Map<ILiveStoreQuery<any>, { span: otel.Span; otelContext: otel.Context }>()
|
|
15
16
|
|
|
16
|
-
export const useQuery = <TResult>(query: ILiveStoreQuery<TResult>): TResult =>
|
|
17
|
+
export const useQuery = <TResult>(query: ILiveStoreQuery<TResult>): TResult => useQueryRef(query).current
|
|
18
|
+
|
|
19
|
+
export const useQueryRef = <TResult>(
|
|
20
|
+
query: ILiveStoreQuery<TResult>,
|
|
21
|
+
parentOtelContext?: otel.Context,
|
|
22
|
+
): React.MutableRefObject<TResult> => {
|
|
17
23
|
const { store } = useStore()
|
|
18
24
|
|
|
19
25
|
const stackInfo = React.useMemo(() => {
|
|
@@ -32,7 +38,7 @@ export const useQuery = <TResult>(query: ILiveStoreQuery<TResult>): TResult => {
|
|
|
32
38
|
const span = store.otel.tracer.startSpan(
|
|
33
39
|
`LiveStore:useQuery:${query.label}`,
|
|
34
40
|
{ attributes: { label: query.label, stackInfo: JSON.stringify(stackInfo) } },
|
|
35
|
-
store.otel.queriesSpanContext,
|
|
41
|
+
parentOtelContext ?? store.otel.queriesSpanContext,
|
|
36
42
|
)
|
|
37
43
|
|
|
38
44
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
@@ -40,7 +46,7 @@ export const useQuery = <TResult>(query: ILiveStoreQuery<TResult>): TResult => {
|
|
|
40
46
|
spanAlreadyStartedCache.set(query, { span, otelContext })
|
|
41
47
|
|
|
42
48
|
return { span, otelContext }
|
|
43
|
-
}, [query, stackInfo, store.otel.queriesSpanContext, store.otel.tracer])
|
|
49
|
+
}, [parentOtelContext, query, stackInfo, store.otel.queriesSpanContext, store.otel.tracer])
|
|
44
50
|
|
|
45
51
|
const initialResult = React.useMemo(
|
|
46
52
|
() =>
|
|
@@ -86,5 +92,5 @@ export const useQuery = <TResult>(query: ILiveStoreQuery<TResult>): TResult => {
|
|
|
86
92
|
}
|
|
87
93
|
}, [stackInfo, query, setValue, store, valueRef, otelContext, span])
|
|
88
94
|
|
|
89
|
-
return valueRef
|
|
95
|
+
return valueRef
|
|
90
96
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Schema } from '@livestore/utils/effect'
|
|
2
|
+
import * as otel from '@opentelemetry/api'
|
|
3
|
+
import type { SqliteDsl } from 'effect-db-schema'
|
|
4
|
+
import { mapValues } from 'lodash-es'
|
|
5
|
+
import React from 'react'
|
|
6
|
+
|
|
7
|
+
import type { LiveStoreJSQuery } from '../reactiveQueries/js.js'
|
|
8
|
+
import type { RowQueryArgs, RowResult } from '../row-query.js'
|
|
9
|
+
import { rowQuery } from '../row-query.js'
|
|
10
|
+
import type { DefaultSqliteTableDef, TableDef, TableOptions } from '../schema/table-def.js'
|
|
11
|
+
import { useStore } from './LiveStoreContext.js'
|
|
12
|
+
import { useQueryRef } from './useQuery.js'
|
|
13
|
+
|
|
14
|
+
export type UseRowResult<TTableDef extends TableDef> = [
|
|
15
|
+
row: RowResult<TTableDef>,
|
|
16
|
+
setRow: StateSetters<TTableDef>,
|
|
17
|
+
query$: LiveStoreJSQuery<RowResult<TTableDef>>,
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
export type UseRowOptions<TTableDef extends TableDef> = {
|
|
21
|
+
defaultValues?: Partial<RowResult<TTableDef>>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Similar to `React.useState` but returns a tuple of `[row, setRow, query$]` for a given table where ...
|
|
26
|
+
*
|
|
27
|
+
* - `row` is the current value of the row (fully decoded according to the table schema)
|
|
28
|
+
* - `setRow` is a function that can be used to update the row (values will be encoded according to the table schema)
|
|
29
|
+
* - `query$` is a `LiveStoreJSQuery` that e.g. can be used to subscribe to changes to the row
|
|
30
|
+
*
|
|
31
|
+
* If the table is a singleton table, `useRow` can be called without an `id` argument. Otherwise, the `id` argument is required.
|
|
32
|
+
*/
|
|
33
|
+
export const useRow: {
|
|
34
|
+
<TTableDef extends TableDef<DefaultSqliteTableDef, boolean, TableOptions & { isSingleton: true }>>(
|
|
35
|
+
table: TTableDef,
|
|
36
|
+
): UseRowResult<TTableDef>
|
|
37
|
+
<TTableDef extends TableDef<DefaultSqliteTableDef, boolean, TableOptions & { isSingleton: false }>>(
|
|
38
|
+
table: TTableDef,
|
|
39
|
+
// TODO adjust so it works with arbitrary primary keys or unique constraints
|
|
40
|
+
id: string,
|
|
41
|
+
options?: UseRowOptions<TTableDef>,
|
|
42
|
+
): UseRowResult<TTableDef>
|
|
43
|
+
} = <TTableDef extends TableDef>(
|
|
44
|
+
table: TTableDef,
|
|
45
|
+
id?: string,
|
|
46
|
+
options?: UseRowOptions<TTableDef>,
|
|
47
|
+
): UseRowResult<TTableDef> => {
|
|
48
|
+
const sqliteTableDef = table.schema
|
|
49
|
+
const { defaultValues } = options ?? {}
|
|
50
|
+
type TComponentState = SqliteDsl.FromColumns.RowDecoded<TTableDef['schema']['columns']>
|
|
51
|
+
|
|
52
|
+
const { store } = useStore()
|
|
53
|
+
|
|
54
|
+
const reactId = React.useId()
|
|
55
|
+
|
|
56
|
+
const { query$, otelContext } = React.useMemo(() => {
|
|
57
|
+
const cachedItem = rcCache.get(table, id ?? 'singleton')
|
|
58
|
+
if (cachedItem !== undefined) {
|
|
59
|
+
cachedItem.reactIds.add(reactId)
|
|
60
|
+
cachedItem.span.addEvent('new-subscriber', { reactId })
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
query$: cachedItem.query$ as LiveStoreJSQuery<RowResult<TTableDef>>,
|
|
64
|
+
otelContext: cachedItem.otelContext,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const span = store.otel.tracer.startSpan(
|
|
69
|
+
`LiveStore:useState:${table.schema.name}${id === undefined ? '' : `:${id}`}`,
|
|
70
|
+
{ attributes: { id } },
|
|
71
|
+
store.otel.queriesSpanContext,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
75
|
+
|
|
76
|
+
const query$ = table.options.isSingleton
|
|
77
|
+
? rowQuery({ table, store, otelContext, defaultValues } as RowQueryArgs<TTableDef>)
|
|
78
|
+
: rowQuery({ table, store, id, otelContext, defaultValues } as RowQueryArgs<TTableDef>)
|
|
79
|
+
|
|
80
|
+
rcCache.set(table, id ?? 'singleton', query$, reactId, otelContext, span)
|
|
81
|
+
|
|
82
|
+
return { query$, otelContext }
|
|
83
|
+
}, [table, id, reactId, store, defaultValues])
|
|
84
|
+
|
|
85
|
+
React.useEffect(
|
|
86
|
+
() => () => {
|
|
87
|
+
const cachedItem = rcCache.get(table, id ?? 'singleton')!
|
|
88
|
+
|
|
89
|
+
cachedItem.reactIds.delete(reactId)
|
|
90
|
+
if (cachedItem.reactIds.size === 0) {
|
|
91
|
+
rcCache.delete(cachedItem.query$)
|
|
92
|
+
cachedItem.query$.destroy()
|
|
93
|
+
cachedItem.span.end()
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[table, id, reactId],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const query$Ref = useQueryRef(query$, otelContext)
|
|
100
|
+
|
|
101
|
+
const setState = React.useMemo<StateSetters<TTableDef>>(() => {
|
|
102
|
+
if (table.isSingleColumn) {
|
|
103
|
+
return (newValueOrFn: RowResult<TTableDef>) => {
|
|
104
|
+
const newValue = typeof newValueOrFn === 'function' ? newValueOrFn(query$Ref.current) : newValueOrFn
|
|
105
|
+
if (query$Ref.current === newValue) return
|
|
106
|
+
|
|
107
|
+
const encodedValue = Schema.encodeSync(sqliteTableDef.columns['value']!.type.codec)(newValue)
|
|
108
|
+
|
|
109
|
+
store.applyEvent('livestore.UpdateComponentState', {
|
|
110
|
+
tableName: sqliteTableDef.name,
|
|
111
|
+
columnNames: ['value'],
|
|
112
|
+
id,
|
|
113
|
+
bindValues: { ['value']: encodedValue },
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
const setState = // TODO: do we have a better type for the values that can go in SQLite?
|
|
118
|
+
mapValues(sqliteTableDef.columns, (column, columnName) => (newValueOrFn: any) => {
|
|
119
|
+
const newValue =
|
|
120
|
+
// @ts-expect-error TODO fix typing
|
|
121
|
+
typeof newValueOrFn === 'function' ? newValueOrFn(query$Ref.current[columnName]) : newValueOrFn
|
|
122
|
+
|
|
123
|
+
// Don't update the state if it's the same as the value already seen in the component
|
|
124
|
+
// @ts-expect-error TODO fix typing
|
|
125
|
+
if (query$Ref.current[columnName] === newValue) return
|
|
126
|
+
|
|
127
|
+
const encodedValue = Schema.encodeSync(column.type.codec)(newValue)
|
|
128
|
+
|
|
129
|
+
store.applyEvent('livestore.UpdateComponentState', {
|
|
130
|
+
tableName: sqliteTableDef.name,
|
|
131
|
+
columnNames: [columnName],
|
|
132
|
+
id,
|
|
133
|
+
bindValues: { [columnName]: encodedValue },
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
setState.setMany = (columnValuesOrFn: Partial<TComponentState>) => {
|
|
138
|
+
const columnValues =
|
|
139
|
+
// @ts-expect-error TODO fix typing
|
|
140
|
+
typeof columnValuesOrFn === 'function' ? columnValuesOrFn(query$Ref.current) : columnValuesOrFn
|
|
141
|
+
|
|
142
|
+
// TODO use hashing instead
|
|
143
|
+
// Don't update the state if it's the same as the value already seen in the component
|
|
144
|
+
if (
|
|
145
|
+
// @ts-expect-error TODO fix typing
|
|
146
|
+
Object.entries(columnValues).every(([columnName, value]) => query$Ref.current[columnName] === value)
|
|
147
|
+
) {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const columnNames = Object.keys(columnValues)
|
|
152
|
+
const bindValues = mapValues(columnValues, (value, columnName) =>
|
|
153
|
+
Schema.encodeSync(sqliteTableDef.columns[columnName]!.type.codec)(value),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
store.applyEvent('livestore.UpdateComponentState', {
|
|
157
|
+
tableName: sqliteTableDef.name,
|
|
158
|
+
columnNames,
|
|
159
|
+
id,
|
|
160
|
+
bindValues,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return setState as any
|
|
165
|
+
}
|
|
166
|
+
}, [table.isSingleColumn, id, sqliteTableDef.columns, sqliteTableDef.name, store, query$Ref])
|
|
167
|
+
|
|
168
|
+
return [query$Ref.current, setState, query$]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type Dispatch<A> = (action: A) => void
|
|
172
|
+
export type SetStateAction<S> = S | ((previousValue: S) => S)
|
|
173
|
+
|
|
174
|
+
export type StateSetters<TTableDef extends TableDef> = TTableDef['isSingleColumn'] extends true
|
|
175
|
+
? Dispatch<SetStateAction<RowResult<TTableDef>>>
|
|
176
|
+
: {
|
|
177
|
+
[K in keyof RowResult<TTableDef>]: Dispatch<SetStateAction<RowResult<TTableDef>[K]>>
|
|
178
|
+
} & {
|
|
179
|
+
setMany: Dispatch<SetStateAction<Partial<RowResult<TTableDef>>>>
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Reference counted cache for `query$` and otel context */
|
|
183
|
+
class RCCache {
|
|
184
|
+
private readonly cache = new Map<
|
|
185
|
+
TableDef,
|
|
186
|
+
Map<
|
|
187
|
+
string,
|
|
188
|
+
{
|
|
189
|
+
reactIds: Set<string>
|
|
190
|
+
span: otel.Span
|
|
191
|
+
otelContext: otel.Context
|
|
192
|
+
query$: LiveStoreJSQuery<any>
|
|
193
|
+
}
|
|
194
|
+
>
|
|
195
|
+
>()
|
|
196
|
+
private reverseCache = new Map<LiveStoreJSQuery<any>, [TableDef, string]>()
|
|
197
|
+
|
|
198
|
+
get = (table: TableDef, id: string) => {
|
|
199
|
+
const queries = this.cache.get(table)
|
|
200
|
+
if (queries === undefined) return undefined
|
|
201
|
+
return queries.get(id)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
set = (
|
|
205
|
+
table: TableDef,
|
|
206
|
+
id: string,
|
|
207
|
+
query$: LiveStoreJSQuery<any>,
|
|
208
|
+
reactId: string,
|
|
209
|
+
otelContext: otel.Context,
|
|
210
|
+
span: otel.Span,
|
|
211
|
+
) => {
|
|
212
|
+
let queries = this.cache.get(table)
|
|
213
|
+
if (queries === undefined) {
|
|
214
|
+
queries = new Map()
|
|
215
|
+
this.cache.set(table, queries)
|
|
216
|
+
}
|
|
217
|
+
queries.set(id, { query$, otelContext, span, reactIds: new Set([reactId]) })
|
|
218
|
+
this.reverseCache.set(query$, [table, id])
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
delete = (query$: LiveStoreJSQuery<any>) => {
|
|
222
|
+
const item = this.reverseCache.get(query$)
|
|
223
|
+
if (item === undefined) return
|
|
224
|
+
|
|
225
|
+
const [table, id] = item
|
|
226
|
+
const queries = this.cache.get(table)
|
|
227
|
+
if (queries === undefined) return
|
|
228
|
+
|
|
229
|
+
queries.delete(id)
|
|
230
|
+
|
|
231
|
+
if (queries.size === 0) {
|
|
232
|
+
this.cache.delete(table)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.reverseCache.delete(query$)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const rcCache = new RCCache()
|
|
@@ -1,23 +1,55 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import type { ILiveStoreQuery } from '../reactiveQueries/base-class.js'
|
|
4
|
-
import {
|
|
4
|
+
import { useQueryRef } from './useQuery.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This is needed because the `React.useMemo` call below, can sometimes be called multiple times 🤷.
|
|
8
|
+
* The map entry is being removed again in the `React.useEffect` call below.
|
|
9
|
+
*/
|
|
10
|
+
const queryCache = new Map<() => ILiveStoreQuery<any>, { reactIds: Set<string>; query$: ILiveStoreQuery<any> }>()
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* Creates a query, subscribes and destroys it when the component unmounts.
|
|
8
14
|
*
|
|
9
15
|
* Make sure `makeQuery` is a memoized function.
|
|
10
16
|
*/
|
|
11
|
-
export const useTemporaryQuery = <TResult>(makeQuery: () => ILiveStoreQuery<TResult>): TResult =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
export const useTemporaryQuery = <TResult>(makeQuery: () => ILiveStoreQuery<TResult>): TResult =>
|
|
18
|
+
useTemporaryQueryRef(makeQuery).current
|
|
19
|
+
|
|
20
|
+
export const useTemporaryQueryRef = <TResult>(
|
|
21
|
+
makeQuery: () => ILiveStoreQuery<TResult>,
|
|
22
|
+
): React.MutableRefObject<TResult> => {
|
|
23
|
+
const reactId = React.useId()
|
|
24
|
+
|
|
25
|
+
const query$ = React.useMemo(() => {
|
|
26
|
+
const cachedItem = queryCache.get(makeQuery)
|
|
27
|
+
if (cachedItem !== undefined) {
|
|
28
|
+
cachedItem.reactIds.add(reactId)
|
|
29
|
+
|
|
30
|
+
return cachedItem.query$
|
|
19
31
|
}
|
|
20
|
-
}, [query])
|
|
21
32
|
|
|
22
|
-
|
|
33
|
+
const query$ = makeQuery()
|
|
34
|
+
|
|
35
|
+
queryCache.set(makeQuery, { reactIds: new Set([reactId]), query$ })
|
|
36
|
+
|
|
37
|
+
return query$
|
|
38
|
+
}, [reactId, makeQuery])
|
|
39
|
+
|
|
40
|
+
React.useEffect(
|
|
41
|
+
() => () => {
|
|
42
|
+
const cachedItem = queryCache.get(makeQuery)!
|
|
43
|
+
|
|
44
|
+
cachedItem.reactIds.delete(reactId)
|
|
45
|
+
|
|
46
|
+
if (cachedItem.reactIds.size === 0) {
|
|
47
|
+
cachedItem.query$.destroy()
|
|
48
|
+
queryCache.delete(makeQuery)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[makeQuery, reactId],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return useQueryRef(query$)
|
|
23
55
|
}
|
|
@@ -38,10 +38,12 @@ export const extractStackInfoFromStackTrace = (stackTrace: string): StackInfo =>
|
|
|
38
38
|
|
|
39
39
|
while ((match = namePattern.exec(stackTrace)) !== null) {
|
|
40
40
|
const [, name, filePath] = match as any as [string, string, string]
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
// NOTE No idea where this `Module.` comes from - possibly a Vite thing?
|
|
43
|
+
if ((name.startsWith('use') || name.startsWith('Module.use')) && name.endsWith('QueryRef') === false) {
|
|
42
44
|
hasReachedStart = true
|
|
43
45
|
|
|
44
|
-
frames.unshift({ name, filePath })
|
|
46
|
+
frames.unshift({ name: name.replace(/^Module\./, ''), filePath })
|
|
45
47
|
} else if (hasReachedStart) {
|
|
46
48
|
// We've reached the end of the `use*` functions, so we're adding the component name and stop
|
|
47
49
|
frames.unshift({ name, filePath })
|
package/src/reactive.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { pick } from '@livestore/utils'
|
|
|
28
28
|
import type * as otel from '@opentelemetry/api'
|
|
29
29
|
import { isEqual, uniqueId } from 'lodash-es'
|
|
30
30
|
|
|
31
|
-
import { BoundArray } from './bounded-collections.js'
|
|
31
|
+
import { BoundArray } from './utils/bounded-collections.js'
|
|
32
32
|
// import { getDurationMsFromSpan } from './otel.js'
|
|
33
33
|
|
|
34
34
|
export const NOT_REFRESHED_YET = Symbol.for('NOT_REFRESHED_YET')
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import type * as otel from '@opentelemetry/api'
|
|
2
2
|
|
|
3
|
+
import { dbGraph } from '../global-state.js'
|
|
3
4
|
import type { StackInfo } from '../react/utils/stack-info.js'
|
|
4
5
|
import { type Atom, type GetAtom, throwContextNotSetError, type Thunk } from '../reactive.js'
|
|
5
|
-
import type { RefreshReason } from '../store.js'
|
|
6
|
-
import { type DbContext, dbGraph } from './graph.js'
|
|
6
|
+
import type { RefreshReason, Store } from '../store.js'
|
|
7
7
|
import type { LiveStoreJSQuery } from './js.js'
|
|
8
8
|
|
|
9
|
+
export type DbContext = {
|
|
10
|
+
store: Store
|
|
11
|
+
otelTracer: otel.Tracer
|
|
12
|
+
rootOtelContext: otel.Context
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export type UnsubscribeQuery = () => void
|
|
10
16
|
|
|
11
17
|
let queryIdCounter = 0
|
|
@@ -3,11 +3,12 @@ import { assertNever, shouldNeverHappen } from '@livestore/utils'
|
|
|
3
3
|
import * as otel from '@opentelemetry/api'
|
|
4
4
|
import * as graphql from 'graphql'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { dbGraph } from '../global-state.js'
|
|
7
7
|
import type { Thunk } from '../reactive.js'
|
|
8
8
|
import type { BaseGraphQLContext, RefreshReason, Store } from '../store.js'
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
10
|
+
import type { DbContext, GetAtomResult } from './base-class.js'
|
|
11
|
+
import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
11
12
|
import { LiveStoreJSQuery } from './js.js'
|
|
12
13
|
|
|
13
14
|
export const queryGraphQL = <TResult extends Record<string, any>, TVariableValues extends Record<string, any>>(
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as otel from '@opentelemetry/api'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { dbGraph } from '../global-state.js'
|
|
4
4
|
import type { Thunk } from '../reactive.js'
|
|
5
5
|
import type { RefreshReason } from '../store.js'
|
|
6
|
-
import {
|
|
7
|
-
import type
|
|
8
|
-
import { dbGraph } from './graph.js'
|
|
6
|
+
import { getDurationMsFromSpan } from '../utils/otel.js'
|
|
7
|
+
import { type DbContext, type GetAtomResult, LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
|
|
9
8
|
|
|
10
9
|
export const queryJS = <TResult>(fn: (get: GetAtomResult) => TResult, options: { label: string }) =>
|
|
11
10
|
new LiveStoreJSQuery<TResult>({ fn, label: options.label })
|