@livestore/livestore 0.0.19 → 0.0.21
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 +5 -5
- 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 +167 -93
- 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/inMemoryDatabase.d.ts +2 -2
- package/dist/inMemoryDatabase.d.ts.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -2
- 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 +3 -0
- package/dist/react/useGlobalQuery.d.ts.map +1 -0
- package/dist/react/useGlobalQuery.js +26 -0
- package/dist/react/useGlobalQuery.js.map +1 -0
- package/dist/react/useGraphQL.d.ts +3 -3
- package/dist/react/useGraphQL.d.ts.map +1 -1
- package/dist/react/useGraphQL.js +10 -8
- package/dist/react/useGraphQL.js.map +1 -1
- package/dist/react/useLiveStoreComponent.d.ts +6 -6
- package/dist/react/useLiveStoreComponent.d.ts.map +1 -1
- package/dist/react/useLiveStoreComponent.js +143 -99
- package/dist/react/useLiveStoreComponent.js.map +1 -1
- package/dist/react/useQuery.d.ts +2 -2
- package/dist/react/useQuery.d.ts.map +1 -1
- package/dist/react/useQuery.js +26 -22
- package/dist/react/useQuery.js.map +1 -1
- 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 -23
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +25 -18
- 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 +34 -17
- 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 +16 -12
- 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 +82 -16
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/store.d.ts +12 -52
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +283 -264
- package/dist/store.js.map +1 -1
- package/package.json +4 -3
- package/src/QueryCache.ts +1 -1
- package/src/__tests__/react/fixture.tsx +12 -7
- 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 +193 -140
- package/src/__tests__/reactiveQueries/sql.test.ts +372 -0
- package/src/inMemoryDatabase.ts +2 -2
- package/src/index.ts +7 -11
- package/src/react/index.ts +3 -7
- package/src/react/{useLiveStoreComponent.ts → useComponentState.ts} +89 -247
- package/src/react/useQuery.ts +29 -27
- package/src/react/useTemporaryQuery.ts +21 -0
- package/src/react/utils/extractStackInfoFromStackTrace.ts +47 -0
- package/src/reactive.ts +385 -268
- package/src/reactiveQueries/base-class.ts +60 -44
- package/src/reactiveQueries/graph.ts +15 -0
- package/src/reactiveQueries/graphql.ts +145 -29
- package/src/reactiveQueries/js.ts +53 -20
- package/src/reactiveQueries/sql.ts +129 -36
- package/src/store.ts +338 -408
- package/src/react/useGraphQL.ts +0 -138
package/src/store.ts
CHANGED
|
@@ -1,34 +1,28 @@
|
|
|
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 graphql from 'graphql'
|
|
7
|
-
import { uniqueId } from 'lodash-es'
|
|
8
|
-
import * as ReactDOM from 'react-dom'
|
|
9
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'
|
|
13
9
|
import { tableNameForComponentKey } from './componentKey.js'
|
|
14
|
-
import type { QueryDefinition } from './effect/LiveStore.js'
|
|
15
10
|
import type { LiveStoreEvent } from './events.js'
|
|
16
11
|
import { InMemoryDatabase } from './inMemoryDatabase.js'
|
|
17
12
|
import { migrateDb } from './migrations.js'
|
|
18
13
|
import { getDurationMsFromSpan } from './otel.js'
|
|
19
|
-
import type {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
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'
|
|
24
20
|
import type { ActionDefinition, GetActionArgs, Schema, SQLWriteStatement } from './schema.js'
|
|
25
21
|
import { componentStateTables } from './schema.js'
|
|
26
22
|
import type { Storage, StorageInit } from './storage/index.js'
|
|
27
|
-
import type {
|
|
23
|
+
import type { ParamsObject } from './util.js'
|
|
28
24
|
import { isPromise, prepareBindValues, sql } from './util.js'
|
|
29
25
|
|
|
30
|
-
export type GetAtomResult = <T>(atom: Atom<T> | LiveStoreJSQuery<T>) => T
|
|
31
|
-
|
|
32
26
|
export type LiveStoreQuery<TResult extends Record<string, any> = any> =
|
|
33
27
|
| LiveStoreSQLQuery<TResult>
|
|
34
28
|
| LiveStoreJSQuery<TResult>
|
|
@@ -48,7 +42,7 @@ export type QueryResult<TQuery> = TQuery extends LiveStoreSQLQuery<infer R>
|
|
|
48
42
|
? Readonly<Result>
|
|
49
43
|
: never
|
|
50
44
|
|
|
51
|
-
const globalComponentKey: ComponentKey = { _tag: 'singleton', componentName: '__global', id: 'singleton' }
|
|
45
|
+
export const globalComponentKey: ComponentKey = { _tag: 'singleton', componentName: '__global', id: 'singleton' }
|
|
52
46
|
|
|
53
47
|
export type GraphQLOptions<TContext> = {
|
|
54
48
|
schema: GraphQLSchema
|
|
@@ -104,7 +98,7 @@ export type StoreOtel = {
|
|
|
104
98
|
}
|
|
105
99
|
|
|
106
100
|
export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLContext> {
|
|
107
|
-
graph: ReactiveGraph<RefreshReason, QueryDebugInfo>
|
|
101
|
+
graph: ReactiveGraph<RefreshReason, QueryDebugInfo, DbContext>
|
|
108
102
|
inMemoryDB: InMemoryDatabase
|
|
109
103
|
// TODO refactor
|
|
110
104
|
_proxyDb: InMemoryDatabase
|
|
@@ -132,12 +126,12 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
132
126
|
}: StoreOptions<TGraphQLContext>) {
|
|
133
127
|
this.inMemoryDB = db
|
|
134
128
|
this._proxyDb = dbProxy
|
|
135
|
-
this.graph = new ReactiveGraph({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
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
|
+
// })
|
|
141
135
|
this.schema = schema
|
|
142
136
|
// TODO generalize the `tableRefs` concept to allow finer-grained refs
|
|
143
137
|
this.tableRefs = {}
|
|
@@ -150,6 +144,9 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
150
144
|
const queriesSpan = otelTracer.startSpan('LiveStore:queries', {}, otelRootSpanContext)
|
|
151
145
|
const otelQueriesSpanContext = otel.trace.setSpan(otel.context.active(), queriesSpan)
|
|
152
146
|
|
|
147
|
+
this.graph = dbGraph
|
|
148
|
+
this.graph.context = { store: this, otelTracer, rootOtelContext: otelQueriesSpanContext }
|
|
149
|
+
|
|
153
150
|
this.otel = {
|
|
154
151
|
tracer: otelTracer,
|
|
155
152
|
applyEventsSpanContext: otelApplyEventsSpanContext,
|
|
@@ -194,398 +191,334 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
194
191
|
*
|
|
195
192
|
* NOTE The query is actually running (even if no one has subscribed to it yet) and will be kept up to date.
|
|
196
193
|
*/
|
|
197
|
-
querySQL = <TResult>(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
): LiveStoreSQLQuery<TResult> =>
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: 'GraphQL error' })
|
|
473
|
-
span.setAttribute('graphql.error', res.errors.join('\n'))
|
|
474
|
-
span.setAttribute('graphql.error-detail', JSON.stringify(res.errors))
|
|
475
|
-
console.error(`graphql error (${operationName})`, res.errors)
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
return { result: res.data as unknown as TResult, queriedTables: Array.from(context.queriedTables.values()) }
|
|
479
|
-
} finally {
|
|
480
|
-
span.end()
|
|
481
|
-
}
|
|
482
|
-
})
|
|
483
|
-
}
|
|
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
|
+
// }
|
|
484
469
|
|
|
485
470
|
/**
|
|
486
471
|
* Subscribe to the results of a query
|
|
487
472
|
* Returns a function to cancel the subscription.
|
|
488
473
|
*/
|
|
489
|
-
subscribe = <
|
|
490
|
-
query:
|
|
491
|
-
onNewValue: (value:
|
|
474
|
+
subscribe = <TResult>(
|
|
475
|
+
query: ILiveStoreQuery<TResult>,
|
|
476
|
+
onNewValue: (value: TResult) => void,
|
|
492
477
|
onSubsubscribe?: () => void,
|
|
493
|
-
options?: { label?: string } | undefined,
|
|
478
|
+
options?: { label?: string; otelContext?: otel.Context } | undefined,
|
|
494
479
|
): (() => void) =>
|
|
495
480
|
this.otel.tracer.startActiveSpan(
|
|
496
481
|
`LiveStore.subscribe`,
|
|
497
482
|
{ attributes: { label: options?.label } },
|
|
498
|
-
|
|
483
|
+
options?.otelContext ?? this.otel.queriesSpanContext,
|
|
499
484
|
(span) => {
|
|
500
485
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
501
486
|
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
const result = get(query.results$) as QueryResult<TQuery>
|
|
505
|
-
onNewValue(result)
|
|
506
|
-
},
|
|
507
|
-
{ label: `subscribe:${options?.label}` },
|
|
508
|
-
otelContext,
|
|
509
|
-
)
|
|
487
|
+
const label = `subscribe:${options?.label}`
|
|
488
|
+
const effect = this.graph.makeEffect((get) => onNewValue(get(query.results$)), { label })
|
|
510
489
|
|
|
511
|
-
|
|
490
|
+
effect.doEffect(otelContext)
|
|
512
491
|
|
|
513
492
|
const unsubscribe = () => {
|
|
514
493
|
try {
|
|
515
494
|
this.graph.destroy(effect)
|
|
516
|
-
|
|
495
|
+
this.activeQueries.delete(query as LiveStoreQuery)
|
|
517
496
|
onSubsubscribe?.()
|
|
518
497
|
} finally {
|
|
519
498
|
span.end()
|
|
520
499
|
}
|
|
521
500
|
}
|
|
522
501
|
|
|
523
|
-
|
|
502
|
+
this.activeQueries.add(query as LiveStoreQuery)
|
|
524
503
|
|
|
525
504
|
return unsubscribe
|
|
526
505
|
},
|
|
527
506
|
)
|
|
528
507
|
|
|
529
|
-
/**
|
|
530
|
-
* Any queries created in the callback will be destroyed when the callback is complete.
|
|
531
|
-
* Useful for temporarily creating reactive queries, which is an idempotent operation
|
|
532
|
-
* that can be safely called inside a React useMemo hook.
|
|
533
|
-
*/
|
|
534
|
-
inTempQueryContext = <TResult>(callback: () => TResult): TResult => {
|
|
535
|
-
this.temporaryQueries = new Set()
|
|
536
|
-
// TODO: consider errors / try/finally here?
|
|
537
|
-
const result = callback()
|
|
538
|
-
for (const query of this.temporaryQueries) {
|
|
539
|
-
this.destroyQuery(query)
|
|
540
|
-
}
|
|
541
|
-
this.temporaryQueries = undefined
|
|
542
|
-
return result
|
|
543
|
-
}
|
|
544
|
-
|
|
545
508
|
/**
|
|
546
509
|
* Destroys the entire store, including all queries and subscriptions.
|
|
547
510
|
*
|
|
548
511
|
* Currently only used when shutting down the app for debugging purposes (e.g. to close Otel spans).
|
|
549
512
|
*/
|
|
550
513
|
destroy = () => {
|
|
551
|
-
for (const query of this.activeQueries) {
|
|
552
|
-
this.destroyQuery(query)
|
|
553
|
-
}
|
|
554
|
-
|
|
555
514
|
Object.values(this.tableRefs).forEach((tableRef) => this.graph.destroy(tableRef))
|
|
556
515
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
const queriesSpan = otel.trace.getSpan(this.otel.queriesSpanContext)!
|
|
561
|
-
queriesSpan.end()
|
|
516
|
+
otel.trace.getSpan(this.otel.applyEventsSpanContext)!.end()
|
|
517
|
+
otel.trace.getSpan(this.otel.queriesSpanContext)!.end()
|
|
562
518
|
|
|
563
519
|
// TODO destroy active subscriptions
|
|
564
520
|
}
|
|
565
521
|
|
|
566
|
-
private destroyQuery = (query: LiveStoreQuery) => {
|
|
567
|
-
if (query._tag === 'sql') {
|
|
568
|
-
// results are downstream of query string, so will automatically be destroyed together
|
|
569
|
-
this.graph.destroy(query.queryString$)
|
|
570
|
-
} else {
|
|
571
|
-
this.graph.destroy(query.results$)
|
|
572
|
-
}
|
|
573
|
-
this.activeQueries.delete(query)
|
|
574
|
-
query.destroy()
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Clean up queries and downstream subscriptions associated with a component.
|
|
579
|
-
* This is critical to avoid memory leaks.
|
|
580
|
-
*/
|
|
581
|
-
unmountComponent = (componentKey: ComponentKey) => {
|
|
582
|
-
for (const query of this.activeQueries) {
|
|
583
|
-
if (query.componentKey === componentKey) {
|
|
584
|
-
this.destroyQuery(query)
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
522
|
/* Apply a single write event to the store, and refresh all queries in response */
|
|
590
523
|
applyEvent = <TEventType extends string & keyof LiveStoreActionDefinitionsTypes>(
|
|
591
524
|
eventType: TEventType,
|
|
@@ -614,20 +547,25 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
614
547
|
tablesToUpdate.push([tableRef!, null])
|
|
615
548
|
}
|
|
616
549
|
|
|
550
|
+
const debugRefreshReason = {
|
|
551
|
+
_tag: 'applyEvent' as const,
|
|
552
|
+
event: { type: eventType, args },
|
|
553
|
+
writeTables: [...writeTables],
|
|
554
|
+
}
|
|
555
|
+
|
|
617
556
|
// Update all table refs together in a batch, to only trigger one reactive update
|
|
618
|
-
this.graph.setRefs(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
)
|
|
557
|
+
this.graph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext })
|
|
558
|
+
|
|
559
|
+
if (skipRefresh === false) {
|
|
560
|
+
// TODO update the graph
|
|
561
|
+
// this.graph.refresh(
|
|
562
|
+
// {
|
|
563
|
+
// otelHint: 'applyEvents',
|
|
564
|
+
// debugRefreshReason,
|
|
565
|
+
// },
|
|
566
|
+
// otelContext,
|
|
567
|
+
// )
|
|
568
|
+
}
|
|
631
569
|
} catch (e: any) {
|
|
632
570
|
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
|
|
633
571
|
|
|
@@ -710,20 +648,18 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
710
648
|
tablesToUpdate.push([tableRef!, null])
|
|
711
649
|
}
|
|
712
650
|
|
|
651
|
+
const debugRefreshReason = {
|
|
652
|
+
_tag: 'applyEvents' as const,
|
|
653
|
+
events: [...events].map((e) => ({ type: e.eventType, args: e.args })),
|
|
654
|
+
writeTables: [...writeTables],
|
|
655
|
+
}
|
|
713
656
|
// Update all table refs together in a batch, to only trigger one reactive update
|
|
714
|
-
this.graph.setRefs(
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
_tag: 'applyEvents',
|
|
721
|
-
events: [...events].map((e) => ({ type: e.eventType, args: e.args })),
|
|
722
|
-
writeTables: [...writeTables],
|
|
723
|
-
},
|
|
724
|
-
},
|
|
725
|
-
otelContext,
|
|
726
|
-
)
|
|
657
|
+
this.graph.setRefs(tablesToUpdate, { debugRefreshReason, otelContext })
|
|
658
|
+
|
|
659
|
+
if (skipRefresh === false) {
|
|
660
|
+
// TODO update the graph
|
|
661
|
+
// this.graph.refresh({ debugRefreshReason, otelHint: 'applyEvents' }, otelContext)
|
|
662
|
+
}
|
|
727
663
|
} catch (e: any) {
|
|
728
664
|
span.setStatus({ code: otel.SpanStatusCode.ERROR, message: e.toString() })
|
|
729
665
|
} finally {
|
|
@@ -746,20 +682,14 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
746
682
|
{ attributes: { 'livestore.manualRefreshLabel': label } },
|
|
747
683
|
this.otel.applyEventsSpanContext,
|
|
748
684
|
(span) => {
|
|
749
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
750
|
-
|
|
685
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
686
|
+
// TODO update the graph
|
|
687
|
+
// this.graph.refresh({ otelHint: 'manualRefresh', debugRefreshReason: { _tag: 'manualRefresh' } }, otelContext)
|
|
751
688
|
span.end()
|
|
752
689
|
},
|
|
753
690
|
)
|
|
754
691
|
}
|
|
755
692
|
|
|
756
|
-
// TODO get rid of this as part of new query definition approach https://www.notion.so/schickling/New-query-definition-approach-1097a78ef0e9495bac25f90417374756?pvs=4
|
|
757
|
-
runOnce = <TQueryDef extends QueryDefinition>(queryDef: TQueryDef): QueryResult<ReturnType<TQueryDef>> => {
|
|
758
|
-
return this.inTempQueryContext(() => {
|
|
759
|
-
return queryDef(this).results$.result
|
|
760
|
-
})
|
|
761
|
-
}
|
|
762
|
-
|
|
763
693
|
/**
|
|
764
694
|
* Apply an event to the store.
|
|
765
695
|
* Returns the tables that were affected by the event.
|
|
@@ -853,7 +783,7 @@ export class Store<TGraphQLContext extends BaseGraphQLContext = BaseGraphQLConte
|
|
|
853
783
|
* This should only be used for framework-internal purposes;
|
|
854
784
|
* all app writes should go through applyEvent.
|
|
855
785
|
*/
|
|
856
|
-
execute =
|
|
786
|
+
execute = (query: string, params: ParamsObject = {}, writeTables?: string[]) => {
|
|
857
787
|
this.inMemoryDB.execute(query, prepareBindValues(params, query), writeTables)
|
|
858
788
|
|
|
859
789
|
if (this.storage !== undefined) {
|