@livestore/livestore 0.3.2-dev.1 → 0.3.2-dev.11
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/base-class.d.ts +5 -0
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +1 -1
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +7 -0
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/db-query.d.ts.map +1 -1
- package/dist/live-queries/db-query.js +12 -2
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/db-query.test.js +8 -10
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/live-queries/signal.d.ts.map +1 -1
- package/dist/live-queries/signal.js +7 -0
- package/dist/live-queries/signal.js.map +1 -1
- package/dist/mod.d.ts +2 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -0
- package/dist/mod.js.map +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +2 -0
- package/dist/reactive.js.map +1 -1
- package/dist/store/create-store.d.ts +5 -7
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +4 -4
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/store-shutdown.test.d.ts +2 -0
- package/dist/store/store-shutdown.test.d.ts.map +1 -0
- package/dist/store/store-shutdown.test.js +103 -0
- package/dist/store/store-shutdown.test.js.map +1 -0
- package/dist/store/store-types.d.ts +4 -4
- 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 +11 -2
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +76 -43
- package/dist/store/store.js.map +1 -1
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +14 -8
- package/dist/utils/dev.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +33 -2
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/fixture.js +16 -3
- package/dist/utils/tests/fixture.js.map +1 -1
- package/package.json +12 -38
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +14 -14
- package/src/live-queries/base-class.ts +5 -1
- package/src/live-queries/computed.ts +7 -0
- package/src/live-queries/db-query.test.ts +8 -10
- package/src/live-queries/db-query.ts +12 -3
- package/src/live-queries/signal.ts +7 -0
- package/src/mod.ts +2 -1
- package/src/reactive.ts +2 -0
- package/src/store/create-store.ts +18 -16
- package/src/store/store-types.ts +9 -5
- package/src/store/store.ts +67 -8
- package/src/utils/dev.ts +14 -7
- package/src/utils/tests/fixture.ts +18 -3
package/src/store/store.ts
CHANGED
@@ -22,7 +22,18 @@ import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
22
22
|
import { getEventDef, LiveStoreEvent, SystemTables } from '@livestore/common/schema'
|
23
23
|
import { assertNever, isDevEnv, notYetImplemented } from '@livestore/utils'
|
24
24
|
import type { Scope } from '@livestore/utils/effect'
|
25
|
-
import {
|
25
|
+
import {
|
26
|
+
Cause,
|
27
|
+
Effect,
|
28
|
+
Exit,
|
29
|
+
Fiber,
|
30
|
+
Inspectable,
|
31
|
+
Option,
|
32
|
+
OtelTracer,
|
33
|
+
Runtime,
|
34
|
+
Schema,
|
35
|
+
Stream,
|
36
|
+
} from '@livestore/utils/effect'
|
26
37
|
import { nanoid } from '@livestore/utils/nanoid'
|
27
38
|
import * as otel from '@opentelemetry/api'
|
28
39
|
|
@@ -54,7 +65,7 @@ if (isDevEnv()) {
|
|
54
65
|
exposeDebugUtils()
|
55
66
|
}
|
56
67
|
|
57
|
-
export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext = {}> extends Inspectable.Class {
|
68
|
+
export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}> extends Inspectable.Class {
|
58
69
|
readonly storeId: string
|
59
70
|
reactivityGraph: ReactivityGraph
|
60
71
|
sqliteDbWrapper: SqliteDbWrapper
|
@@ -68,6 +79,9 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
68
79
|
*/
|
69
80
|
tableRefs: { [key: string]: Ref<null, ReactivityGraphContext, RefreshReason> }
|
70
81
|
|
82
|
+
/** Tracks whether the store has been shut down */
|
83
|
+
private isShutdown = false
|
84
|
+
|
71
85
|
private effectContext: {
|
72
86
|
runtime: Runtime.Runtime<Scope.Scope>
|
73
87
|
lifetimeScope: Scope.Scope
|
@@ -282,6 +296,15 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
282
296
|
return this.clientSession.clientId
|
283
297
|
}
|
284
298
|
|
299
|
+
private checkShutdown = (operation: string): void => {
|
300
|
+
if (this.isShutdown) {
|
301
|
+
throw new UnexpectedError({
|
302
|
+
cause: `Store has been shut down (while performing "${operation}").`,
|
303
|
+
note: `You cannot perform this operation after the store has been shut down.`,
|
304
|
+
})
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
285
308
|
/**
|
286
309
|
* Subscribe to the results of a query
|
287
310
|
* Returns a function to cancel the subscription.
|
@@ -309,8 +332,10 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
309
332
|
/** If provided, the stack info will be added to the `activeSubscriptions` set of the query */
|
310
333
|
stackInfo?: StackInfo
|
311
334
|
},
|
312
|
-
): Unsubscribe =>
|
313
|
-
this.
|
335
|
+
): Unsubscribe => {
|
336
|
+
this.checkShutdown('subscribe')
|
337
|
+
|
338
|
+
return this.otel.tracer.startActiveSpan(
|
314
339
|
`LiveStore.subscribe`,
|
315
340
|
{ attributes: { label: options?.label, queryLabel: isQueryBuilder(query) ? query.toString() : query.label } },
|
316
341
|
options?.otelContext ?? this.otel.queriesSpanContext,
|
@@ -369,6 +394,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
369
394
|
return unsubscribe
|
370
395
|
},
|
371
396
|
)
|
397
|
+
}
|
372
398
|
|
373
399
|
subscribeStream = <TResult>(
|
374
400
|
query$: LiveQueryDef<TResult>,
|
@@ -417,6 +443,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
417
443
|
| { query: string; bindValues: Bindable; schema?: Schema.Schema<TResult> },
|
418
444
|
options?: { otelContext?: otel.Context; debugRefreshReason?: RefreshReason },
|
419
445
|
): TResult => {
|
446
|
+
this.checkShutdown('query')
|
447
|
+
|
420
448
|
if (typeof query === 'object' && 'query' in query && 'bindValues' in query) {
|
421
449
|
const res = this.sqliteDbWrapper.cachedSelect(query.query, prepareBindValues(query.bindValues, query.query), {
|
422
450
|
otelContext: options?.otelContext,
|
@@ -438,6 +466,12 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
438
466
|
|
439
467
|
const sqlRes = query.asSql()
|
440
468
|
const schema = getResultSchema(query)
|
469
|
+
|
470
|
+
// Replace SessionIdSymbol in bind values before executing the query
|
471
|
+
if (sqlRes.bindValues) {
|
472
|
+
replaceSessionIdSymbol(sqlRes.bindValues, this.clientSession.sessionId)
|
473
|
+
}
|
474
|
+
|
441
475
|
const rawRes = this.sqliteDbWrapper.cachedSelect(sqlRes.query, sqlRes.bindValues as any as PreparedBindValues, {
|
442
476
|
otelContext: options?.otelContext,
|
443
477
|
queriedTables: new Set([query[QueryBuilderAstSymbol].tableDef.sqliteDef.name]),
|
@@ -473,6 +507,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
473
507
|
* ```
|
474
508
|
*/
|
475
509
|
setSignal = <T>(signalDef: SignalDef<T>, value: T | ((prev: T) => T)): void => {
|
510
|
+
this.checkShutdown('setSignal')
|
511
|
+
|
476
512
|
const signalRef = signalDef.make(this.reactivityGraph.context!)
|
477
513
|
const newValue: T = typeof value === 'function' ? (value as any)(signalRef.value.get()) : value
|
478
514
|
signalRef.value.set(newValue)
|
@@ -557,6 +593,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
557
593
|
) => void,
|
558
594
|
): void
|
559
595
|
} = (firstEventOrTxnFnOrOptions: any, ...restEvents: any[]) => {
|
596
|
+
this.checkShutdown('commit')
|
597
|
+
|
560
598
|
const { events, options } = this.getCommitArgs(firstEventOrTxnFnOrOptions, restEvents)
|
561
599
|
|
562
600
|
for (const event of events) {
|
@@ -660,10 +698,14 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
660
698
|
* ```
|
661
699
|
*/
|
662
700
|
events = (_options?: StoreEventsOptions<TSchema>): AsyncIterable<LiveStoreEvent.ForSchema<TSchema>> => {
|
701
|
+
this.checkShutdown('events')
|
702
|
+
|
663
703
|
return notYetImplemented(`store.events() is not yet implemented but planned soon`)
|
664
704
|
}
|
665
705
|
|
666
706
|
eventsStream = (_options?: StoreEventsOptions<TSchema>): Stream.Stream<LiveStoreEvent.ForSchema<TSchema>> => {
|
707
|
+
this.checkShutdown('eventsStream')
|
708
|
+
|
667
709
|
return notYetImplemented(`store.eventsStream() is not yet implemented but planned soon`)
|
668
710
|
}
|
669
711
|
|
@@ -672,6 +714,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
672
714
|
* We might need a better solution for this. Let's see.
|
673
715
|
*/
|
674
716
|
manualRefresh = (options?: { label?: string }) => {
|
717
|
+
this.checkShutdown('manualRefresh')
|
718
|
+
|
675
719
|
const { label } = options ?? {}
|
676
720
|
this.otel.tracer.startActiveSpan(
|
677
721
|
'LiveStore:manualRefresh',
|
@@ -690,10 +734,25 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
690
734
|
*
|
691
735
|
* This is called automatically when the store was created using the React or Effect API.
|
692
736
|
*/
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
737
|
+
shutdownPromise = async (cause?: UnexpectedError) => {
|
738
|
+
this.checkShutdown('shutdownPromise')
|
739
|
+
|
740
|
+
this.isShutdown = true
|
741
|
+
await this.shutdown(cause ? Cause.fail(cause) : undefined).pipe(this.runEffectFork, Fiber.join, Effect.runPromise)
|
742
|
+
}
|
743
|
+
|
744
|
+
/**
|
745
|
+
* Shuts down the store and closes the client session.
|
746
|
+
*
|
747
|
+
* This is called automatically when the store was created using the React or Effect API.
|
748
|
+
*/
|
749
|
+
shutdown = (cause?: Cause.Cause<UnexpectedError>): Effect.Effect<void> => {
|
750
|
+
this.checkShutdown('shutdown')
|
751
|
+
|
752
|
+
this.isShutdown = true
|
753
|
+
return this.clientSession.shutdown(
|
754
|
+
cause ? Exit.failCause(cause) : Exit.succeed(IntentionalShutdownCause.make({ reason: 'manual' })),
|
755
|
+
)
|
697
756
|
}
|
698
757
|
|
699
758
|
/**
|
package/src/utils/dev.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import type { SqliteDb } from '@livestore/common'
|
2
|
+
import { prettyBytes } from '@livestore/utils'
|
2
3
|
import { Effect } from '@livestore/utils/effect'
|
3
4
|
|
4
5
|
export const downloadBlob = (
|
@@ -26,11 +27,17 @@ export const downloadURL = (data: string, fileName: string) => {
|
|
26
27
|
}
|
27
28
|
|
28
29
|
export const exposeDebugUtils = () => {
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
globalThis.__debugLiveStoreUtils = {
|
31
|
+
downloadBlob,
|
32
|
+
runSync: (effect: Effect.Effect<any, any, never>) => Effect.runSync(effect),
|
33
|
+
runFork: (effect: Effect.Effect<any, any, never>) => Effect.runFork(effect),
|
34
|
+
dumpDb: (db: SqliteDb) => {
|
35
|
+
const tables = db.select<{ name: string }>(`SELECT name FROM sqlite_master WHERE type='table'`)
|
36
|
+
for (const table of tables) {
|
37
|
+
const rows = db.select<any>(`SELECT * FROM ${table.name}`)
|
38
|
+
console.log(`Table: ${table.name} (${prettyBytes(table.name.length)}, ${rows.length} rows)`)
|
39
|
+
console.table(rows)
|
40
|
+
}
|
41
|
+
},
|
35
42
|
}
|
36
43
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { makeInMemoryAdapter } from '@livestore/adapter-web'
|
2
2
|
import { provideOtel } from '@livestore/common'
|
3
|
-
import { createStore, makeSchema, State } from '@livestore/livestore'
|
3
|
+
import { createStore, Events, makeSchema, State } from '@livestore/livestore'
|
4
4
|
import { Effect, Schema } from '@livestore/utils/effect'
|
5
5
|
import type * as otel from '@opentelemetry/api'
|
6
6
|
|
@@ -37,8 +37,23 @@ export const app = State.SQLite.clientDocument({
|
|
37
37
|
|
38
38
|
export const tables = { todos, app }
|
39
39
|
|
40
|
-
export const
|
41
|
-
|
40
|
+
export const events = {
|
41
|
+
todoCreated: Events.synced({
|
42
|
+
name: 'todo.created',
|
43
|
+
schema: Schema.Struct({
|
44
|
+
id: Schema.String,
|
45
|
+
text: Schema.String,
|
46
|
+
completed: Schema.Boolean,
|
47
|
+
}),
|
48
|
+
}),
|
49
|
+
}
|
50
|
+
|
51
|
+
const materializers = State.SQLite.materializers(events, {
|
52
|
+
'todo.created': ({ id, text, completed }) => tables.todos.insert({ id, text, completed }),
|
53
|
+
})
|
54
|
+
|
55
|
+
export const state = State.SQLite.makeState({ tables, materializers })
|
56
|
+
export const schema = makeSchema({ state, events })
|
42
57
|
|
43
58
|
export const makeTodoMvc = ({
|
44
59
|
otelTracer,
|