@livestore/livestore 0.4.0-dev.16 → 0.4.0-dev.18

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.
Files changed (40) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/SqliteDbWrapper.test.js +2 -1
  3. package/dist/SqliteDbWrapper.test.js.map +1 -1
  4. package/dist/live-queries/client-document-get-query.js +3 -2
  5. package/dist/live-queries/client-document-get-query.js.map +1 -1
  6. package/dist/live-queries/db-query.d.ts.map +1 -1
  7. package/dist/live-queries/db-query.js +6 -4
  8. package/dist/live-queries/db-query.js.map +1 -1
  9. package/dist/live-queries/db-query.test.js +64 -4
  10. package/dist/live-queries/db-query.test.js.map +1 -1
  11. package/dist/mod.d.ts +1 -2
  12. package/dist/mod.d.ts.map +1 -1
  13. package/dist/mod.js +1 -1
  14. package/dist/mod.js.map +1 -1
  15. package/dist/store/create-store.d.ts.map +1 -1
  16. package/dist/store/create-store.js +3 -2
  17. package/dist/store/create-store.js.map +1 -1
  18. package/dist/store/devtools.d.ts +2 -13
  19. package/dist/store/devtools.d.ts.map +1 -1
  20. package/dist/store/devtools.js +34 -13
  21. package/dist/store/devtools.js.map +1 -1
  22. package/dist/store/store-types.d.ts +89 -4
  23. package/dist/store/store-types.d.ts.map +1 -1
  24. package/dist/store/store-types.js +1 -0
  25. package/dist/store/store-types.js.map +1 -1
  26. package/dist/store/store.d.ts +20 -26
  27. package/dist/store/store.d.ts.map +1 -1
  28. package/dist/store/store.js +129 -91
  29. package/dist/store/store.js.map +1 -1
  30. package/package.json +5 -5
  31. package/src/SqliteDbWrapper.test.ts +2 -2
  32. package/src/live-queries/__snapshots__/db-query.test.ts.snap +220 -0
  33. package/src/live-queries/client-document-get-query.ts +3 -3
  34. package/src/live-queries/db-query.test.ts +103 -6
  35. package/src/live-queries/db-query.ts +7 -4
  36. package/src/mod.ts +8 -8
  37. package/src/store/create-store.ts +3 -2
  38. package/src/store/devtools.ts +40 -26
  39. package/src/store/store-types.ts +107 -4
  40. package/src/store/store.ts +166 -123
@@ -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, ReactivityGraph, ReactivityGraphContext, SignalDef } from '../live-queries/base-class.ts'
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 type {
49
- Queryable,
50
- RefreshReason,
51
- StoreCommitOptions,
52
- StoreEventsOptions,
53
- StoreOptions,
54
- StoreOtel,
55
- SubscribeOptions,
56
- Unsubscribe,
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> }
94
+ readonly networkStatus: ClientSession['leaderThread']['networkStatus'];
103
95
 
