@livestore/livestore 0.0.0-snapshot-6788a4cf00171a177a3a1e59cefc10f5bad71049 → 0.0.0-snapshot-783e1b05c962636b7bb28308b34f6cd9baf596e1
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/live-queries/db-query.test.js +0 -30
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js.map +1 -1
- package/dist/store/store-types.d.ts +0 -9
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store.d.ts +24 -20
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +48 -75
- package/dist/store/store.js.map +1 -1
- package/package.json +5 -5
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +0 -87
- package/src/live-queries/db-query.test.ts +0 -47
- package/src/mod.ts +1 -7
- package/src/store/store-types.ts +0 -10
- package/src/store/store.ts +78 -112
|
@@ -1,92 +1,5 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`otel > QueryBuilder subscription - async iterator 1`] = `
|
|
4
|
-
{
|
|
5
|
-
"_name": "createStore",
|
|
6
|
-
"attributes": {
|
|
7
|
-
"debugInstanceId": "test",
|
|
8
|
-
"storeId": "default",
|
|
9
|
-
},
|
|
10
|
-
"children": [
|
|
11
|
-
{
|
|
12
|
-
"_name": "livestore.in-memory-db:execute",
|
|
13
|
-
"attributes": {
|
|
14
|
-
"sql.query": "
|
|
15
|
-
PRAGMA page_size=32768;
|
|
16
|
-
PRAGMA cache_size=10000;
|
|
17
|
-
PRAGMA synchronous='OFF';
|
|
18
|
-
PRAGMA temp_store='MEMORY';
|
|
19
|
-
PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
|
|
20
|
-
",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"_name": "@livestore/common:LeaderSyncProcessor:push",
|
|
25
|
-
"attributes": {
|
|
26
|
-
"batch": "undefined",
|
|
27
|
-
"batchSize": 1,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"_name": "client-session-sync-processor:pull",
|
|
32
|
-
"attributes": {
|
|
33
|
-
"code.stacktrace": "<STACKTRACE>",
|
|
34
|
-
"span.label": "⚠︎ Interrupted",
|
|
35
|
-
"status.interrupted": true,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"_name": "LiveStore:sync",
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"_name": "LiveStore:commits",
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"_name": "LiveStore:queries",
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
}
|
|
49
|
-
`;
|
|
50
|
-
|
|
51
|
-
exports[`otel > QueryBuilder subscription - async iterator 2`] = `
|
|
52
|
-
[
|
|
53
|
-
{
|
|
54
|
-
"_name": "LiveStore:commit",
|
|
55
|
-
"attributes": {
|
|
56
|
-
"livestore.eventTags": "[
|
|
57
|
-
"todo.created"
|
|
58
|
-
]",
|
|
59
|
-
"livestore.eventsCount": 1,
|
|
60
|
-
},
|
|
61
|
-
"children": [
|
|
62
|
-
{
|
|
63
|
-
"_name": "client-session-sync-processor:push",
|
|
64
|
-
"attributes": {
|
|
65
|
-
"batchSize": 1,
|
|
66
|
-
"eventCounts": "{
|
|
67
|
-
"todo.created": 1
|
|
68
|
-
}",
|
|
69
|
-
"mergeResultTag": "advance",
|
|
70
|
-
},
|
|
71
|
-
"children": [
|
|
72
|
-
{
|
|
73
|
-
"_name": "client-session-sync-processor:materialize-event",
|
|
74
|
-
"children": [
|
|
75
|
-
{
|
|
76
|
-
"_name": "livestore.in-memory-db:execute",
|
|
77
|
-
"attributes": {
|
|
78
|
-
"sql.query": "INSERT INTO 'todos' (id, text, completed) VALUES (?, ?, ?)",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
]
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
3
|
exports[`otel > QueryBuilder subscription - basic functionality 1`] = `
|
|
91
4
|
{
|
|
92
5
|
"_name": "createStore",
|
|
@@ -289,53 +289,6 @@ Vitest.describe('otel', () => {
|
|
|
289
289
|
),
|
|
290
290
|
)
|
|
291
291
|
|
|
292
|
-
Vitest.scopedLive('QueryBuilder subscription - async iterator', () =>
|
|
293
|
-
Effect.gen(function* () {
|
|
294
|
-
const { store, exporter, span, provider } = yield* makeQuery
|
|
295
|
-
|
|
296
|
-
const defaultTodo = { id: '', text: '', completed: false }
|
|
297
|
-
|
|
298
|
-
const queryBuilder = tables.todos
|
|
299
|
-
.where({ completed: false })
|
|
300
|
-
.first({ behaviour: 'fallback', fallback: () => defaultTodo })
|
|
301
|
-
|
|
302
|
-
yield* Effect.promise(async () => {
|
|
303
|
-
const iterator = store.subscribe(queryBuilder)
|
|
304
|
-
|
|
305
|
-
const initial = await iterator.next()
|
|
306
|
-
expect(initial.done).toBe(false)
|
|
307
|
-
expect(initial.value).toMatchObject(defaultTodo)
|
|
308
|
-
|
|
309
|
-
store.commit(events.todoCreated({ id: 't-async', text: 'write tests', completed: false }))
|
|
310
|
-
|
|
311
|
-
const update = await iterator.next()
|
|
312
|
-
expect(update.done).toBe(false)
|
|
313
|
-
expect(update.value).toMatchObject({
|
|
314
|
-
id: 't-async',
|
|
315
|
-
text: 'write tests',
|
|
316
|
-
completed: false,
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
const doneResult = await iterator.return()
|
|
320
|
-
expect(doneResult.done).toBe(true)
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
span.end()
|
|
324
|
-
|
|
325
|
-
return { exporter, provider }
|
|
326
|
-
}).pipe(
|
|
327
|
-
Effect.scoped,
|
|
328
|
-
Effect.tap(({ exporter, provider }) =>
|
|
329
|
-
Effect.promise(async () => {
|
|
330
|
-
await provider.forceFlush()
|
|
331
|
-
expect(getSimplifiedRootSpan(exporter, 'createStore', mapAttributes)).toMatchSnapshot()
|
|
332
|
-
expect(getAllSimplifiedRootSpans(exporter, 'LiveStore:commit', mapAttributes)).toMatchSnapshot()
|
|
333
|
-
await provider.shutdown()
|
|
334
|
-
}),
|
|
335
|
-
),
|
|
336
|
-
),
|
|
337
|
-
)
|
|
338
|
-
|
|
339
292
|
Vitest.scopedLive('QueryBuilder subscription - direct table subscription', () =>
|
|
340
293
|
Effect.gen(function* () {
|
|
341
294
|
const { store, exporter, span, provider } = yield* makeQuery
|
package/src/mod.ts
CHANGED
|
@@ -37,13 +37,7 @@ export {
|
|
|
37
37
|
export { emptyDebugInfo, SqliteDbWrapper } from './SqliteDbWrapper.ts'
|
|
38
38
|
export { type CreateStoreOptions, createStore, createStorePromise } from './store/create-store.ts'
|
|
39
39
|
export { Store } from './store/store.ts'
|
|
40
|
-
export type {
|
|
41
|
-
OtelOptions,
|
|
42
|
-
QueryDebugInfo,
|
|
43
|
-
RefreshReason,
|
|
44
|
-
SubscribeOptions,
|
|
45
|
-
Unsubscribe,
|
|
46
|
-
} from './store/store-types.ts'
|
|
40
|
+
export type { OtelOptions, QueryDebugInfo, RefreshReason, Unsubscribe } from './store/store-types.ts'
|
|
47
41
|
export {
|
|
48
42
|
type LiveStoreContext,
|
|
49
43
|
type LiveStoreContextRunning,
|
package/src/store/store-types.ts
CHANGED
|
@@ -14,7 +14,6 @@ import type { Effect, Runtime, Scope } from '@livestore/utils/effect'
|
|
|
14
14
|
import { Deferred } from '@livestore/utils/effect'
|
|
15
15
|
import type * as otel from '@opentelemetry/api'
|
|
16
16
|
|
|
17
|
-
import type { LiveQuery } from '../live-queries/base-class.ts'
|
|
18
17
|
import type { DebugRefreshReasonBase } from '../reactive.ts'
|
|
19
18
|
import type { StackInfo } from '../utils/stack-info.ts'
|
|
20
19
|
import type { Store } from './store.ts'
|
|
@@ -136,12 +135,3 @@ export type StoreEventsOptions<TSchema extends LiveStoreSchema> = {
|
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
export type Unsubscribe = () => void
|
|
139
|
-
|
|
140
|
-
export type SubscribeOptions<TResult> = {
|
|
141
|
-
onSubscribe?: (query$: LiveQuery<TResult>) => void
|
|
142
|
-
onUnsubsubscribe?: () => void
|
|
143
|
-
label?: string
|
|
144
|
-
skipInitialRun?: boolean
|
|
145
|
-
otelContext?: otel.Context
|
|
146
|
-
stackInfo?: StackInfo
|
|
147
|
-
}
|
package/src/store/store.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
UnexpectedError,
|
|
21
21
|
} from '@livestore/common'
|
|
22
22
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
23
|
-
import {
|
|
23
|
+
import { LiveStoreEvent, resolveEventDef, SystemTables } from '@livestore/common/schema'
|
|
24
24
|
import { assertNever, isDevEnv, notYetImplemented, omitUndefineds, shouldNeverHappen } from '@livestore/utils'
|
|
25
25
|
import type { Scope } from '@livestore/utils/effect'
|
|
26
26
|
import {
|
|
@@ -52,21 +52,16 @@ import type { Ref } from '../reactive.ts'
|
|
|
52
52
|
import { SqliteDbWrapper } from '../SqliteDbWrapper.ts'
|
|
53
53
|
import { ReferenceCountedSet } from '../utils/data-structures.ts'
|
|
54
54
|
import { downloadBlob, exposeDebugUtils } from '../utils/dev.ts'
|
|
55
|
+
import type { StackInfo } from '../utils/stack-info.ts'
|
|
55
56
|
import type {
|
|
56
57
|
RefreshReason,
|
|
57
58
|
StoreCommitOptions,
|
|
58
59
|
StoreEventsOptions,
|
|
59
60
|
StoreOptions,
|
|
60
61
|
StoreOtel,
|
|
61
|
-
SubscribeOptions,
|
|
62
62
|
Unsubscribe,
|
|
63
63
|
} from './store-types.ts'
|
|
64
64
|
|
|
65
|
-
type SubscribeQuery<TResult> =
|
|
66
|
-
| LiveQueryDef<TResult, 'def' | 'signal-def'>
|
|
67
|
-
| LiveQuery<TResult>
|
|
68
|
-
| QueryBuilder<TResult, any, any>
|
|
69
|
-
|
|
70
65
|
if (isDevEnv()) {
|
|
71
66
|
exposeDebugUtils()
|
|
72
67
|
}
|
|
@@ -354,125 +349,98 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
354
349
|
}
|
|
355
350
|
|
|
356
351
|
/**
|
|
357
|
-
* Subscribe to the results of a query
|
|
358
|
-
*
|
|
359
|
-
* - When providing an `onUpdate` callback it returns an {@link Unsubscribe} function.
|
|
360
|
-
* - Without a callback it returns an {@link AsyncIterable} that yields query results.
|
|
352
|
+
* Subscribe to the results of a query
|
|
353
|
+
* Returns a function to cancel the subscription.
|
|
361
354
|
*
|
|
362
355
|
* @example
|
|
363
356
|
* ```ts
|
|
364
357
|
* const unsubscribe = store.subscribe(query$, (result) => console.log(result))
|
|
365
358
|
* ```
|
|
366
|
-
*
|
|
367
|
-
* @example
|
|
368
|
-
* ```ts
|
|
369
|
-
* for await (const result of store.subscribe(query$)) {
|
|
370
|
-
* console.log(result)
|
|
371
|
-
* }
|
|
372
|
-
* ```
|
|
373
359
|
*/
|
|
374
|
-
subscribe
|
|
375
|
-
<TResult>
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
{ attributes: { label: options?.label, queryLabel: isQueryBuilder(query) ? query.toString() : query.label } },
|
|
395
|
-
options?.otelContext ?? this.otel.queriesSpanContext,
|
|
396
|
-
(span) => {
|
|
397
|
-
// console.debug('store sub', query$.id, query$.label)
|
|
398
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
399
|
-
|
|
400
|
-
const queryRcRef = isQueryBuilder(query)
|
|
401
|
-
? queryDb(query).make(this.reactivityGraph.context!)
|
|
402
|
-
: query._tag === 'def' || query._tag === 'signal-def'
|
|
403
|
-
? query.make(this.reactivityGraph.context!)
|
|
404
|
-
: {
|
|
405
|
-
value: query as LiveQuery<TResult>,
|
|
406
|
-
deref: () => {},
|
|
407
|
-
}
|
|
408
|
-
const query$ = queryRcRef.value
|
|
360
|
+
subscribe = <TResult>(
|
|
361
|
+
query: LiveQueryDef<TResult, 'def' | 'signal-def'> | LiveQuery<TResult> | QueryBuilder<TResult, any, any>,
|
|
362
|
+
/** Called when the query result has changed */
|
|
363
|
+
onUpdate: (value: TResult) => void,
|
|
364
|
+
options?: {
|
|
365
|
+
onSubscribe?: (query$: LiveQuery<TResult>) => void
|
|
366
|
+
/** Gets called after the query subscription has been removed */
|
|
367
|
+
onUnsubsubscribe?: () => void
|
|
368
|
+
label?: string
|
|
369
|
+
/**
|
|
370
|
+
* Skips the initial `onUpdate` callback
|
|
371
|
+
* @default false
|
|
372
|
+
*/
|
|
373
|
+
skipInitialRun?: boolean
|
|
374
|
+
otelContext?: otel.Context
|
|
375
|
+
/** If provided, the stack info will be added to the `activeSubscriptions` set of the query */
|
|
376
|
+
stackInfo?: StackInfo
|
|
377
|
+
},
|
|
378
|
+
): Unsubscribe => {
|
|
379
|
+
this.checkShutdown('subscribe')
|
|
409
380
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
381
|
+
return this.otel.tracer.startActiveSpan(
|
|
382
|
+
`LiveStore.subscribe`,
|
|
383
|
+
{ attributes: { label: options?.label, queryLabel: isQueryBuilder(query) ? query.toString() : query.label } },
|
|
384
|
+
options?.otelContext ?? this.otel.queriesSpanContext,
|
|
385
|
+
(span) => {
|
|
386
|
+
// console.debug('store sub', query$.id, query$.label)
|
|
387
|
+
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
415
388
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
389
|
+
const queryRcRef = isQueryBuilder(query)
|
|
390
|
+
? queryDb(query).make(this.reactivityGraph.context!)
|
|
391
|
+
: query._tag === 'def' || query._tag === 'signal-def'
|
|
392
|
+
? query.make(this.reactivityGraph.context!)
|
|
393
|
+
: {
|
|
394
|
+
value: query as LiveQuery<TResult>,
|
|
395
|
+
deref: () => {},
|
|
396
|
+
}
|
|
397
|
+
const query$ = queryRcRef.value
|
|
419
398
|
|
|
420
|
-
|
|
399
|
+
const label = `subscribe:${options?.label}`
|
|
400
|
+
const effect = this.reactivityGraph.makeEffect(
|
|
401
|
+
(get, _otelContext, debugRefreshReason) => onUpdate(get(query$.results$, otelContext, debugRefreshReason)),
|
|
402
|
+
{ label },
|
|
403
|
+
)
|
|
421
404
|
|
|
422
|
-
|
|
405
|
+
if (options?.stackInfo) {
|
|
406
|
+
query$.activeSubscriptions.add(options.stackInfo)
|
|
407
|
+
}
|
|
423
408
|
|
|
424
|
-
|
|
425
|
-
if (options?.skipInitialRun !== true && !query$.isDestroyed) {
|
|
426
|
-
effect.doEffect(otelContext, {
|
|
427
|
-
_tag: 'subscribe.initial',
|
|
428
|
-
label: `subscribe-initial-run:${options?.label}`,
|
|
429
|
-
})
|
|
430
|
-
}
|
|
409
|
+
options?.onSubscribe?.(query$)
|
|
431
410
|
|
|
432
|
-
|
|
433
|
-
// console.debug('store unsub', query$.id, query$.label)
|
|
434
|
-
try {
|
|
435
|
-
this.reactivityGraph.destroyNode(effect)
|
|
436
|
-
this.activeQueries.remove(query$ as LiveQuery<TResult>)
|
|
411
|
+
this.activeQueries.add(query$ as LiveQuery<TResult>)
|
|
437
412
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
413
|
+
// Running effect right away to get initial value (unless `skipInitialRun` is set)
|
|
414
|
+
if (options?.skipInitialRun !== true && !query$.isDestroyed) {
|
|
415
|
+
effect.doEffect(otelContext, { _tag: 'subscribe.initial', label: `subscribe-initial-run:${options?.label}` })
|
|
416
|
+
}
|
|
441
417
|
|
|
442
|
-
|
|
418
|
+
const unsubscribe = () => {
|
|
419
|
+
// console.debug('store unsub', query$.id, query$.label)
|
|
420
|
+
try {
|
|
421
|
+
this.reactivityGraph.destroyNode(effect)
|
|
422
|
+
this.activeQueries.remove(query$ as LiveQuery<TResult>)
|
|
443
423
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
span.end()
|
|
424
|
+
if (options?.stackInfo) {
|
|
425
|
+
query$.activeSubscriptions.delete(options.stackInfo)
|
|
447
426
|
}
|
|
448
|
-
}
|
|
449
427
|
|
|
450
|
-
|
|
451
|
-
},
|
|
452
|
-
)
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return this.subscribeAsAsyncIterator(query, onUpdateOrOptions)
|
|
456
|
-
}
|
|
428
|
+
queryRcRef.deref()
|
|
457
429
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const iterator = Stream.toAsyncIterable(this.subscribeStream(query, options))[Symbol.asyncIterator]()
|
|
430
|
+
options?.onUnsubsubscribe?.()
|
|
431
|
+
} finally {
|
|
432
|
+
span.end()
|
|
433
|
+
}
|
|
434
|
+
}
|
|
465
435
|
|
|
466
|
-
|
|
467
|
-
[Symbol.asyncIterator]() {
|
|
468
|
-
return this
|
|
436
|
+
return unsubscribe
|
|
469
437
|
},
|
|
470
|
-
|
|
438
|
+
)
|
|
471
439
|
}
|
|
472
440
|
|
|
473
441
|
subscribeStream = <TResult>(
|
|
474
|
-
query
|
|
475
|
-
options?:
|
|
442
|
+
query$: LiveQueryDef<TResult>,
|
|
443
|
+
options?: { label?: string; skipInitialRun?: boolean } | undefined,
|
|
476
444
|
): Stream.Stream<TResult> =>
|
|
477
445
|
Stream.asyncPush<TResult>((emit) =>
|
|
478
446
|
Effect.gen(this, function* () {
|
|
@@ -483,10 +451,11 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
483
451
|
|
|
484
452
|
yield* Effect.acquireRelease(
|
|
485
453
|
Effect.sync(() =>
|
|
486
|
-
this.subscribe(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
454
|
+
this.subscribe(
|
|
455
|
+
query$,
|
|
456
|
+
(result) => emit.single(result),
|
|
457
|
+
omitUndefineds({ otelContext, label: options?.label }),
|
|
458
|
+
),
|
|
490
459
|
),
|
|
491
460
|
(unsub) => Effect.sync(() => unsub()),
|
|
492
461
|
)
|
|
@@ -886,14 +855,11 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
886
855
|
Effect.gen(this, function* () {
|
|
887
856
|
const session = yield* this.syncProcessor.syncState
|
|
888
857
|
yield* Effect.log(
|
|
889
|
-
`Session sync state: ${
|
|
858
|
+
`Session sync state: ${session.localHead} (upstream: ${session.upstreamHead})`,
|
|
890
859
|
session.toJSON(),
|
|
891
860
|
)
|
|
892
861
|
const leader = yield* this.clientSession.leaderThread.getSyncState
|
|
893
|
-
yield* Effect.log(
|
|
894
|
-
`Leader sync state: ${EventSequenceNumber.toString(leader.localHead)} (upstream: ${EventSequenceNumber.toString(leader.upstreamHead)})`,
|
|
895
|
-
leader.toJSON(),
|
|
896
|
-
)
|
|
862
|
+
yield* Effect.log(`Leader sync state: ${leader.localHead} (upstream: ${leader.upstreamHead})`, leader.toJSON())
|
|
897
863
|
}).pipe(this.runEffectFork)
|
|
898
864
|
},
|
|
899
865
|
|