@livestore/livestore 0.4.0-dev.15 → 0.4.0-dev.17
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/SqliteDbWrapper.test.js +2 -1
- package/dist/SqliteDbWrapper.test.js.map +1 -1
- package/dist/live-queries/client-document-get-query.js +3 -2
- package/dist/live-queries/client-document-get-query.js.map +1 -1
- package/dist/live-queries/db-query.d.ts.map +1 -1
- package/dist/live-queries/db-query.js +6 -4
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/db-query.test.js +5 -4
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/mod.d.ts +1 -2
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -1
- package/dist/mod.js.map +1 -1
- package/dist/store/create-store.d.ts +2 -2
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +6 -5
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts +2 -13
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +34 -13
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-types.d.ts +89 -4
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js +1 -0
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store.d.ts +20 -26
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +106 -86
- package/dist/store/store.js.map +1 -1
- package/package.json +5 -5
- package/src/SqliteDbWrapper.test.ts +2 -2
- package/src/live-queries/client-document-get-query.ts +3 -3
- package/src/live-queries/db-query.test.ts +5 -4
- package/src/live-queries/db-query.ts +7 -4
- package/src/mod.ts +8 -8
- package/src/store/create-store.ts +6 -7
- package/src/store/devtools.ts +40 -26
- package/src/store/store-types.ts +107 -4
- package/src/store/store.ts +143 -117
package/src/store/store.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Bindable,
|
|
3
3
|
type ClientSession,
|
|
4
|
-
type ClientSessionSyncProcessor,
|
|
5
4
|
Devtools,
|
|
6
5
|
getExecStatementsFromMaterializer,
|
|
7
6
|
getResultSchema,
|
|
@@ -37,7 +36,7 @@ import {
|
|
|
37
36
|
import { nanoid } from '@livestore/utils/nanoid'
|
|
38
37
|
import * as otel from '@opentelemetry/api'
|
|
39
38
|
|
|
40
|
-
import type { LiveQuery,
|
|
39
|
+
import type { LiveQuery, ReactivityGraphContext, SignalDef } from '../live-queries/base-class.ts'
|
|
41
40
|
import { makeReactivityGraph } from '../live-queries/base-class.ts'
|
|
42
41
|
import { makeExecBeforeFirstRun } from '../live-queries/client-document-get-query.ts'
|
|
43
42
|
import { queryDb } from '../live-queries/db-query.ts'
|
|
@@ -45,18 +44,20 @@ import type { Ref } from '../reactive.ts'
|
|
|
45
44
|
import { SqliteDbWrapper } from '../SqliteDbWrapper.ts'
|
|
46
45
|
import { ReferenceCountedSet } from '../utils/data-structures.ts'
|
|
47
46
|
import { downloadBlob, exposeDebugUtils } from '../utils/dev.ts'
|
|
48
|
-
import
|
|
49
|
-
Queryable,
|
|
50
|
-
RefreshReason,
|
|
51
|
-
StoreCommitOptions,
|
|
52
|
-
StoreEventsOptions,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
import {
|
|
48
|
+
type Queryable,
|
|
49
|
+
type RefreshReason,
|
|
50
|
+
type StoreCommitOptions,
|
|
51
|
+
type StoreEventsOptions,
|
|
52
|
+
type StoreInternals,
|
|
53
|
+
StoreInternalsSymbol,
|
|
54
|
+
type StoreOptions,
|
|
55
|
+
type StoreOtel,
|
|
56
|
+
type SubscribeOptions,
|
|
57
|
+
type Unsubscribe,
|
|
57
58
|
} from './store-types.ts'
|
|
58
59
|
|
|
59
|
-
type SubscribeFn = {
|
|
60
|
+
export type SubscribeFn = {
|
|
60
61
|
<TResult>(
|
|
61
62
|
query: Queryable<TResult>,
|
|
62
63
|
onUpdate: (value: TResult) => void,
|
|
@@ -71,12 +72,8 @@ if (isDevEnv()) {
|
|
|
71
72
|
|
|
72
73
|
export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}> extends Inspectable.Class {
|
|
73
74
|
readonly storeId: string
|
|
74
|
-
reactivityGraph: ReactivityGraph
|
|
75
|
-
sqliteDbWrapper: SqliteDbWrapper
|
|
76
|
-
clientSession: ClientSession
|
|
77
75
|
schema: LiveStoreSchema
|
|
78
76
|
context: TContext
|
|
79
|
-
otel: StoreOtel
|
|
80
77
|
/**
|
|
81
78
|
* Reactive connectivity updates emitted by the backing sync backend.
|
|
82
79
|
*
|
|
@@ -94,29 +91,16 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
94
91
|
* )
|
|
95
92
|
* ```
|
|
96
93
|
*/
|
|
97
|
-
readonly networkStatus: ClientSession['leaderThread']['networkStatus']
|
|
98
|
-
/**
|
|
99
|
-
* Note we're using `Ref<null>` here as we don't care about the value but only about *that* something has changed.
|
|
100
|
-
* This only works in combination with `equal: () => false` which will always trigger a refresh.
|
|
101
|
-
*/
|
|
102
|
-
tableRefs: { [key: string]: Ref<null, ReactivityGraphContext, RefreshReason> }
|
|
103
|
-
|
|
104
|
-
/** Tracks whether the store has been shut down */
|
|
105
|
-
private isShutdown = false
|
|
94
|
+
readonly networkStatus: ClientSession['leaderThread']['networkStatus'];
|
|
106
95
|
|
|
107
|
-
|
|
108
|
-
runtime: Runtime.Runtime<Scope.Scope>
|
|
109
|
-
lifetimeScope: Scope.Scope
|
|
110
|
-
}
|
|
96
|
+
/** Tracks whether the store has been shut down is kept in internals */
|
|
111
97
|
|
|
112
98
|
/** RC-based set to see which queries are currently subscribed to */
|
|
113
|
-
activeQueries: ReferenceCountedSet<LiveQuery<any>>
|
|
114
|
-
|
|
115
|
-
// NOTE this is currently exposed for the Devtools databrowser to commit events
|
|
116
|
-
readonly __eventSchema
|
|
117
|
-
readonly syncProcessor: ClientSessionSyncProcessor
|
|
118
99
|
|
|
119
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Store internals. Shouldn't be used directly in application code.
|
|
102
|
+
*/
|
|
103
|
+
[StoreInternalsSymbol]: StoreInternals
|
|
120
104
|
|
|
121
105
|
// #region constructor
|
|
122
106
|
constructor({
|
|
@@ -134,20 +118,14 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
134
118
|
super()
|
|
135
119
|
|
|
136
120
|
this.storeId = storeId
|
|
137
|
-
|
|
138
|
-
this.sqliteDbWrapper = new SqliteDbWrapper({ otel: otelOptions, db: clientSession.sqliteDb })
|
|
139
|
-
this.clientSession = clientSession
|
|
140
121
|
this.schema = schema
|
|
141
122
|
this.context = context
|
|
142
|
-
this.networkStatus = clientSession.leaderThread.networkStatus
|
|
143
|
-
|
|
144
|
-
this.effectContext = effectContext
|
|
145
123
|
|
|
146
124
|
const reactivityGraph = makeReactivityGraph()
|
|
147
125
|
|
|
148
126
|
const syncSpan = otelOptions.tracer.startSpan('LiveStore:sync', {}, otelOptions.rootSpanContext)
|
|
149
127
|
|
|
150
|
-
|
|
128
|
+
const syncProcessor = makeClientSessionSyncProcessor({
|
|
151
129
|
schema,
|
|
152
130
|
clientSession,
|
|
153
131
|
runtime: effectContext.runtime,
|
|
@@ -175,7 +153,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
175
153
|
const execArgsArr = getExecStatementsFromMaterializer({
|
|
176
154
|
eventDef,
|
|
177
155
|
materializer,
|
|
178
|
-
dbState: this.sqliteDbWrapper,
|
|
156
|
+
dbState: this[StoreInternalsSymbol].sqliteDbWrapper,
|
|
179
157
|
event: { decoded: undefined, encoded: eventEncoded },
|
|
180
158
|
})
|
|
181
159
|
|
|
@@ -202,10 +180,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
202
180
|
for (const {
|
|
203
181
|
statementSql,
|
|
204
182
|
bindValues,
|
|
205
|
-
writeTables = this.sqliteDbWrapper.getTablesUsed(statementSql),
|
|
183
|
+
writeTables = this[StoreInternalsSymbol].sqliteDbWrapper.getTablesUsed(statementSql),
|
|
206
184
|
} of execArgsArr) {
|
|
207
185
|
try {
|
|
208
|
-
this.sqliteDbWrapper.cachedExecute(statementSql, bindValues, {
|
|
186
|
+
this[StoreInternalsSymbol].sqliteDbWrapper.cachedExecute(statementSql, bindValues, {
|
|
187
|
+
otelContext,
|
|
188
|
+
writeTables,
|
|
189
|
+
})
|
|
209
190
|
} catch (cause) {
|
|
210
191
|
// TOOD refactor with `SqliteError`
|
|
211
192
|
throw UnexpectedError.make({
|
|
@@ -219,7 +200,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
219
200
|
writeTablesForEvent.add(table)
|
|
220
201
|
}
|
|
221
202
|
|
|
222
|
-
this.sqliteDbWrapper.debug.head = eventEncoded.seqNum
|
|
203
|
+
this[StoreInternalsSymbol].sqliteDbWrapper.debug.head = eventEncoded.seqNum
|
|
223
204
|
}
|
|
224
205
|
}
|
|
225
206
|
|
|
@@ -228,7 +209,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
228
209
|
| { _tag: 'no-op' }
|
|
229
210
|
| { _tag: 'unset' } = { _tag: 'unset' }
|
|
230
211
|
if (withChangeset === true) {
|
|
231
|
-
sessionChangeset = this.sqliteDbWrapper.withChangeset(exec).changeset
|
|
212
|
+
sessionChangeset = this[StoreInternalsSymbol].sqliteDbWrapper.withChangeset(exec).changeset
|
|
232
213
|
} else {
|
|
233
214
|
exec()
|
|
234
215
|
}
|
|
@@ -237,12 +218,12 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
237
218
|
}).pipe(Effect.mapError((cause) => MaterializeError.make({ cause }))),
|
|
238
219
|
),
|
|
239
220
|
rollback: (changeset) => {
|
|
240
|
-
this.sqliteDbWrapper.rollback(changeset)
|
|
221
|
+
this[StoreInternalsSymbol].sqliteDbWrapper.rollback(changeset)
|
|
241
222
|
},
|
|
242
223
|
refreshTables: (tables) => {
|
|
243
224
|
const tablesToUpdate = [] as [Ref<null, ReactivityGraphContext, RefreshReason>, null][]
|
|
244
225
|
for (const tableName of tables) {
|
|
245
|
-
const tableRef = this.tableRefs[tableName]
|
|
226
|
+
const tableRef = this[StoreInternalsSymbol].tableRefs[tableName]
|
|
246
227
|
assertNever(tableRef !== undefined, `No table ref found for ${tableName}`)
|
|
247
228
|
tablesToUpdate.push([tableRef!, null])
|
|
248
229
|
}
|
|
@@ -260,11 +241,9 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
260
241
|
confirmUnsavedChanges,
|
|
261
242
|
})
|
|
262
243
|
|
|
263
|
-
this.__eventSchema = LiveStoreEvent.makeEventDefSchemaMemo(schema)
|
|
264
|
-
|
|
265
244
|
// TODO generalize the `tableRefs` concept to allow finer-grained refs
|
|
266
|
-
|
|
267
|
-
|
|
245
|
+
const tableRefs: { [key: string]: Ref<null, ReactivityGraphContext, RefreshReason> } = {}
|
|
246
|
+
const activeQueries = new ReferenceCountedSet<LiveQuery<any>>()
|
|
268
247
|
|
|
269
248
|
const commitsSpan = otelOptions.tracer.startSpan('LiveStore:commits', {}, otelOptions.rootSpanContext)
|
|
270
249
|
const otelMuationsSpanContext = otel.trace.setSpan(otel.context.active(), commitsSpan)
|
|
@@ -272,8 +251,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
272
251
|
const queriesSpan = otelOptions.tracer.startSpan('LiveStore:queries', {}, otelOptions.rootSpanContext)
|
|
273
252
|
const otelQueriesSpanContext = otel.trace.setSpan(otel.context.active(), queriesSpan)
|
|
274
253
|
|
|
275
|
-
|
|
276
|
-
this.reactivityGraph.context = {
|
|
254
|
+
reactivityGraph.context = {
|
|
277
255
|
store: this as unknown as Store<LiveStoreSchema>,
|
|
278
256
|
defRcMap: new Map(),
|
|
279
257
|
reactivityGraph: new WeakRef(reactivityGraph),
|
|
@@ -281,8 +259,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
281
259
|
rootOtelContext: otelQueriesSpanContext,
|
|
282
260
|
effectsWrapper: batchUpdates,
|
|
283
261
|
}
|
|
284
|
-
|
|
285
|
-
this.otel = {
|
|
262
|
+
const otelObj: StoreOtel = {
|
|
286
263
|
tracer: otelOptions.tracer,
|
|
287
264
|
rootSpanContext: otelOptions.rootSpanContext,
|
|
288
265
|
commitsSpanContext: otelMuationsSpanContext,
|
|
@@ -298,27 +275,27 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
298
275
|
: Array.from(this.schema.state.sqlite.tables.keys()).filter((_) => !SystemTables.isStateSystemTable(_)),
|
|
299
276
|
)
|
|
300
277
|
const existingTableRefs = new Map(
|
|
301
|
-
Array.from(
|
|
278
|
+
Array.from(reactivityGraph.atoms.values())
|
|
302
279
|
.filter((_): _ is Ref<any, any, any> => _._tag === 'ref' && _.label?.startsWith('tableRef:') === true)
|
|
303
280
|
.map((_) => [_.label!.slice('tableRef:'.length), _] as const),
|
|
304
281
|
)
|
|
305
282
|
for (const tableName of allTableNames) {
|
|
306
|
-
|
|
283
|
+
tableRefs[tableName] =
|
|
307
284
|
existingTableRefs.get(tableName) ??
|
|
308
|
-
|
|
285
|
+
reactivityGraph.makeRef(null, {
|
|
309
286
|
equal: () => false,
|
|
310
287
|
label: `tableRef:${tableName}`,
|
|
311
288
|
meta: { liveStoreRefType: 'table' },
|
|
312
289
|
})
|
|
313
290
|
}
|
|
314
291
|
|
|
315
|
-
|
|
292
|
+
const boot = Effect.gen(this, function* () {
|
|
316
293
|
yield* Effect.addFinalizer(() =>
|
|
317
294
|
Effect.sync(() => {
|
|
318
295
|
// Remove all table refs from the reactivity graph
|
|
319
|
-
for (const tableRef of Object.values(
|
|
296
|
+
for (const tableRef of Object.values(tableRefs)) {
|
|
320
297
|
for (const superComp of tableRef.super) {
|
|
321
|
-
this.reactivityGraph.removeEdge(superComp, tableRef)
|
|
298
|
+
this[StoreInternalsSymbol].reactivityGraph.removeEdge(superComp, tableRef)
|
|
322
299
|
}
|
|
323
300
|
}
|
|
324
301
|
|
|
@@ -329,21 +306,57 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
329
306
|
}),
|
|
330
307
|
)
|
|
331
308
|
|
|
332
|
-
yield*
|
|
309
|
+
yield* syncProcessor.boot
|
|
333
310
|
})
|
|
311
|
+
|
|
312
|
+
// Build Sqlite wrapper last to avoid using getters before internals are set
|
|
313
|
+
const sqliteDbWrapper = new SqliteDbWrapper({ otel: otelOptions, db: clientSession.sqliteDb })
|
|
314
|
+
|
|
315
|
+
// Initialize internals bag
|
|
316
|
+
this[StoreInternalsSymbol] = {
|
|
317
|
+
eventSchema: LiveStoreEvent.makeEventDefSchemaMemo(schema) as Schema.Schema<
|
|
318
|
+
LiveStoreEvent.AnyDecoded,
|
|
319
|
+
LiveStoreEvent.AnyEncoded
|
|
320
|
+
>,
|
|
321
|
+
clientSession,
|
|
322
|
+
sqliteDbWrapper,
|
|
323
|
+
effectContext,
|
|
324
|
+
otel: otelObj,
|
|
325
|
+
reactivityGraph,
|
|
326
|
+
tableRefs,
|
|
327
|
+
activeQueries,
|
|
328
|
+
syncProcessor,
|
|
329
|
+
boot,
|
|
330
|
+
isShutdown: false,
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Initialize stable network status property from client session
|
|
334
|
+
this.networkStatus = clientSession.leaderThread.networkStatus
|
|
334
335
|
}
|
|
335
336
|
// #endregion constructor
|
|
336
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Current session identifier for this Store instance.
|
|
340
|
+
*
|
|
341
|
+
* - Stable for the lifetime of the Store
|
|
342
|
+
* - Useful for correlating events or scoping per-session data
|
|
343
|
+
*/
|
|
337
344
|
get sessionId(): string {
|
|
338
|
-
return this.clientSession.sessionId
|
|
345
|
+
return this[StoreInternalsSymbol].clientSession.sessionId
|
|
339
346
|
}
|
|
340
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Stable client identifier for the process/device using this Store.
|
|
350
|
+
*
|
|
351
|
+
* - Shared across Store instances created by the same client
|
|
352
|
+
* - Useful for diagnostics and multi-client correlation
|
|
353
|
+
*/
|
|
341
354
|
get clientId(): string {
|
|
342
|
-
return this.clientSession.clientId
|
|
355
|
+
return this[StoreInternalsSymbol].clientSession.clientId
|
|
343
356
|
}
|
|
344
357
|
|
|
345
358
|
private checkShutdown = (operation: string): void => {
|
|
346
|
-
if (this.isShutdown) {
|
|
359
|
+
if (this[StoreInternalsSymbol].isShutdown) {
|
|
347
360
|
throw new UnexpectedError({
|
|
348
361
|
cause: `Store has been shut down (while performing "${operation}").`,
|
|
349
362
|
note: `You cannot perform this operation after the store has been shut down.`,
|
|
@@ -388,17 +401,17 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
388
401
|
): Unsubscribe => {
|
|
389
402
|
this.checkShutdown('subscribe')
|
|
390
403
|
|
|
391
|
-
return this.otel.tracer.startActiveSpan(
|
|
404
|
+
return this[StoreInternalsSymbol].otel.tracer.startActiveSpan(
|
|
392
405
|
`LiveStore.subscribe`,
|
|
393
406
|
{ attributes: { label: options?.label, queryLabel: isQueryBuilder(query) ? query.toString() : query.label } },
|
|
394
|
-
options?.otelContext ?? this.otel.queriesSpanContext,
|
|
407
|
+
options?.otelContext ?? this[StoreInternalsSymbol].otel.queriesSpanContext,
|
|
395
408
|
(span) => {
|
|
396
409
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
397
410
|
|
|
398
411
|
const queryRcRef = isQueryBuilder(query)
|
|
399
|
-
? queryDb(query).make(this.reactivityGraph.context!)
|
|
412
|
+
? queryDb(query).make(this[StoreInternalsSymbol].reactivityGraph.context!)
|
|
400
413
|
: query._tag === 'def' || query._tag === 'signal-def'
|
|
401
|
-
? query.make(this.reactivityGraph.context!)
|
|
414
|
+
? query.make(this[StoreInternalsSymbol].reactivityGraph.context!)
|
|
402
415
|
: {
|
|
403
416
|
value: query as LiveQuery<TResult>,
|
|
404
417
|
deref: () => {},
|
|
@@ -406,7 +419,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
406
419
|
const query$ = queryRcRef.value
|
|
407
420
|
|
|
408
421
|
const label = `subscribe:${options?.label}`
|
|
409
|
-
const effect = this.reactivityGraph.makeEffect(
|
|
422
|
+
const effect = this[StoreInternalsSymbol].reactivityGraph.makeEffect(
|
|
410
423
|
(get, _otelContext, debugRefreshReason) => onUpdate(get(query$.results$, otelContext, debugRefreshReason)),
|
|
411
424
|
{ label },
|
|
412
425
|
)
|
|
@@ -417,7 +430,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
417
430
|
|
|
418
431
|
options?.onSubscribe?.(query$)
|
|
419
432
|
|
|
420
|
-
this.activeQueries.add(query$ as LiveQuery<TResult>)
|
|
433
|
+
this[StoreInternalsSymbol].activeQueries.add(query$ as LiveQuery<TResult>)
|
|
421
434
|
|
|
422
435
|
if (options?.skipInitialRun !== true && !query$.isDestroyed) {
|
|
423
436
|
effect.doEffect(otelContext, {
|
|
@@ -428,8 +441,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
428
441
|
|
|
429
442
|
const unsubscribe = () => {
|
|
430
443
|
try {
|
|
431
|
-
this.reactivityGraph.destroyNode(effect)
|
|
432
|
-
this.activeQueries.remove(query$ as LiveQuery<TResult>)
|
|
444
|
+
this[StoreInternalsSymbol].reactivityGraph.destroyNode(effect)
|
|
445
|
+
this[StoreInternalsSymbol].activeQueries.remove(query$ as LiveQuery<TResult>)
|
|
433
446
|
|
|
434
447
|
if (options?.stackInfo) {
|
|
435
448
|
query$.activeSubscriptions.delete(options.stackInfo)
|
|
@@ -498,9 +511,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
498
511
|
this.checkShutdown('query')
|
|
499
512
|
|
|
500
513
|
if (typeof query === 'object' && 'query' in query && 'bindValues' in query) {
|
|
501
|
-
const res = this.sqliteDbWrapper.cachedSelect(
|
|
502
|
-
|
|
503
|
-
|
|
514
|
+
const res = this[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect(
|
|
515
|
+
query.query,
|
|
516
|
+
prepareBindValues(query.bindValues, query.query),
|
|
517
|
+
{
|
|
518
|
+
...omitUndefineds({ otelContext: options?.otelContext }),
|
|
519
|
+
},
|
|
520
|
+
) as any
|
|
504
521
|
if (query.schema) {
|
|
505
522
|
return Schema.decodeSync(query.schema)(res)
|
|
506
523
|
}
|
|
@@ -513,7 +530,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
513
530
|
id: ast.id,
|
|
514
531
|
explicitDefaultValues: ast.explicitDefaultValues,
|
|
515
532
|
otelContext: options?.otelContext,
|
|
516
|
-
})(this.reactivityGraph.context!)
|
|
533
|
+
})(this[StoreInternalsSymbol].reactivityGraph.context!)
|
|
517
534
|
}
|
|
518
535
|
|
|
519
536
|
const sqlRes = query.asSql()
|
|
@@ -521,13 +538,17 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
521
538
|
|
|
522
539
|
// Replace SessionIdSymbol in bind values before executing the query
|
|
523
540
|
if (sqlRes.bindValues) {
|
|
524
|
-
replaceSessionIdSymbol(sqlRes.bindValues, this.clientSession.sessionId)
|
|
541
|
+
replaceSessionIdSymbol(sqlRes.bindValues, this[StoreInternalsSymbol].clientSession.sessionId)
|
|
525
542
|
}
|
|
526
543
|
|
|
527
|
-
const rawRes = this.sqliteDbWrapper.cachedSelect(
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
544
|
+
const rawRes = this[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect(
|
|
545
|
+
sqlRes.query,
|
|
546
|
+
sqlRes.bindValues as any as PreparedBindValues,
|
|
547
|
+
{
|
|
548
|
+
...omitUndefineds({ otelContext: options?.otelContext }),
|
|
549
|
+
queriedTables: new Set([query[QueryBuilderAstSymbol].tableDef.sqliteDef.name]),
|
|
550
|
+
},
|
|
551
|
+
)
|
|
531
552
|
|
|
532
553
|
const decodeResult = Schema.decodeEither(schema)(rawRes)
|
|
533
554
|
if (decodeResult._tag === 'Right') {
|
|
@@ -543,12 +564,12 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
543
564
|
)
|
|
544
565
|
}
|
|
545
566
|
} else if (query._tag === 'def') {
|
|
546
|
-
const query$ = query.make(this.reactivityGraph.context!)
|
|
567
|
+
const query$ = query.make(this[StoreInternalsSymbol].reactivityGraph.context!)
|
|
547
568
|
const result = this.query(query$.value, options)
|
|
548
569
|
query$.deref()
|
|
549
570
|
return result
|
|
550
571
|
} else if (query._tag === 'signal-def') {
|
|
551
|
-
const signal$ = query.make(this.reactivityGraph.context!)
|
|
572
|
+
const signal$ = query.make(this[StoreInternalsSymbol].reactivityGraph.context!)
|
|
552
573
|
return signal$.value.get()
|
|
553
574
|
} else {
|
|
554
575
|
return query.run({
|
|
@@ -575,7 +596,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
575
596
|
setSignal = <T>(signalDef: SignalDef<T>, value: T | ((prev: T) => T)): void => {
|
|
576
597
|
this.checkShutdown('setSignal')
|
|
577
598
|
|
|
578
|
-
const signalRef = signalDef.make(this.reactivityGraph.context!)
|
|
599
|
+
const signalRef = signalDef.make(this[StoreInternalsSymbol].reactivityGraph.context!)
|
|
579
600
|
const newValue: T = typeof value === 'function' ? (value as any)(signalRef.value.get()) : value
|
|
580
601
|
signalRef.value.set(newValue)
|
|
581
602
|
|
|
@@ -664,13 +685,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
664
685
|
const { events, options } = this.getCommitArgs(firstEventOrTxnFnOrOptions, restEvents)
|
|
665
686
|
|
|
666
687
|
Effect.gen(this, function* () {
|
|
667
|
-
const commitsSpan = otel.trace.getSpan(this.otel.commitsSpanContext)
|
|
688
|
+
const commitsSpan = otel.trace.getSpan(this[StoreInternalsSymbol].otel.commitsSpanContext)
|
|
668
689
|
commitsSpan?.addEvent('commit')
|
|
669
690
|
const currentSpan = yield* OtelTracer.currentOtelSpan.pipe(Effect.orDie)
|
|
670
691
|
commitsSpan?.addLink({ context: currentSpan.spanContext() })
|
|
671
692
|
|
|
672
693
|
for (const event of events) {
|
|
673
|
-
replaceSessionIdSymbol(event.args, this.clientSession.sessionId)
|
|
694
|
+
replaceSessionIdSymbol(event.args, this[StoreInternalsSymbol].clientSession.sessionId)
|
|
674
695
|
}
|
|
675
696
|
|
|
676
697
|
if (events.length === 0) return
|
|
@@ -680,11 +701,11 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
680
701
|
const materializeEventsTx = Effect.try({
|
|
681
702
|
try: () => {
|
|
682
703
|
const runMaterializeEvents = () => {
|
|
683
|
-
return this.syncProcessor.push(events).pipe(Runtime.runSync(localRuntime))
|
|
704
|
+
return this[StoreInternalsSymbol].syncProcessor.push(events).pipe(Runtime.runSync(localRuntime))
|
|
684
705
|
}
|
|
685
706
|
|
|
686
707
|
if (events.length > 1) {
|
|
687
|
-
return this.sqliteDbWrapper.txn(runMaterializeEvents)
|
|
708
|
+
return this[StoreInternalsSymbol].sqliteDbWrapper.txn(runMaterializeEvents)
|
|
688
709
|
} else {
|
|
689
710
|
return runMaterializeEvents()
|
|
690
711
|
}
|
|
@@ -697,7 +718,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
697
718
|
|
|
698
719
|
const tablesToUpdate: [Ref<null, ReactivityGraphContext, RefreshReason>, null][] = []
|
|
699
720
|
for (const tableName of writeTables) {
|
|
700
|
-
const tableRef = this.tableRefs[tableName]
|
|
721
|
+
const tableRef = this[StoreInternalsSymbol].tableRefs[tableName]
|
|
701
722
|
assertNever(tableRef !== undefined, `No table ref found for ${tableName}`)
|
|
702
723
|
tablesToUpdate.push([tableRef!, null])
|
|
703
724
|
}
|
|
@@ -710,7 +731,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
710
731
|
const skipRefresh = options?.skipRefresh ?? false
|
|
711
732
|
|
|
712
733
|
// Update all table refs together in a batch, to only trigger one reactive update
|
|
713
|
-
this.reactivityGraph.setRefs(tablesToUpdate, {
|
|
734
|
+
this[StoreInternalsSymbol].reactivityGraph.setRefs(tablesToUpdate, {
|
|
714
735
|
debugRefreshReason,
|
|
715
736
|
skipRefresh,
|
|
716
737
|
otelContext: otel.trace.setSpan(otel.context.active(), currentSpan),
|
|
@@ -725,14 +746,16 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
725
746
|
},
|
|
726
747
|
links: [
|
|
727
748
|
// Span link to LiveStore:commits
|
|
728
|
-
OtelTracer.makeSpanLink({
|
|
749
|
+
OtelTracer.makeSpanLink({
|
|
750
|
+
context: otel.trace.getSpanContext(this[StoreInternalsSymbol].otel.commitsSpanContext)!,
|
|
751
|
+
}),
|
|
729
752
|
// User-provided span links
|
|
730
753
|
...(options?.spanLinks?.map(OtelTracer.makeSpanLink) ?? []),
|
|
731
754
|
],
|
|
732
755
|
}),
|
|
733
756
|
Effect.tapErrorCause(Effect.logError),
|
|
734
757
|
Effect.catchAllCause((cause) => Effect.fork(this.shutdown(cause))),
|
|
735
|
-
Runtime.runSync(this.effectContext.runtime),
|
|
758
|
+
Runtime.runSync(this[StoreInternalsSymbol].effectContext.runtime),
|
|
736
759
|
)
|
|
737
760
|
}
|
|
738
761
|
// #endregion commit
|
|
@@ -775,13 +798,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
775
798
|
this.checkShutdown('manualRefresh')
|
|
776
799
|
|
|
777
800
|
const { label } = options ?? {}
|
|
778
|
-
this.otel.tracer.startActiveSpan(
|
|
801
|
+
this[StoreInternalsSymbol].otel.tracer.startActiveSpan(
|
|
779
802
|
'LiveStore:manualRefresh',
|
|
780
803
|
{ attributes: { 'livestore.manualRefreshLabel': label } },
|
|
781
|
-
this.otel.commitsSpanContext,
|
|
804
|
+
this[StoreInternalsSymbol].otel.commitsSpanContext,
|
|
782
805
|
(span) => {
|
|
783
806
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
784
|
-
this.reactivityGraph.runDeferredEffects({ otelContext })
|
|
807
|
+
this[StoreInternalsSymbol].reactivityGraph.runDeferredEffects({ otelContext })
|
|
785
808
|
span.end()
|
|
786
809
|
},
|
|
787
810
|
)
|
|
@@ -795,7 +818,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
795
818
|
shutdownPromise = async (cause?: UnexpectedError) => {
|
|
796
819
|
this.checkShutdown('shutdownPromise')
|
|
797
820
|
|
|
798
|
-
this.isShutdown = true
|
|
821
|
+
this[StoreInternalsSymbol].isShutdown = true
|
|
799
822
|
await this.shutdown(cause ? Cause.fail(cause) : undefined).pipe(this.runEffectFork, Fiber.join, Effect.runPromise)
|
|
800
823
|
}
|
|
801
824
|
|
|
@@ -805,8 +828,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
805
828
|
* This is called automatically when the store was created using the React or Effect API.
|
|
806
829
|
*/
|
|
807
830
|
shutdown = (cause?: Cause.Cause<UnexpectedError | MaterializeError>): Effect.Effect<void> => {
|
|
808
|
-
this.isShutdown = true
|
|
809
|
-
return this.clientSession.shutdown(
|
|
831
|
+
this[StoreInternalsSymbol].isShutdown = true
|
|
832
|
+
return this[StoreInternalsSymbol].clientSession.shutdown(
|
|
810
833
|
cause ? Exit.failCause(cause) : Exit.succeed(IntentionalShutdownCause.make({ reason: 'manual' })),
|
|
811
834
|
)
|
|
812
835
|
}
|
|
@@ -819,30 +842,33 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
819
842
|
_dev = {
|
|
820
843
|
downloadDb: (source: 'local' | 'leader' = 'local') => {
|
|
821
844
|
Effect.gen(this, function* () {
|
|
822
|
-
const data =
|
|
845
|
+
const data =
|
|
846
|
+
source === 'local'
|
|
847
|
+
? this[StoreInternalsSymbol].sqliteDbWrapper.export()
|
|
848
|
+
: yield* this[StoreInternalsSymbol].clientSession.leaderThread.export
|
|
823
849
|
downloadBlob(data, `livestore-${Date.now()}.db`)
|
|
824
850
|
}).pipe(this.runEffectFork)
|
|
825
851
|
},
|
|
826
852
|
|
|
827
853
|
downloadEventlogDb: () => {
|
|
828
854
|
Effect.gen(this, function* () {
|
|
829
|
-
const data = yield* this.clientSession.leaderThread.getEventlogData
|
|
855
|
+
const data = yield* this[StoreInternalsSymbol].clientSession.leaderThread.getEventlogData
|
|
830
856
|
downloadBlob(data, `livestore-eventlog-${Date.now()}.db`)
|
|
831
857
|
}).pipe(this.runEffectFork)
|
|
832
858
|
},
|
|
833
859
|
|
|
834
860
|
hardReset: (mode: 'all-data' | 'only-app-db' = 'all-data') => {
|
|
835
861
|
Effect.gen(this, function* () {
|
|
836
|
-
const clientId = this.clientSession.clientId
|
|
837
|
-
yield* this.clientSession.leaderThread.sendDevtoolsMessage(
|
|
862
|
+
const clientId = this[StoreInternalsSymbol].clientSession.clientId
|
|
863
|
+
yield* this[StoreInternalsSymbol].clientSession.leaderThread.sendDevtoolsMessage(
|
|
838
864
|
Devtools.Leader.ResetAllData.Request.make({ liveStoreVersion, mode, requestId: nanoid(), clientId }),
|
|
839
865
|
)
|
|
840
866
|
}).pipe(this.runEffectFork)
|
|
841
867
|
},
|
|
842
868
|
|
|
843
869
|
overrideNetworkStatus: (status: 'online' | 'offline') => {
|
|
844
|
-
const clientId = this.clientSession.clientId
|
|
845
|
-
this.clientSession.leaderThread
|
|
870
|
+
const clientId = this[StoreInternalsSymbol].clientSession.clientId
|
|
871
|
+
this[StoreInternalsSymbol].clientSession.leaderThread
|
|
846
872
|
.sendDevtoolsMessage(
|
|
847
873
|
Devtools.Leader.SetSyncLatch.Request.make({
|
|
848
874
|
clientId,
|
|
@@ -856,19 +882,19 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
856
882
|
|
|
857
883
|
syncStates: () =>
|
|
858
884
|
Effect.gen(this, function* () {
|
|
859
|
-
const session = yield* this.syncProcessor.syncState
|
|
860
|
-
const leader = yield* this.clientSession.leaderThread.syncState
|
|
885
|
+
const session = yield* this[StoreInternalsSymbol].syncProcessor.syncState
|
|
886
|
+
const leader = yield* this[StoreInternalsSymbol].clientSession.leaderThread.syncState
|
|
861
887
|
return { session, leader }
|
|
862
888
|
}).pipe(this.runEffectPromise),
|
|
863
889
|
|
|
864
890
|
printSyncStates: () => {
|
|
865
891
|
Effect.gen(this, function* () {
|
|
866
|
-
const session = yield* this.syncProcessor.syncState
|
|
892
|
+
const session = yield* this[StoreInternalsSymbol].syncProcessor.syncState
|
|
867
893
|
yield* Effect.log(
|
|
868
894
|
`Session sync state: ${session.localHead} (upstream: ${session.upstreamHead})`,
|
|
869
895
|
session.toJSON(),
|
|
870
896
|
)
|
|
871
|
-
const leader = yield* this.clientSession.leaderThread.syncState
|
|
897
|
+
const leader = yield* this[StoreInternalsSymbol].clientSession.leaderThread.syncState
|
|
872
898
|
yield* Effect.log(`Leader sync state: ${leader.localHead} (upstream: ${leader.upstreamHead})`, leader.toJSON())
|
|
873
899
|
}).pipe(this.runEffectFork)
|
|
874
900
|
},
|
|
@@ -876,25 +902,25 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
876
902
|
version: liveStoreVersion,
|
|
877
903
|
|
|
878
904
|
otel: {
|
|
879
|
-
rootSpanContext: () => otel.trace.getSpan(this.otel.rootSpanContext)?.spanContext(),
|
|
905
|
+
rootSpanContext: () => otel.trace.getSpan(this[StoreInternalsSymbol].otel.rootSpanContext)?.spanContext(),
|
|
880
906
|
},
|
|
881
907
|
}
|
|
882
908
|
|
|
883
909
|
// NOTE This is needed because when booting a Store via Effect it seems to call `toJSON` in the error path
|
|
884
910
|
toJSON = () => ({
|
|
885
911
|
_tag: 'livestore.Store',
|
|
886
|
-
reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults: true }),
|
|
912
|
+
reactivityGraph: this[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults: true }),
|
|
887
913
|
})
|
|
888
914
|
|
|
889
915
|
private runEffectFork = <A, E>(effect: Effect.Effect<A, E, Scope.Scope>) =>
|
|
890
916
|
effect.pipe(
|
|
891
|
-
Effect.forkIn(this.effectContext.lifetimeScope),
|
|
917
|
+
Effect.forkIn(this[StoreInternalsSymbol].effectContext.lifetimeScope),
|
|
892
918
|
Effect.tapCauseLogPretty,
|
|
893
|
-
Runtime.runFork(this.effectContext.runtime),
|
|
919
|
+
Runtime.runFork(this[StoreInternalsSymbol].effectContext.runtime),
|
|
894
920
|
)
|
|
895
921
|
|
|
896
922
|
private runEffectPromise = <A, E>(effect: Effect.Effect<A, E, Scope.Scope>) =>
|
|
897
|
-
effect.pipe(Effect.tapCauseLogPretty, Runtime.runPromise(this.effectContext.runtime))
|
|
923
|
+
effect.pipe(Effect.tapCauseLogPretty, Runtime.runPromise(this[StoreInternalsSymbol].effectContext.runtime))
|
|
898
924
|
|
|
899
925
|
private getCommitArgs = (
|
|
900
926
|
firstEventOrTxnFnOrOptions: any,
|
|
@@ -926,7 +952,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
|
|
|
926
952
|
|
|
927
953
|
// for (const event of events) {
|
|
928
954
|
// if (event.args.id === SessionIdSymbol) {
|
|
929
|
-
// event.args.id = this.
|
|
955
|
+
// event.args.id = this.sessionId
|
|
930
956
|
// }
|
|
931
957
|
// }
|
|
932
958
|
|