104
- /** Tracks whether the store has been shut down */
105
- private isShutdown = false
106
-
107
- private effectContext: {
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
- readonly boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
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
- this.syncProcessor = makeClientSessionSyncProcessor({
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, { otelContext, writeTables })
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
- this.tableRefs = {}
267
- this.activeQueries = new ReferenceCountedSet()
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
- this.reactivityGraph = reactivityGraph
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(this.reactivityGraph.atoms.values())
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
- this.tableRefs[tableName] =
283
+ tableRefs[tableName] =
307
284
  existingTableRefs.get(tableName) ??
308
- this.reactivityGraph.makeRef(null, {
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
- this.boot = Effect.gen(this, function* () {
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(this.tableRefs)) {
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* this.syncProcessor.boot
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,10 +419,23 @@ 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(
410
- (get, _otelContext, debugRefreshReason) => onUpdate(get(query$.results$, otelContext, debugRefreshReason)),
422
+ let suppressCallback = options?.skipInitialRun === true
423
+ const effect = this[StoreInternalsSymbol].reactivityGraph.makeEffect(
424
+ (get, _otelContext, debugRefreshReason) => {
425
+ const result = get(query$.results$, otelContext, debugRefreshReason)
426
+ if (suppressCallback) {
427
+ return
428
+ }
429
+ onUpdate(result)
430
+ },
411
431
  { label },
412
432
  )
433
+ const runInitialEffect = () => {
434
+ effect.doEffect(otelContext, {
435
+ _tag: 'subscribe.initial',
436
+ label: `subscribe-initial-run:${options?.label}`,
437
+ })
438
+ }
413
439
 
414
440
  if (options?.stackInfo) {
415
441
  query$.activeSubscriptions.add(options.stackInfo)
@@ -417,19 +443,23 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
417
443
 
418
444
  options?.onSubscribe?.(query$)
419
445
 
420
- this.activeQueries.add(query$ as LiveQuery<TResult>)
446
+ this[StoreInternalsSymbol].activeQueries.add(query$ as LiveQuery<TResult>)
421
447
 
422
- if (options?.skipInitialRun !== true && !query$.isDestroyed) {
423
- effect.doEffect(otelContext, {
424
- _tag: 'subscribe.initial',
425
- label: `subscribe-initial-run:${options?.label}`,
426
- })
448
+ if (!query$.isDestroyed) {
449
+ if (suppressCallback) {
450
+ // We still run once to register dependencies in the reactive graph, but suppress the initial callback so the
451
+ // caller truly skips the first emission; subsequent runs (after commits) will call the callback.
452
+ runInitialEffect()
453
+ suppressCallback = false
454
+ } else {
455
+ runInitialEffect()
456
+ }
427
457
  }
428
458
 
429
459
  const unsubscribe = () => {
430
460
  try {
431
- this.reactivityGraph.destroyNode(effect)
432
- this.activeQueries.remove(query$ as LiveQuery<TResult>)
461
+ this[StoreInternalsSymbol].reactivityGraph.destroyNode(effect)
462
+ this[StoreInternalsSymbol].activeQueries.remove(query$ as LiveQuery<TResult>)
433
463
 
434
464
  if (options?.stackInfo) {
435
465
  query$.activeSubscriptions.delete(options.stackInfo)
@@ -498,9 +528,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
498
528
  this.checkShutdown('query')
499
529
 
500
530
  if (typeof query === 'object' && 'query' in query && 'bindValues' in query) {
501
- const res = this.sqliteDbWrapper.cachedSelect(query.query, prepareBindValues(query.bindValues, query.query), {
502
- ...omitUndefineds({ otelContext: options?.otelContext }),
503
- }) as any
531
+ const res = this[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect(
532
+ query.query,
533
+ prepareBindValues(query.bindValues, query.query),
534
+ {
535
+ ...omitUndefineds({ otelContext: options?.otelContext }),
536
+ },
537
+ ) as any
504
538
  if (query.schema) {
505
539
  return Schema.decodeSync(query.schema)(res)
506
540
  }
@@ -513,7 +547,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
513
547
  id: ast.id,
514
548
  explicitDefaultValues: ast.explicitDefaultValues,
515
549
  otelContext: options?.otelContext,
516
- })(this.reactivityGraph.context!)
550
+ })(this[StoreInternalsSymbol].reactivityGraph.context!)
517
551
  }
518
552
 
519
553
  const sqlRes = query.asSql()
@@ -521,13 +555,17 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
521
555
 
522
556
  // Replace SessionIdSymbol in bind values before executing the query
523
557
  if (sqlRes.bindValues) {
524
- replaceSessionIdSymbol(sqlRes.bindValues, this.clientSession.sessionId)
558
+ replaceSessionIdSymbol(sqlRes.bindValues, this[StoreInternalsSymbol].clientSession.sessionId)
525
559
  }
526
560
 
527
- const rawRes = this.sqliteDbWrapper.cachedSelect(sqlRes.query, sqlRes.bindValues as any as PreparedBindValues, {
528
- ...omitUndefineds({ otelContext: options?.otelContext }),
529
- queriedTables: new Set([query[QueryBuilderAstSymbol].tableDef.sqliteDef.name]),
530
- })
561
+ const rawRes = this[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect(
562
+ sqlRes.query,
563
+ sqlRes.bindValues as any as PreparedBindValues,
564
+ {
565
+ ...omitUndefineds({ otelContext: options?.otelContext }),
566
+ queriedTables: new Set([query[QueryBuilderAstSymbol].tableDef.sqliteDef.name]),
567
+ },
568
+ )
531
569
 
532
570
  const decodeResult = Schema.decodeEither(schema)(rawRes)
533
571
  if (decodeResult._tag === 'Right') {
@@ -543,12 +581,12 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
543
581
  )
544
582
  }
545
583
  } else if (query._tag === 'def') {
546
- const query$ = query.make(this.reactivityGraph.context!)
584
+ const query$ = query.make(this[StoreInternalsSymbol].reactivityGraph.context!)
547
585
  const result = this.query(query$.value, options)
548
586
  query$.deref()
549
587
  return result
550
588
  } else if (query._tag === 'signal-def') {
551
- const signal$ = query.make(this.reactivityGraph.context!)
589
+ const signal$ = query.make(this[StoreInternalsSymbol].reactivityGraph.context!)
552
590
  return signal$.value.get()
553
591
  } else {
554
592
  return query.run({
@@ -575,7 +613,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
575
613
  setSignal = <T>(signalDef: SignalDef<T>, value: T | ((prev: T) => T)): void => {
576
614
  this.checkShutdown('setSignal')
577
615
 
578
- const signalRef = signalDef.make(this.reactivityGraph.context!)
616
+ const signalRef = signalDef.make(this[StoreInternalsSymbol].reactivityGraph.context!)
579
617
  const newValue: T = typeof value === 'function' ? (value as any)(signalRef.value.get()) : value
580
618
  signalRef.value.set(newValue)
581
619
 
@@ -664,13 +702,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
664
702
  const { events, options } = this.getCommitArgs(firstEventOrTxnFnOrOptions, restEvents)
665
703
 
666
704
  Effect.gen(this, function* () {
667
- const commitsSpan = otel.trace.getSpan(this.otel.commitsSpanContext)
705
+ const commitsSpan = otel.trace.getSpan(this[StoreInternalsSymbol].otel.commitsSpanContext)
668
706
  commitsSpan?.addEvent('commit')
669
707
  const currentSpan = yield* OtelTracer.currentOtelSpan.pipe(Effect.orDie)
670
708
  commitsSpan?.addLink({ context: currentSpan.spanContext() })
671
709
 
672
710
  for (const event of events) {
673
- replaceSessionIdSymbol(event.args, this.clientSession.sessionId)
711
+ replaceSessionIdSymbol(event.args, this[StoreInternalsSymbol].clientSession.sessionId)
674
712
  }
675
713
 
676
714
  if (events.length === 0) return
@@ -680,11 +718,11 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
680
718
  const materializeEventsTx = Effect.try({
681
719
  try: () => {
682
720
  const runMaterializeEvents = () => {
683
- return this.syncProcessor.push(events).pipe(Runtime.runSync(localRuntime))
721
+ return this[StoreInternalsSymbol].syncProcessor.push(events).pipe(Runtime.runSync(localRuntime))
684
722
  }
685
723
 
686
724
  if (events.length > 1) {
687
- return this.sqliteDbWrapper.txn(runMaterializeEvents)
725
+ return this[StoreInternalsSymbol].sqliteDbWrapper.txn(runMaterializeEvents)
688
726
  } else {
689
727
  return runMaterializeEvents()
690
728
  }
@@ -697,7 +735,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
697
735
 
698
736
  const tablesToUpdate: [Ref<null, ReactivityGraphContext, RefreshReason>, null][] = []
699
737
  for (const tableName of writeTables) {
700
- const tableRef = this.tableRefs[tableName]
738
+ const tableRef = this[StoreInternalsSymbol].tableRefs[tableName]
701
739
  assertNever(tableRef !== undefined, `No table ref found for ${tableName}`)
702
740
  tablesToUpdate.push([tableRef!, null])
703
741
  }
@@ -710,7 +748,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
710
748
  const skipRefresh = options?.skipRefresh ?? false
711
749
 
712
750
  // Update all table refs together in a batch, to only trigger one reactive update
713
- this.reactivityGraph.setRefs(tablesToUpdate, {
751
+ this[StoreInternalsSymbol].reactivityGraph.setRefs(tablesToUpdate, {
714
752
  debugRefreshReason,
715
753
  skipRefresh,
716
754
  otelContext: otel.trace.setSpan(otel.context.active(), currentSpan),
@@ -725,14 +763,16 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
725
763
  },
726
764
  links: [
727
765
  // Span link to LiveStore:commits
728
- OtelTracer.makeSpanLink({ context: otel.trace.getSpanContext(this.otel.commitsSpanContext)! }),
766
+ OtelTracer.makeSpanLink({
767
+ context: otel.trace.getSpanContext(this[StoreInternalsSymbol].otel.commitsSpanContext)!,
768
+ }),
729
769
  // User-provided span links
730
770
  ...(options?.spanLinks?.map(OtelTracer.makeSpanLink) ?? []),
731
771
  ],
732
772
  }),
733
773
  Effect.tapErrorCause(Effect.logError),
734
774
  Effect.catchAllCause((cause) => Effect.fork(this.shutdown(cause))),
735
- Runtime.runSync(this.effectContext.runtime),
775
+ Runtime.runSync(this[StoreInternalsSymbol].effectContext.runtime),
736
776
  )
737
777
  }
738
778
  // #endregion commit
@@ -775,13 +815,13 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
775
815
  this.checkShutdown('manualRefresh')
776
816
 
777
817
  const { label } = options ?? {}
778
- this.otel.tracer.startActiveSpan(
818
+ this[StoreInternalsSymbol].otel.tracer.startActiveSpan(
779
819
  'LiveStore:manualRefresh',
780
820
  { attributes: { 'livestore.manualRefreshLabel': label } },
781
- this.otel.commitsSpanContext,
821
+ this[StoreInternalsSymbol].otel.commitsSpanContext,
782
822
  (span) => {
783
823
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
784
- this.reactivityGraph.runDeferredEffects({ otelContext })
824
+ this[StoreInternalsSymbol].reactivityGraph.runDeferredEffects({ otelContext })
785
825
  span.end()
786
826
  },
787
827
  )
@@ -795,7 +835,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
795
835
  shutdownPromise = async (cause?: UnexpectedError) => {
796
836
  this.checkShutdown('shutdownPromise')
797
837
 
798
- this.isShutdown = true
838
+ this[StoreInternalsSymbol].isShutdown = true
799
839
  await this.shutdown(cause ? Cause.fail(cause) : undefined).pipe(this.runEffectFork, Fiber.join, Effect.runPromise)
800
840
  }
801
841
 
@@ -805,8 +845,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
805
845
  * This is called automatically when the store was created using the React or Effect API.
806
846
  */
807
847
  shutdown = (cause?: Cause.Cause<UnexpectedError | MaterializeError>): Effect.Effect<void> => {
808
- this.isShutdown = true
809
- return this.clientSession.shutdown(
848
+ this[StoreInternalsSymbol].isShutdown = true
849
+ return this[StoreInternalsSymbol].clientSession.shutdown(
810
850
  cause ? Exit.failCause(cause) : Exit.succeed(IntentionalShutdownCause.make({ reason: 'manual' })),
811
851
  )
812
852
  }
@@ -819,30 +859,33 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
819
859
  _dev = {
820
860
  downloadDb: (source: 'local' | 'leader' = 'local') => {
821
861
  Effect.gen(this, function* () {
822
- const data = source === 'local' ? this.sqliteDbWrapper.export() : yield* this.clientSession.leaderThread.export
862
+ const data =
863
+ source === 'local'
864
+ ? this[StoreInternalsSymbol].sqliteDbWrapper.export()
865
+ : yield* this[StoreInternalsSymbol].clientSession.leaderThread.export
823
866
  downloadBlob(data, `livestore-${Date.now()}.db`)
824
867
  }).pipe(this.runEffectFork)
825
868
  },
826
869
 
827
870
  downloadEventlogDb: () => {
828
871
  Effect.gen(this, function* () {
829
- const data = yield* this.clientSession.leaderThread.getEventlogData
872
+ const data = yield* this[StoreInternalsSymbol].clientSession.leaderThread.getEventlogData
830
873
  downloadBlob(data, `livestore-eventlog-${Date.now()}.db`)
831
874
  }).pipe(this.runEffectFork)
832
875
  },
833
876
 
834
877
  hardReset: (mode: 'all-data' | 'only-app-db' = 'all-data') => {
835
878
  Effect.gen(this, function* () {
836
- const clientId = this.clientSession.clientId
837
- yield* this.clientSession.leaderThread.sendDevtoolsMessage(
879
+ const clientId = this[StoreInternalsSymbol].clientSession.clientId
880
+ yield* this[StoreInternalsSymbol].clientSession.leaderThread.sendDevtoolsMessage(
838
881
  Devtools.Leader.ResetAllData.Request.make({ liveStoreVersion, mode, requestId: nanoid(), clientId }),
839
882
  )
840
883
  }).pipe(this.runEffectFork)
841
884
  },
842
885
 
843
886
  overrideNetworkStatus: (status: 'online' | 'offline') => {
844
- const clientId = this.clientSession.clientId
845
- this.clientSession.leaderThread
887
+ const clientId = this[StoreInternalsSymbol].clientSession.clientId
888
+ this[StoreInternalsSymbol].clientSession.leaderThread
846
889
  .sendDevtoolsMessage(
847
890
  Devtools.Leader.SetSyncLatch.Request.make({
848
891
  clientId,
@@ -856,19 +899,19 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
856
899
 
857
900
  syncStates: () =>
858
901
  Effect.gen(this, function* () {
859
- const session = yield* this.syncProcessor.syncState
860
- const leader = yield* this.clientSession.leaderThread.syncState
902
+ const session = yield* this[StoreInternalsSymbol].syncProcessor.syncState
903
+ const leader = yield* this[StoreInternalsSymbol].clientSession.leaderThread.syncState
861
904
  return { session, leader }
862
905
  }).pipe(this.runEffectPromise),
863
906
 
864
907
  printSyncStates: () => {
865
908
  Effect.gen(this, function* () {
866
- const session = yield* this.syncProcessor.syncState
909
+ const session = yield* this[StoreInternalsSymbol].syncProcessor.syncState
867
910
  yield* Effect.log(
868
911
  `Session sync state: ${session.localHead} (upstream: ${session.upstreamHead})`,
869
912
  session.toJSON(),
870
913
  )
871
- const leader = yield* this.clientSession.leaderThread.syncState
914
+ const leader = yield* this[StoreInternalsSymbol].clientSession.leaderThread.syncState
872
915
  yield* Effect.log(`Leader sync state: ${leader.localHead} (upstream: ${leader.upstreamHead})`, leader.toJSON())
873
916
  }).pipe(this.runEffectFork)
874
917
  },
@@ -876,25 +919,25 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
876
919
  version: liveStoreVersion,
877
920
 
878
921
  otel: {
879
- rootSpanContext: () => otel.trace.getSpan(this.otel.rootSpanContext)?.spanContext(),
922
+ rootSpanContext: () => otel.trace.getSpan(this[StoreInternalsSymbol].otel.rootSpanContext)?.spanContext(),
880
923
  },
881
924
  }
882
925
 
883
926
  // NOTE This is needed because when booting a Store via Effect it seems to call `toJSON` in the error path
884
927
  toJSON = () => ({
885
928
  _tag: 'livestore.Store',
886
- reactivityGraph: this.reactivityGraph.getSnapshot({ includeResults: true }),
929
+ reactivityGraph: this[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults: true }),
887
930
  })
888
931
 
889
932
  private runEffectFork = <A, E>(effect: Effect.Effect<A, E, Scope.Scope>) =>
890
933
  effect.pipe(
891
- Effect.forkIn(this.effectContext.lifetimeScope),
934
+ Effect.forkIn(this[StoreInternalsSymbol].effectContext.lifetimeScope),
892
935
  Effect.tapCauseLogPretty,
893
- Runtime.runFork(this.effectContext.runtime),
936
+ Runtime.runFork(this[StoreInternalsSymbol].effectContext.runtime),
894
937
  )
895
938
 
896
939
  private runEffectPromise = <A, E>(effect: Effect.Effect<A, E, Scope.Scope>) =>
897
- effect.pipe(Effect.tapCauseLogPretty, Runtime.runPromise(this.effectContext.runtime))
940
+ effect.pipe(Effect.tapCauseLogPretty, Runtime.runPromise(this[StoreInternalsSymbol].effectContext.runtime))
898
941
 
899
942
  private getCommitArgs = (
900
943
  firstEventOrTxnFnOrOptions: any,
@@ -926,7 +969,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TConte
926
969
 
927
970
  // for (const event of events) {
928
971
  // if (event.args.id === SessionIdSymbol) {
929
- // event.args.id = this.clientSession.sessionId
972
+ // event.args.id = this.sessionId
930
973
  // }
931
974
  // }
932
975