@livestore/livestore 0.0.13 → 0.0.14
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 +18 -21
- package/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.d.ts +1 -1
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +5 -4
- 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/useComponentState.test.d.ts +2 -0
- package/dist/__tests__/react/useComponentState.test.d.ts.map +1 -0
- package/dist/__tests__/react/useComponentState.test.js +68 -0
- package/dist/__tests__/react/useComponentState.test.js.map +1 -0
- package/dist/__tests__/react/useLQuery.test.d.ts +2 -0
- package/dist/__tests__/react/useLQuery.test.d.ts.map +1 -0
- package/dist/__tests__/react/useLQuery.test.js +38 -0
- package/dist/__tests__/react/useLQuery.test.js.map +1 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js +4 -9
- package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -1
- package/dist/__tests__/react/useQuery.test.d.ts +2 -0
- package/dist/__tests__/react/useQuery.test.d.ts.map +1 -0
- package/dist/__tests__/react/useQuery.test.js +33 -0
- package/dist/__tests__/react/useQuery.test.js.map +1 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +2 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +1 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +38 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +1 -0
- package/dist/__tests__/reactive.test.js +168 -95
- package/dist/__tests__/reactive.test.js.map +1 -1
- package/dist/__tests__/reactiveQueries/sql.test.d.ts +2 -0
- package/dist/__tests__/reactiveQueries/sql.test.d.ts.map +1 -0
- package/dist/__tests__/reactiveQueries/sql.test.js +337 -0
- package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -0
- package/dist/effect/LiveStore.d.ts +3 -9
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +11 -7
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/inMemoryDatabase.d.ts +17 -21
- package/dist/inMemoryDatabase.d.ts.map +1 -1
- package/dist/inMemoryDatabase.js +2 -9
- package/dist/inMemoryDatabase.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +18 -13
- package/dist/migrations.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +1 -3
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +13 -10
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/useComponentState.d.ts +50 -0
- package/dist/react/useComponentState.d.ts.map +1 -0
- package/dist/react/useComponentState.js +248 -0
- package/dist/react/useComponentState.js.map +1 -0
- package/dist/react/useGlobalQuery.d.ts +2 -2
- package/dist/react/useGlobalQuery.d.ts.map +1 -1
- package/dist/react/useGlobalQuery.js +5 -2
- package/dist/react/useGlobalQuery.js.map +1 -1
- package/dist/react/useGraphQL.d.ts +5 -3
- package/dist/react/useGraphQL.d.ts.map +1 -1
- package/dist/react/useGraphQL.js +27 -7
- package/dist/react/useGraphQL.js.map +1 -1
- package/dist/react/useLiveStoreComponent.d.ts +14 -14
- package/dist/react/useLiveStoreComponent.d.ts.map +1 -1
- package/dist/react/useLiveStoreComponent.js +151 -91
- package/dist/react/useLiveStoreComponent.js.map +1 -1
- package/dist/react/useQuery.d.ts +3 -0
- package/dist/react/useQuery.d.ts.map +1 -0
- package/dist/react/useQuery.js +42 -0
- package/dist/react/useQuery.js.map +1 -0
- package/dist/react/useTemporaryQuery.d.ts +8 -0
- package/dist/react/useTemporaryQuery.d.ts.map +1 -0
- package/dist/react/useTemporaryQuery.js +17 -0
- package/dist/react/useTemporaryQuery.js.map +1 -0
- package/dist/react/utils/extractNamesFromStackTrace.d.ts +3 -0
- package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +1 -0
- package/dist/react/utils/extractNamesFromStackTrace.js +40 -0
- package/dist/react/utils/extractNamesFromStackTrace.js.map +1 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +7 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +1 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.js +40 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.js.map +1 -0
- package/dist/reactive.d.ts +42 -48
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +293 -186
- package/dist/reactive.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +28 -20
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +25 -17
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/graph.d.ts +10 -0
- package/dist/reactiveQueries/graph.d.ts.map +1 -0
- package/dist/reactiveQueries/graph.js +6 -0
- package/dist/reactiveQueries/graph.js.map +1 -0
- package/dist/reactiveQueries/graphql.d.ts +35 -18
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +91 -10
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +17 -13
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js +31 -8
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +22 -18
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +81 -16
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/schema.d.ts +0 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +3 -6
- package/dist/schema.js.map +1 -1
- package/dist/storage/in-memory/index.d.ts +2 -2
- package/dist/storage/in-memory/index.d.ts.map +1 -1
- package/dist/storage/in-memory/index.js.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/tauri/index.d.ts +2 -2
- package/dist/storage/tauri/index.d.ts.map +1 -1
- package/dist/storage/tauri/index.js.map +1 -1
- package/dist/storage/web-worker/index.d.ts +4 -4
- package/dist/storage/web-worker/index.d.ts.map +1 -1
- package/dist/storage/web-worker/index.js +3 -5
- package/dist/storage/web-worker/index.js.map +1 -1
- package/dist/storage/web-worker/worker.js +2 -2
- package/dist/storage/web-worker/worker.js.map +1 -1
- package/dist/store.d.ts +19 -52
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +323 -266
- package/dist/store.js.map +1 -1
- package/dist/util.d.ts +3 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +2 -0
- package/dist/util.js.map +1 -1
- package/package.json +2 -1
- package/src/QueryCache.ts +1 -1
- package/src/__tests__/react/fixture.tsx +21 -16
- package/src/__tests__/react/{useLiveStoreComponent.test.tsx → useComponentState.test.tsx} +9 -20
- package/src/__tests__/react/useQuery.test.tsx +48 -0
- package/src/__tests__/react/utils/extractStackInfoFromStackTrace.test.ts +40 -0
- package/src/__tests__/reactive.test.ts +194 -142
- package/src/__tests__/reactiveQueries/sql.test.ts +372 -0
- package/src/effect/LiveStore.ts +14 -18
- package/src/inMemoryDatabase.ts +22 -30
- package/src/index.ts +8 -6
- package/src/migrations.ts +39 -21
- package/src/react/LiveStoreProvider.tsx +13 -16
- package/src/react/index.ts +4 -8
- package/src/react/{useLiveStoreComponent.ts → useComponentState.ts} +98 -230
- package/src/react/useQuery.ts +58 -0
- package/src/react/useTemporaryQuery.ts +21 -0
- package/src/react/utils/extractStackInfoFromStackTrace.ts +47 -0
- package/src/reactive.ts +386 -267
- package/src/reactiveQueries/base-class.ts +61 -39
- package/src/reactiveQueries/graph.ts +15 -0
- package/src/reactiveQueries/graphql.ts +147 -31
- package/src/reactiveQueries/js.ts +54 -21
- package/src/reactiveQueries/sql.ts +128 -37
- package/src/schema.ts +2 -5
- package/src/storage/in-memory/index.ts +2 -2
- package/src/storage/index.ts +2 -2
- package/src/storage/tauri/index.ts +2 -2
- package/src/storage/web-worker/index.ts +6 -8
- package/src/storage/web-worker/worker.ts +2 -2
- package/src/store.ts +394 -418
- package/src/util.ts +8 -2
- package/dist/backends/base.d.ts +0 -13
- package/dist/backends/base.d.ts.map +0 -1
- package/dist/backends/base.js +0 -53
- package/dist/backends/base.js.map +0 -1
- package/dist/backends/in-memory/index.d.ts +0 -22
- package/dist/backends/in-memory/index.d.ts.map +0 -1
- package/dist/backends/in-memory/index.js +0 -45
- package/dist/backends/in-memory/index.js.map +0 -1
- package/dist/backends/index.d.ts +0 -41
- package/dist/backends/index.d.ts.map +0 -1
- package/dist/backends/index.js +0 -16
- package/dist/backends/index.js.map +0 -1
- package/dist/backends/tauri/index.d.ts +0 -21
- package/dist/backends/tauri/index.d.ts.map +0 -1
- package/dist/backends/tauri/index.js +0 -48
- package/dist/backends/tauri/index.js.map +0 -1
- package/dist/backends/utils/idb.d.ts +0 -10
- package/dist/backends/utils/idb.d.ts.map +0 -1
- package/dist/backends/utils/idb.js +0 -58
- package/dist/backends/utils/idb.js.map +0 -1
- package/dist/backends/web-worker/index.d.ts +0 -26
- package/dist/backends/web-worker/index.d.ts.map +0 -1
- package/dist/backends/web-worker/index.js +0 -63
- package/dist/backends/web-worker/index.js.map +0 -1
- package/dist/backends/web-worker/worker.d.ts +0 -17
- package/dist/backends/web-worker/worker.d.ts.map +0 -1
- package/dist/backends/web-worker/worker.js +0 -139
- package/dist/backends/web-worker/worker.js.map +0 -1
- package/dist/storage/base.d.ts +0 -10
- package/dist/storage/base.d.ts.map +0 -1
- package/dist/storage/base.js +0 -14
- package/dist/storage/base.js.map +0 -1
- package/src/react/useGlobalQuery.ts +0 -37
- package/src/react/useGraphQL.ts +0 -112
package/src/store.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
|
|
2
1
|
import { assertNever, makeNoopSpan, makeNoopTracer, shouldNeverHappen } from '@livestore/utils'
|
|
3
2
|
import { identity } from '@livestore/utils/effect'
|
|
4
3
|
import * as otel from '@opentelemetry/api'
|
|
5
4
|
import type { GraphQLSchema } from 'graphql'
|
|
6
|
-
import * as
|
|
7
|
-
import { uniqueId } from 'lodash-es'
|
|
8
|
-
import * as ReactDOM from 'react-dom'
|
|
9
|
-
import initSqlite3Wasm from 'sqlite-esm'
|
|
5
|
+
import type * as Sqlite from 'sqlite-esm'
|
|
10
6
|
import { v4 as uuid } from 'uuid'
|
|
11
7
|
|
|
12
8
|
import type { ComponentKey } from './componentKey.js'
|
|
@@ -15,16 +11,17 @@ import type { LiveStoreEvent } from './events.js'
|
|
|
15
11
|
import { InMemoryDatabase } from './inMemoryDatabase.js'
|
|
16
12
|
import { migrateDb } from './migrations.js'
|
|
17
13
|
import { getDurationMsFromSpan } from './otel.js'
|
|
18
|
-
import type {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
14
|
+
import type { ReactiveGraph, Ref } from './reactive.js'
|
|
15
|
+
import type { ILiveStoreQuery } from './reactiveQueries/base-class.js'
|
|
16
|
+
import { type DbContext, dbGraph } from './reactiveQueries/graph.js'
|
|
17
|
+
import type { LiveStoreGraphQLQuery } from './reactiveQueries/graphql.js'
|
|
18
|
+
import type { LiveStoreJSQuery } from './reactiveQueries/js.js'
|
|
19
|
+
import type { LiveStoreSQLQuery } from './reactiveQueries/sql.js'
|
|
23
20
|
import type { ActionDefinition, GetActionArgs, Schema, SQLWriteStatement } from './schema.js'
|
|
24
21
|
import { componentStateTables } from './schema.js'
|
|
25
22
|
import type { Storage, StorageInit } from './storage/index.js'
|
|
26
|
-
import type {
|
|
27
|
-
import { isPromise, sql } from './util.js'
|
|
23
|
+
import type { ParamsObject } from './util.js'
|
|
24
|
+
import { isPromise, prepareBindValues, sql } from './util.js'
|
|
28
25
|
|
|
29
26
|
export type LiveStoreQuery<TResult extends Record<string, any> = any> =
|
|
30
27
|
| LiveStoreSQLQuery<TResult>
|
|
@@ -37,8 +34,6 @@ export type BaseGraphQLContext = {
|
|
|
37
34
|
otelContext?: otel.Context
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
export const RESET_DB_LOCAL_STORAGE_KEY = 'livestore-reset'
|
|
41
|
-
|
|
42
37
|
export type QueryResult<TQuery> = TQuery extends LiveStoreSQLQuery<infer R>
|
|
43
38
|
? ReadonlyArray<Readonly<R>>
|
|
44
39
|
: TQuery extends LiveStoreJSQuery<infer S>
|
|
@@ -47,7 +42,7 @@ export type QueryResult<TQuery> = TQuery extends LiveStoreSQLQuery<infer R>
|
|
|
47
42
|
? Readonly<Result>
|
|
48
43
|
: never
|
|
49
44
|
|
|
50
|
-
const globalComponentKey: ComponentKey = { _tag: 'singleton', componentName: '__global', id: 'singleton' }
|
|
45
|
+
export const globalComponentKey: ComponentKey = { _tag: 'singleton', componentName: '__global', id: 'singleton' }
|
|
51
46
|
|
|
52
47
|
export type GraphQLOptions<TContext> = {
|
|
53
48
|
schema: GraphQLSchema
|
|
@@ -56,6 +51,8 @@ export type GraphQLOptions<TContext> = {
|
|
|
56
51
|
|
|
57
52
|
export type StoreOptions<TGraphQLContext extends BaseGraphQLContext> = {
|
|
58
53
|
db: InMemoryDatabase
|
|
54
|
+
/** A `Proxy`d version of `db` except that it also mirrors `execute` calls to the storage */
|
|
55
|
+
dbProxy: InMemoryDatabase
|
|
59
56
|
schema: Schema
|
|
60
57
|
storage?: Storage
|
|
61
58
|
graphQLOptions?: GraphQLOptions<TGraphQLContext>
|
|
@@ -100,9 +97,11 @@ export type StoreOtel = {
|
|
|
100
97
|
queriesSpanContext: otel.Context
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
104
|
-
graph: ReactiveGraph<RefreshReason, QueryDebugInfo>
|
|
100
|
+
export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLContext> {
|
|
101
|
+
graph: ReactiveGraph<RefreshReason, QueryDebugInfo, DbContext>
|
|
105
102
|
inMemoryDB: InMemoryDatabase
|
|
103
|
+
// TODO refactor
|
|
104
|
+
_proxyDb: InMemoryDatabase
|
|
106
105
|
schema: Schema
|
|
107
106
|
graphQLSchema?: GraphQLSchema
|
|
108
107
|
graphQLContext?: TGraphQLContext
|
|
@@ -118,6 +117,7 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
118
117
|
|
|
119
118
|
private constructor({
|
|
120
119
|
db,
|
|
120
|
+
dbProxy,
|
|
121
121
|
schema,
|
|
122
122
|
storage,
|
|
123
123
|
graphQLOptions,
|
|
@@ -125,12 +125,13 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
125
125
|
otelRootSpanContext,
|
|
126
126
|
}: StoreOptions<TGraphQLContext>) {
|
|
127
127
|
this.inMemoryDB = db
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
this._proxyDb = dbProxy
|
|
129
|
+
// this.graph = new ReactiveGraph({
|
|
130
|
+
// // TODO move this into React module
|
|
131
|
+
// // Do all our updates inside a single React setState batch to avoid multiple UI re-renders
|
|
132
|
+
// effectsWrapper: (run) => ReactDOM.unstable_batchedUpdates(() => run()),
|
|
133
|
+
// otelTracer,
|
|
134
|
+
// })
|
|
134
135
|
this.schema = schema
|
|
135
136
|
// TODO generalize the `tableRefs` concept to allow finer-grained refs
|
|
136
137
|
this.tableRefs = {}
|
|
@@ -143,6 +144,9 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
143
144
|
const queriesSpan = otelTracer.startSpan('LiveStore:queries', {}, otelRootSpanContext)
|
|
144
145
|
const otelQueriesSpanContext = otel.trace.setSpan(otel.context.active(), queriesSpan)
|
|
145
146
|
|
|
147
|
+
this.graph = dbGraph
|
|
148
|
+
this.graph.context = { store: this, otelTracer, rootOtelContext: otelQueriesSpanContext }
|
|
149
|
+
|
|
146
150
|
this.otel = {
|
|
147
151
|
tracer: otelTracer,
|
|
148
152
|
applyEventsSpanContext: otelApplyEventsSpanContext,
|
|
@@ -187,372 +191,340 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
187
191
|
*
|
|
188
192
|
* NOTE The query is actually running (even if no one has subscribed to it yet) and will be kept up to date.
|
|
189
193
|
*/
|
|
190
|
-
querySQL = <TResult>(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
): LiveStoreSQLQuery<TResult> =>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
194
|
+
// querySQL = <TResult>(
|
|
195
|
+
// genQueryString: string | ((get: GetAtomResult) => string),
|
|
196
|
+
// {
|
|
197
|
+
// queriedTables,
|
|
198
|
+
// bindValues,
|
|
199
|
+
// componentKey,
|
|
200
|
+
// label,
|
|
201
|
+
// otelContext = otel.context.active(),
|
|
202
|
+
// }: {
|
|
203
|
+
// /**
|
|
204
|
+
// * List of tables that are queried in this query;
|
|
205
|
+
// * used to determine reactive dependencies.
|
|
206
|
+
// *
|
|
207
|
+
// * NOTE In the future we want to auto-generate this via parsing the query
|
|
208
|
+
// */
|
|
209
|
+
// queriedTables: string[]
|
|
210
|
+
// bindValues?: Bindable | undefined
|
|
211
|
+
// componentKey?: ComponentKey | undefined
|
|
212
|
+
// label?: string | undefined
|
|
213
|
+
// otelContext?: otel.Context
|
|
214
|
+
// },
|
|
215
|
+
// ): LiveStoreSQLQuery<TResult> =>
|
|
216
|
+
// this.otel.tracer.startActiveSpan(
|
|
217
|
+
// 'querySQL', // NOTE span name will be overridden further down
|
|
218
|
+
// { attributes: { label } },
|
|
219
|
+
// otelContext,
|
|
220
|
+
// (span) => {
|
|
221
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
222
|
+
|
|
223
|
+
// const queryString$ = this.graph.makeThunk(
|
|
224
|
+
// (get, addDebugInfo) => {
|
|
225
|
+
// if (typeof genQueryString === 'function') {
|
|
226
|
+
// const queryString = genQueryString(makeGetAtomResult(get))
|
|
227
|
+
// addDebugInfo({ _tag: 'js', label: `${label}:queryString`, query: queryString })
|
|
228
|
+
// return queryString
|
|
229
|
+
// } else {
|
|
230
|
+
// return genQueryString
|
|
231
|
+
// }
|
|
232
|
+
// },
|
|
233
|
+
// { label: `${label}:queryString`, meta: { liveStoreThunkType: 'sqlQueryString' } },
|
|
234
|
+
// otelContext,
|
|
235
|
+
// )
|
|
236
|
+
|
|
237
|
+
// label = label ?? queryString$.result
|
|
238
|
+
// span.updateName(`querySQL:${label}`)
|
|
239
|
+
|
|
240
|
+
// const queryLabel = `${label}:results` + (this.temporaryQueries ? ':temp' : '')
|
|
241
|
+
|
|
242
|
+
// const results$ = this.graph.makeThunk<ReadonlyArray<TResult>>(
|
|
243
|
+
// (get, addDebugInfo) =>
|
|
244
|
+
// this.otel.tracer.startActiveSpan(
|
|
245
|
+
// 'sql:', // NOTE span name will be overridden further down
|
|
246
|
+
// {},
|
|
247
|
+
// otelContext,
|
|
248
|
+
// (span) => {
|
|
249
|
+
// try {
|
|
250
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
251
|
+
|
|
252
|
+
// // Establish a reactive dependency on the tables used in the query
|
|
253
|
+
// for (const tableName of queriedTables) {
|
|
254
|
+
// const tableRef =
|
|
255
|
+
// this.tableRefs[tableName] ?? shouldNeverHappen(`No table ref found for ${tableName}`)
|
|
256
|
+
// get(tableRef)
|
|
257
|
+
// }
|
|
258
|
+
// const sqlString = get(queryString$)
|
|
259
|
+
|
|
260
|
+
// span.setAttribute('sql.query', sqlString)
|
|
261
|
+
// span.updateName(`sql:${sqlString.slice(0, 50)}`)
|
|
262
|
+
|
|
263
|
+
// const results = this.inMemoryDB.select<TResult>(sqlString, {
|
|
264
|
+
// queriedTables,
|
|
265
|
+
// bindValues: bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
|
|
266
|
+
// otelContext,
|
|
267
|
+
// })
|
|
268
|
+
|
|
269
|
+
// span.setAttribute('sql.rowsCount', results.length)
|
|
270
|
+
// addDebugInfo({ _tag: 'sql', label: label ?? '', query: sqlString })
|
|
271
|
+
|
|
272
|
+
// return results
|
|
273
|
+
// } finally {
|
|
274
|
+
// span.end()
|
|
275
|
+
// }
|
|
276
|
+
// },
|
|
277
|
+
// ),
|
|
278
|
+
// { label: queryLabel },
|
|
279
|
+
// otelContext,
|
|
280
|
+
// )
|
|
281
|
+
|
|
282
|
+
// const query = new LiveStoreSQLQuery<TResult>({
|
|
283
|
+
// label,
|
|
284
|
+
// queryString$,
|
|
285
|
+
// results$,
|
|
286
|
+
// componentKey: componentKey ?? globalComponentKey,
|
|
287
|
+
// store: this,
|
|
288
|
+
// otelContext,
|
|
289
|
+
// })
|
|
290
|
+
|
|
291
|
+
// this.activeQueries.add(query)
|
|
292
|
+
|
|
293
|
+
// // TODO get rid of temporary query workaround
|
|
294
|
+
// if (this.temporaryQueries !== undefined) {
|
|
295
|
+
// this.temporaryQueries.add(query)
|
|
296
|
+
// }
|
|
297
|
+
|
|
298
|
+
// // NOTE we are not ending the span here but in the query `destroy` method
|
|
299
|
+
// return query
|
|
300
|
+
// },
|
|
301
|
+
// )
|
|
302
|
+
|
|
303
|
+
// queryJS = <TResult>(
|
|
304
|
+
// genResults: (get: GetAtomResult) => TResult,
|
|
305
|
+
// {
|
|
306
|
+
// componentKey = globalComponentKey,
|
|
307
|
+
// label = `js${uniqueId()}`,
|
|
308
|
+
// otelContext = otel.context.active(),
|
|
309
|
+
// }: { componentKey?: ComponentKey; label?: string; otelContext?: otel.Context },
|
|
310
|
+
// ): LiveStoreJSQuery<TResult> =>
|
|
311
|
+
// this.otel.tracer.startActiveSpan(`queryJS:${label}`, { attributes: { label } }, otelContext, (span) => {
|
|
312
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
313
|
+
// const queryLabel = `${label}:results` + (this.temporaryQueries ? ':temp' : '')
|
|
314
|
+
// const results$ = this.graph.makeThunk(
|
|
315
|
+
// (get, addDebugInfo) => {
|
|
316
|
+
// addDebugInfo({ _tag: 'js', label, query: genResults.toString() })
|
|
317
|
+
// return genResults(makeGetAtomResult(get))
|
|
318
|
+
// },
|
|
319
|
+
// { label: queryLabel, meta: { liveStoreThunkType: 'jsResults' } },
|
|
320
|
+
// otelContext,
|
|
321
|
+
// )
|
|
322
|
+
|
|
323
|
+
// // const query = new LiveStoreJSQuery<TResult>({
|
|
324
|
+
// // label,
|
|
325
|
+
// // results$,
|
|
326
|
+
// // componentKey,
|
|
327
|
+
// // store: this,
|
|
328
|
+
// // otelContext,
|
|
329
|
+
// // })
|
|
330
|
+
|
|
331
|
+
// this.activeQueries.add(query)
|
|
332
|
+
|
|
333
|
+
// // TODO get rid of temporary query workaround
|
|
334
|
+
// if (this.temporaryQueries !== undefined) {
|
|
335
|
+
// this.temporaryQueries.add(query)
|
|
336
|
+
// }
|
|
337
|
+
|
|
338
|
+
// // NOTE we are not ending the span here but in the query `destroy` method
|
|
339
|
+
// return query
|
|
340
|
+
// })
|
|
341
|
+
|
|
342
|
+
// queryGraphQL = <TResult extends Record<string, any>, TVariableValues extends Record<string, any>>(
|
|
343
|
+
// document: DocumentNode<TResult, TVariableValues>,
|
|
344
|
+
// genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues),
|
|
345
|
+
// {
|
|
346
|
+
// componentKey,
|
|
347
|
+
// label,
|
|
348
|
+
// otelContext = otel.context.active(),
|
|
349
|
+
// }: {
|
|
350
|
+
// componentKey: ComponentKey
|
|
351
|
+
// label?: string
|
|
352
|
+
// otelContext?: otel.Context
|
|
353
|
+
// },
|
|
354
|
+
// ): LiveStoreGraphQLQuery<TResult, TVariableValues, TGraphQLContext> =>
|
|
355
|
+
// this.otel.tracer.startActiveSpan(
|
|
356
|
+
// `queryGraphQL:`, // NOTE span name will be overridden further down
|
|
357
|
+
// {},
|
|
358
|
+
// otelContext,
|
|
359
|
+
// (span) => {
|
|
360
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
361
|
+
|
|
362
|
+
// if (this.graphQLContext === undefined) {
|
|
363
|
+
// return shouldNeverHappen("Can't run a GraphQL query on a store without GraphQL context")
|
|
364
|
+
// }
|
|
365
|
+
|
|
366
|
+
// const labelWithDefault = label ?? graphql.getOperationAST(document)?.name?.value ?? 'graphql'
|
|
367
|
+
|
|
368
|
+
// span.updateName(`queryGraphQL:${labelWithDefault}`)
|
|
369
|
+
|
|
370
|
+
// const variableValues$ = this.graph.makeThunk(
|
|
371
|
+
// (get) => {
|
|
372
|
+
// if (typeof genVariableValues === 'function') {
|
|
373
|
+
// return genVariableValues(makeGetAtomResult(get))
|
|
374
|
+
// } else {
|
|
375
|
+
// return genVariableValues
|
|
376
|
+
// }
|
|
377
|
+
// },
|
|
378
|
+
// { label: `${labelWithDefault}:variableValues`, meta: { liveStoreThunkType: 'graphqlVariableValues' } },
|
|
379
|
+
// // otelContext,
|
|
380
|
+
// )
|
|
381
|
+
|
|
382
|
+
// const resultsLabel = `${labelWithDefault}:results` + (this.temporaryQueries ? ':temp' : '')
|
|
383
|
+
// const results$ = this.graph.makeThunk<TResult>(
|
|
384
|
+
// (get, addDebugInfo) => {
|
|
385
|
+
// const variableValues = get(variableValues$)
|
|
386
|
+
// const { result, queriedTables } = this.queryGraphQLOnce(document, variableValues, otelContext)
|
|
387
|
+
|
|
388
|
+
// // Add dependencies on any tables that were used
|
|
389
|
+
// for (const tableName of queriedTables) {
|
|
390
|
+
// const tableRef = this.tableRefs[tableName]
|
|
391
|
+
// assertNever(tableRef !== undefined, `No table ref found for ${tableName}`)
|
|
392
|
+
// get(tableRef!)
|
|
393
|
+
// }
|
|
394
|
+
|
|
395
|
+
// addDebugInfo({ _tag: 'graphql', label: resultsLabel, query: graphql.print(document) })
|
|
396
|
+
|
|
397
|
+
// return result
|
|
398
|
+
// },
|
|
399
|
+
// { label: resultsLabel, meta: { liveStoreThunkType: 'graphqlResults' } },
|
|
400
|
+
// // otelContext,
|
|
401
|
+
// )
|
|
402
|
+
|
|
403
|
+
// const query = new LiveStoreGraphQLQuery({
|
|
404
|
+
// document,
|
|
405
|
+
// context: this.graphQLContext,
|
|
406
|
+
// results$,
|
|
407
|
+
// componentKey,
|
|
408
|
+
// label: labelWithDefault,
|
|
409
|
+
// store: this,
|
|
410
|
+
// otelContext,
|
|
411
|
+
// })
|
|
412
|
+
|
|
413
|
+
// this.activeQueries.add(query)
|
|
414
|
+
|
|
415
|
+
// // TODO get rid of temporary query workaround
|
|
416
|
+
// if (this.temporaryQueries !== undefined) {
|
|
417
|
+
// this.temporaryQueries.add(query)
|
|
418
|
+
// }
|
|
419
|
+
|
|
420
|
+
// // NOTE we are not ending the span here but in the query `destroy` method
|
|
421
|
+
// return query
|
|
422
|
+
// },
|
|
423
|
+
// )
|
|
424
|
+
|
|
425
|
+
// queryGraphQLOnce = <TResult extends Record<string, any>, TVariableValues extends Record<string, any>>(
|
|
426
|
+
// document: DocumentNode<TResult, TVariableValues>,
|
|
427
|
+
// variableValues: TVariableValues,
|
|
428
|
+
// otelContext: otel.Context = this.otel.queriesSpanContext,
|
|
429
|
+
// ): { result: TResult; queriedTables: string[] } => {
|
|
430
|
+
// const schema =
|
|
431
|
+
// this.graphQLSchema ?? shouldNeverHappen("Can't run a GraphQL query on a store without GraphQL schema")
|
|
432
|
+
// const context =
|
|
433
|
+
// this.graphQLContext ?? shouldNeverHappen("Can't run a GraphQL query on a store without GraphQL context")
|
|
434
|
+
// const tracer = this.otel.tracer
|
|
435
|
+
|
|
436
|
+
// const operationName = graphql.getOperationAST(document)?.name?.value
|
|
437
|
+
|
|
438
|
+
// return tracer.startActiveSpan(`executeGraphQLQuery: ${operationName}`, {}, otelContext, (span) => {
|
|
439
|
+
// try {
|
|
440
|
+
// span.setAttribute('graphql.variables', JSON.stringify(variableValues))
|
|
441
|
+
// span.setAttribute('graphql.query', graphql.print(document))
|
|
442
|
+
|
|
443
|
+
// context.queriedTables.clear()
|
|
444
|
+
|
|
445
|
+
// context.otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
446
|
+
|
|
447
|
+
// const res = graphql.executeSync({
|
|
448
|
+
// document,
|
|
449
|
+
// contextValue: context,
|
|
450
|
+
// schema: schema,
|
|
451
|
+
// variableValues,
|
|
452
|
+
// })
|
|
453
|
+
|
|
454
|
+
// // TODO track number of nested SQL queries via Otel + debug info
|
|
455
|
+
|
|
456
|
+
// if (res.errors) {
|
|
457
|
+
// span.setStatus({ code: otel.SpanStatusCode.ERROR, message: 'GraphQL error' })
|
|
458
|
+
// span.setAttribute('graphql.error', res.errors.join('\n'))
|
|
459
|
+
// span.setAttribute('graphql.error-detail', JSON.stringify(res.errors))
|
|
460
|
+
// console.error(`graphql error (${operationName})`, res.errors)
|
|
461
|
+
// }
|
|
462
|
+
|
|
463
|
+
// return { result: res.data as unknown as TResult, queriedTables: Array.from(context.queriedTables.values()) }
|
|
464
|
+
// } finally {
|
|
465
|
+
// span.end()
|
|
466
|
+
// }
|
|
467
|
+
// })
|
|
468
|
+
// }
|
|
451
469
|
|
|
452
470
|
/**
|
|
453
471
|
* Subscribe to the results of a query
|
|
454
472
|
* Returns a function to cancel the subscription.
|
|
455
473
|
*/
|
|
456
|
-
subscribe = <
|
|
457
|
-
query:
|
|
458
|
-
onNewValue: (value:
|
|
474
|
+
subscribe = <TResult>(
|
|
475
|
+
query: ILiveStoreQuery<TResult>,
|
|
476
|
+
onNewValue: (value: TResult) => void,
|
|
459
477
|
onSubsubscribe?: () => void,
|
|
460
|
-
options?: { label?: string } | undefined,
|
|
478
|
+
options?: { label?: string; otelContext?: otel.Context } | undefined,
|
|
461
479
|
): (() => void) =>
|
|
462
480
|
this.otel.tracer.startActiveSpan(
|
|
463
481
|
`LiveStore.subscribe`,
|
|
464
482
|
{ attributes: { label: options?.label } },
|
|
465
|
-
|
|
483
|
+
options?.otelContext ?? this.otel.queriesSpanContext,
|
|
466
484
|
(span) => {
|
|
467
485
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
468
486
|
|
|
469
|
-
const effect = this.graph.makeEffect(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
{ label: `subscribe:${options?.label}` },
|
|
475
|
-
otelContext,
|
|
476
|
-
)
|
|
487
|
+
const effect = this.graph.makeEffect((get) => onNewValue(get(query.results$)), {
|
|
488
|
+
label: `subscribe:${options?.label}`,
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
effect.doEffect(otelContext)
|
|
477
492
|
|
|
478
|
-
const subscriptionKey = uuid()
|
|
493
|
+
// const subscriptionKey = uuid()
|
|
479
494
|
|
|
480
495
|
const unsubscribe = () => {
|
|
481
496
|
try {
|
|
482
497
|
this.graph.destroy(effect)
|
|
483
|
-
|
|
498
|
+
this.activeQueries.delete(query as LiveStoreQuery)
|
|
499
|
+
// query.activeSubscriptions.delete(subscriptionKey)
|
|
484
500
|
onSubsubscribe?.()
|
|
485
501
|
} finally {
|
|
486
502
|
span.end()
|
|
487
503
|
}
|
|
488
504
|
}
|
|
489
505
|
|
|
490
|
-
|
|
506
|
+
this.activeQueries.add(query as LiveStoreQuery)
|
|
507
|
+
|
|
508
|
+
// query.activeSubscriptions.set(subscriptionKey, unsubscribe)
|
|
491
509
|
|
|
492
510
|
return unsubscribe
|
|
493
511
|
},
|
|
494
512
|
)
|
|
495
513
|
|
|
496
|
-
/**
|
|
497
|
-
* Any queries created in the callback will be destroyed when the callback is complete.
|
|
498
|
-
* Useful for temporarily creating reactive queries, which is an idempotent operation
|
|
499
|
-
* that can be safely called inside a React useMemo hook.
|
|
500
|
-
*/
|
|
501
|
-
inTempQueryContext = <TResult>(callback: () => TResult): TResult => {
|
|
502
|
-
this.temporaryQueries = new Set()
|
|
503
|
-
// TODO: consider errors / try/finally here?
|
|
504
|
-
const result = callback()
|
|
505
|
-
for (const query of this.temporaryQueries) {
|
|
506
|
-
this.destroyQuery(query)
|
|
507
|
-
}
|
|
508
|
-
this.temporaryQueries = undefined
|
|
509
|
-
return result
|
|
510
|
-
}
|
|
511
|
-
|
|
512
514
|
/**
|
|
513
515
|
* Destroys the entire store, including all queries and subscriptions.
|
|
514
516
|
*
|
|
515
517
|
* Currently only used when shutting down the app for debugging purposes (e.g. to close Otel spans).
|
|
516
518
|
*/
|
|
517
519
|
destroy = () => {
|
|
518
|
-
for (const query of this.activeQueries) {
|
|
519
|
-
this.destroyQuery(query)
|
|
520
|
-
}
|
|
521
|
-
|
|
522
520
|
Object.values(this.tableRefs).forEach((tableRef) => this.graph.destroy(tableRef))
|
|
523
521
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const queriesSpan = otel.trace.getSpan(this.otel.queriesSpanContext)!
|
|
528
|
-
queriesSpan.end()
|
|
522
|
+
otel.trace.getSpan(this.otel.applyEventsSpanContext)!.end()
|
|
523
|
+
otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
|
|
529
524
|
|
|
530
525
|
// TODO destroy active subscriptions
|
|
531
526
|
}
|
|
532
527
|
|
|
533
|
-
private destroyQuery = (query: LiveStoreQuery) => {
|
|
534
|
-
if (query._tag === 'sql') {
|
|
535
|
-
// results are downstream of query string, so will automatically be destroyed together
|
|
536
|
-
this.graph.destroy(query.queryString$)
|
|
537
|
-
} else {
|
|
538
|
-
this.graph.destroy(query.results$)
|
|
539
|
-
}
|
|
540
|
-
this.activeQueries.delete(query)
|
|
541
|
-
query.destroy()
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Clean up queries and downstream subscriptions associated with a component.
|
|
546
|
-
* This is critical to avoid memory leaks.
|
|
547
|
-
*/
|
|
548
|
-
unmountComponent = (componentKey: ComponentKey) => {
|
|
549
|
-
for (const query of this.activeQueries) {
|
|
550
|
-
if (query.componentKey === componentKey) {
|
|
551
|
-
this.destroyQuery(query)
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
528
|
/* Apply a single write event to the store, and refresh all queries in response */
|
|
557
529
|
applyEvent = <TEventType extends string & keyof LiveStoreActionDefinitionsTypes>(
|
|
558
530
|
eventType: TEventType,
|
|
@@ -581,20 +553,25 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
581
553
|
tablesToUpdate.push([tableRef!, null])
|
|
582
554
|
}
|
|
583
555
|
|
|
556
|
+
const debugRefreshReason = {
|
|
557
|
+
_tag: 'applyEvent' as const,
|
|
558
|
+
event: { type: eventType, args },
|
|
559
|
+
writeTables: [...writeTables],
|
|
560
|
+
}
|
|
561
|
+
|
|
584
562
|
// Update all table refs together in a batch, to only trigger one reactive update
|
|
585
|
-
this.graph.setRefs(
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
)
|
|
563
|
+
this.graph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext })
|
|
564
|
+
|
|
565
|
+
if (skipRefresh === false) {
|
|
566
|
+
// TODO update the graph
|
|
567
|
+
// this.graph.refresh(
|
|
568
|
+
// {
|
|
569
|
+
// otelHint: 'applyEvents',
|
|
570
|
+
// debugRefreshReason,
|
|
571
|
+
// },
|
|
572
|
+
// otelContext,
|
|
573
|
+
// )
|
|
574
|
+
}
|
|
598
575
|
} catch (e: any) {
|
|
599
576
|
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
|
|
600
577
|
|
|
@@ -677,20 +654,18 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
677
654
|
tablesToUpdate.push([tableRef!, null])
|
|
678
655
|
}
|
|
679
656
|
|
|
657
|
+
const debugRefreshReason = {
|
|
658
|
+
_tag: 'applyEvents' as const,
|
|
659
|
+
events: [...events].map((e) => ({ type: e.eventType, args: e.args })),
|
|
660
|
+
writeTables: [...writeTables],
|
|
661
|
+
}
|
|
680
662
|
// Update all table refs together in a batch, to only trigger one reactive update
|
|
681
|
-
this.graph.setRefs(
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
_tag: 'applyEvents',
|
|
688
|
-
events: [...events].map((e) => ({ type: e.eventType, args: e.args })),
|
|
689
|
-
writeTables: [...writeTables],
|
|
690
|
-
},
|
|
691
|
-
},
|
|
692
|
-
otelContext,
|
|
693
|
-
)
|
|
663
|
+
this.graph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext })
|
|
664
|
+
|
|
665
|
+
if (skipRefresh === false) {
|
|
666
|
+
// TODO update the graph
|
|
667
|
+
// this.graph.refresh({ debugRefreshReason, otelHint: 'applyEvents' }, otelContext)
|
|
668
|
+
}
|
|
694
669
|
} catch (e: any) {
|
|
695
670
|
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
|
|
696
671
|
} finally {
|
|
@@ -713,8 +688,9 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
713
688
|
{ attributes: { 'livestore.manualRefreshLabel': label } },
|
|
714
689
|
this.otel.applyEventsSpanContext,
|
|
715
690
|
(span) => {
|
|
716
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
717
|
-
|
|
691
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
692
|
+
// TODO update the graph
|
|
693
|
+
// this.graph.refresh({ otelHint: 'manualRefresh', debugRefreshReason: { _tag: 'manualRefresh' } }, otelContext)
|
|
718
694
|
span.end()
|
|
719
695
|
},
|
|
720
696
|
)
|
|
@@ -778,14 +754,19 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
778
754
|
// Synchronously apply the event to the in-memory database
|
|
779
755
|
// const { durationMs } = this.inMemoryDB.applyEvent(eventWithId, actionDefinition, otelContext)
|
|
780
756
|
const { statement, bindValues } = eventToSql(eventWithId, actionDefinition)
|
|
781
|
-
const { durationMs } = this.inMemoryDB.execute(
|
|
782
|
-
|
|
783
|
-
|
|
757
|
+
const { durationMs } = this.inMemoryDB.execute(
|
|
758
|
+
statement.sql,
|
|
759
|
+
prepareBindValues(bindValues, statement.sql),
|
|
760
|
+
statement.writeTables,
|
|
761
|
+
{
|
|
762
|
+
otelContext,
|
|
763
|
+
},
|
|
764
|
+
)
|
|
784
765
|
|
|
785
766
|
// Asynchronously apply the event to a persistent storage (we're not awaiting this promise here)
|
|
786
767
|
if (this.storage !== undefined) {
|
|
787
768
|
// this.storage.applyEvent(eventWithId, actionDefinition, span)
|
|
788
|
-
this.storage.execute(statement.sql, bindValues, span)
|
|
769
|
+
this.storage.execute(statement.sql, prepareBindValues(bindValues, statement.sql), span)
|
|
789
770
|
}
|
|
790
771
|
|
|
791
772
|
// Uncomment to print a list of queries currently registered on the store
|
|
@@ -808,12 +789,12 @@ export class Store<TGraphQLContext extends BaseGraphQLContext> {
|
|
|
808
789
|
* This should only be used for framework-internal purposes;
|
|
809
790
|
* all app writes should go through applyEvent.
|
|
810
791
|
*/
|
|
811
|
-
execute =
|
|
812
|
-
this.inMemoryDB.execute(query, params, writeTables)
|
|
792
|
+
execute = (query: string, params: ParamsObject = {}, writeTables?: string[]) => {
|
|
793
|
+
this.inMemoryDB.execute(query, prepareBindValues(params, query), writeTables)
|
|
813
794
|
|
|
814
795
|
if (this.storage !== undefined) {
|
|
815
796
|
const parentSpan = otel.trace.getSpan(otel.context.active())
|
|
816
|
-
this.storage.execute(query, params, parentSpan)
|
|
797
|
+
this.storage.execute(query, prepareBindValues(params, query), parentSpan)
|
|
817
798
|
}
|
|
818
799
|
}
|
|
819
800
|
}
|
|
@@ -826,6 +807,7 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
|
|
|
826
807
|
otelTracer = makeNoopTracer(),
|
|
827
808
|
otelRootSpanContext = otel.context.active(),
|
|
828
809
|
boot,
|
|
810
|
+
sqlite3,
|
|
829
811
|
}: {
|
|
830
812
|
schema: Schema
|
|
831
813
|
loadStorage: () => StorageInit | Promise<StorageInit>
|
|
@@ -833,50 +815,36 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
|
|
|
833
815
|
otelTracer?: otel.Tracer
|
|
834
816
|
otelRootSpanContext?: otel.Context
|
|
835
817
|
boot?: (db: InMemoryDatabase, parentSpan: otel.Span) => unknown | Promise<unknown>
|
|
818
|
+
sqlite3: Sqlite.Sqlite3Static
|
|
836
819
|
}): Promise<Store<TGraphQLContext>> => {
|
|
837
820
|
return otelTracer.startActiveSpan('createStore', {}, otelRootSpanContext, async (span) => {
|
|
838
821
|
try {
|
|
839
822
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
840
823
|
|
|
841
|
-
const
|
|
842
|
-
|
|
824
|
+
const storage = await otelTracer.startActiveSpan('storage:load', {}, otelContext, async (span) => {
|
|
825
|
+
try {
|
|
826
|
+
const init = await loadStorage()
|
|
827
|
+
const parentSpan = otel.trace.getSpan(otel.context.active()) ?? makeNoopSpan()
|
|
828
|
+
return init({ otelTracer, parentSpan })
|
|
829
|
+
} finally {
|
|
830
|
+
span.end()
|
|
831
|
+
}
|
|
832
|
+
})
|
|
833
|
+
|
|
834
|
+
const persistedData = await otelTracer.startActiveSpan(
|
|
835
|
+
'storage:getPersistedData',
|
|
836
|
+
{},
|
|
837
|
+
otelContext,
|
|
838
|
+
async (span) => {
|
|
843
839
|
try {
|
|
844
|
-
|
|
845
|
-
const parentSpan = otel.trace.getSpan(otel.context.active()) ?? makeNoopSpan()
|
|
846
|
-
return init({ otelTracer, parentSpan })
|
|
840
|
+
return await storage.getPersistedData(span)
|
|
847
841
|
} finally {
|
|
848
842
|
span.end()
|
|
849
843
|
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
const persistedData = await otelTracer.startActiveSpan(
|
|
853
|
-
'storage:getPersistedData',
|
|
854
|
-
{},
|
|
855
|
-
otelContext,
|
|
856
|
-
async (span) => {
|
|
857
|
-
try {
|
|
858
|
-
return await storage.getPersistedData(span)
|
|
859
|
-
} finally {
|
|
860
|
-
span.end()
|
|
861
|
-
}
|
|
862
|
-
},
|
|
863
|
-
)
|
|
864
|
-
|
|
865
|
-
return { storage, persistedData }
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
const loadSqlite3 = () =>
|
|
869
|
-
initSqlite3Wasm({
|
|
870
|
-
// Required to load the wasm binary asynchronously. Of course, you can host it wherever you want
|
|
871
|
-
// You can omit locateFile completely when running in node
|
|
872
|
-
// locateFile: () => `/sql-wasm.wasm`,
|
|
873
|
-
print: (message) => console.log(`[livestore sqlite] ${message}`),
|
|
874
|
-
printErr: (message) => console.error(`[livestore sqlite] ${message}`),
|
|
875
|
-
})
|
|
876
|
-
|
|
877
|
-
const [{ storage, persistedData }, sqlite3] = await Promise.all([loadStorageAndPersistedData(), loadSqlite3()])
|
|
844
|
+
},
|
|
845
|
+
)
|
|
878
846
|
|
|
879
|
-
const db = InMemoryDatabase.load(persistedData, otelTracer, otelRootSpanContext, sqlite3)
|
|
847
|
+
const db = InMemoryDatabase.load({ data: persistedData, otelTracer, otelRootSpanContext, sqlite3 })
|
|
880
848
|
|
|
881
849
|
// Proxy to `db` that also mirrors `execute` calls to `storage`
|
|
882
850
|
const dbProxy = new Proxy(db, {
|
|
@@ -887,6 +855,14 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
|
|
|
887
855
|
return db.execute(query, bindValues, writeTables, options)
|
|
888
856
|
}
|
|
889
857
|
return execute
|
|
858
|
+
} else if (prop === 'select') {
|
|
859
|
+
// NOTE we're even proxying `select` calls here as some apps (e.g. Overtone) currently rely on this
|
|
860
|
+
// TODO remove this once we've migrated all apps to use `execute` instead of `select`
|
|
861
|
+
const select: InMemoryDatabase['select'] = (query, options = {}) => {
|
|
862
|
+
storage.execute(query, options.bindValues as any)
|
|
863
|
+
return db.select(query, options)
|
|
864
|
+
}
|
|
865
|
+
return select
|
|
890
866
|
} else {
|
|
891
867
|
return Reflect.get(db, prop, receiver)
|
|
892
868
|
}
|
|
@@ -914,7 +890,7 @@ export const createStore = async <TGraphQLContext extends BaseGraphQLContext>({
|
|
|
914
890
|
// Think about what to do about this case.
|
|
915
891
|
// await applySchema(db, schema)
|
|
916
892
|
return Store.createStore<TGraphQLContext>(
|
|
917
|
-
{ db, schema, storage, graphQLOptions, otelTracer, otelRootSpanContext },
|
|
893
|
+
{ db, dbProxy, schema, storage, graphQLOptions, otelTracer, otelRootSpanContext },
|
|
918
894
|
span,
|
|
919
895
|
)
|
|
920
896
|
} finally {
|