@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
|
@@ -3,7 +3,7 @@ import { SessionIdSymbol } from '@livestore/common'
|
|
|
3
3
|
import { State } from '@livestore/common/schema'
|
|
4
4
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
5
5
|
import type * as otel from '@opentelemetry/api'
|
|
6
|
-
|
|
6
|
+
import { StoreInternalsSymbol } from '../store/store-types.ts'
|
|
7
7
|
import type { ReactivityGraphContext } from './base-class.ts'
|
|
8
8
|
|
|
9
9
|
export const rowQueryLabel = (
|
|
@@ -30,11 +30,11 @@ export const makeExecBeforeFirstRun =
|
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const otelContext = otelContext_ ?? store.otel.queriesSpanContext
|
|
33
|
+
const otelContext = otelContext_ ?? store[StoreInternalsSymbol].otel.queriesSpanContext
|
|
34
34
|
|
|
35
35
|
const idVal = id === SessionIdSymbol ? store.sessionId : id!
|
|
36
36
|
const rowExists =
|
|
37
|
-
store.sqliteDbWrapper.cachedSelect(
|
|
37
|
+
store[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect(
|
|
38
38
|
`SELECT 1 FROM '${table.sqliteDef.name}' WHERE id = ?`,
|
|
39
39
|
[idVal] as any as PreparedBindValues,
|
|
40
40
|
{ otelContext },
|
|
@@ -5,6 +5,7 @@ import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '
|
|
|
5
5
|
import { assert, expect } from 'vitest'
|
|
6
6
|
|
|
7
7
|
import * as RG from '../reactive.ts'
|
|
8
|
+
import { StoreInternalsSymbol } from '../store/store-types.ts'
|
|
8
9
|
import { events, makeTodoMvc, tables } from '../utils/tests/fixture.ts'
|
|
9
10
|
import { getAllSimplifiedRootSpans, getSimplifiedRootSpan } from '../utils/tests/otel.ts'
|
|
10
11
|
import { computed } from './computed.ts'
|
|
@@ -106,7 +107,7 @@ Vitest.describe('otel', () => {
|
|
|
106
107
|
{ label: 'all todos' },
|
|
107
108
|
)
|
|
108
109
|
|
|
109
|
-
expect(store.reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
110
|
+
expect(store[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
110
111
|
|
|
111
112
|
expect(store.query(query$)).toMatchInlineSnapshot(`
|
|
112
113
|
{
|
|
@@ -116,11 +117,11 @@ Vitest.describe('otel', () => {
|
|
|
116
117
|
}
|
|
117
118
|
`)
|
|
118
119
|
|
|
119
|
-
expect(store.reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
120
|
+
expect(store[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
120
121
|
|
|
121
122
|
store.commit(events.todoCreated({ id: 't1', text: 'buy milk', completed: false }))
|
|
122
123
|
|
|
123
|
-
expect(store.reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
124
|
+
expect(store[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
124
125
|
|
|
125
126
|
expect(store.query(query$)).toMatchInlineSnapshot(`
|
|
126
127
|
{
|
|
@@ -130,7 +131,7 @@ Vitest.describe('otel', () => {
|
|
|
130
131
|
}
|
|
131
132
|
`)
|
|
132
133
|
|
|
133
|
-
expect(store.reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
134
|
+
expect(store[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
|
|
134
135
|
|
|
135
136
|
span.end()
|
|
136
137
|
|
|
@@ -16,6 +16,7 @@ import * as otel from '@opentelemetry/api'
|
|
|
16
16
|
import type { Thunk } from '../reactive.ts'
|
|
17
17
|
import { isThunk, NOT_REFRESHED_YET } from '../reactive.ts'
|
|
18
18
|
import type { RefreshReason } from '../store/store-types.ts'
|
|
19
|
+
import { StoreInternalsSymbol } from '../store/store-types.ts'
|
|
19
20
|
import { isValidFunctionString } from '../utils/function-string.ts'
|
|
20
21
|
import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.ts'
|
|
21
22
|
import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.ts'
|
|
@@ -364,23 +365,25 @@ export class LiveStoreDbQuery<TResultSchema, TResult = TResultSchema> extends Li
|
|
|
364
365
|
const bindValues = queryInputResult.bindValues
|
|
365
366
|
|
|
366
367
|
if (queriedTablesRef.current === undefined) {
|
|
367
|
-
queriedTablesRef.current = store.sqliteDbWrapper.getTablesUsed(sqlString)
|
|
368
|
+
queriedTablesRef.current = store[StoreInternalsSymbol].sqliteDbWrapper.getTablesUsed(sqlString)
|
|
368
369
|
}
|
|
369
370
|
|
|
370
371
|
if (bindValues !== undefined) {
|
|
371
|
-
replaceSessionIdSymbol(bindValues, store.
|
|
372
|
+
replaceSessionIdSymbol(bindValues, store.sessionId)
|
|
372
373
|
}
|
|
373
374
|
|
|
374
375
|
// Establish a reactive dependency on the tables used in the query
|
|
375
376
|
for (const tableName of queriedTablesRef.current) {
|
|
376
|
-
const tableRef =
|
|
377
|
+
const tableRef =
|
|
378
|
+
store[StoreInternalsSymbol].tableRefs[tableName] ??
|
|
379
|
+
shouldNeverHappen(`No table ref found for ${tableName}`)
|
|
377
380
|
get(tableRef, otelContext, debugRefreshReason)
|
|
378
381
|
}
|
|
379
382
|
|
|
380
383
|
span.setAttribute('sql.query', sqlString)
|
|
381
384
|
span.updateName(`db:${sqlString.slice(0, 50)}`)
|
|
382
385
|
|
|
383
|
-
const rawDbResults = store.sqliteDbWrapper.cachedSelect<any>(
|
|
386
|
+
const rawDbResults = store[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect<any>(
|
|
384
387
|
sqlString,
|
|
385
388
|
bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
|
|
386
389
|
{
|
package/src/mod.ts
CHANGED
|
@@ -37,20 +37,20 @@ 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
|
-
Queryable,
|
|
43
|
-
QueryDebugInfo,
|
|
44
|
-
RefreshReason,
|
|
45
|
-
SubscribeOptions,
|
|
46
|
-
Unsubscribe,
|
|
47
|
-
} from './store/store-types.ts'
|
|
48
40
|
export {
|
|
49
41
|
isQueryable,
|
|
50
42
|
type LiveStoreContext,
|
|
51
43
|
type LiveStoreContextRunning,
|
|
52
44
|
makeShutdownDeferred,
|
|
45
|
+
type OtelOptions,
|
|
46
|
+
type Queryable,
|
|
47
|
+
type QueryDebugInfo,
|
|
48
|
+
type RefreshReason,
|
|
53
49
|
type ShutdownDeferred,
|
|
50
|
+
type StoreInternals,
|
|
51
|
+
StoreInternalsSymbol,
|
|
52
|
+
type SubscribeOptions,
|
|
53
|
+
type Unsubscribe,
|
|
54
54
|
} from './store/store-types.ts'
|
|
55
55
|
export { exposeDebugUtils } from './utils/dev.ts'
|
|
56
56
|
export * from './utils/stack-info.ts'
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type IntentionalShutdownCause,
|
|
8
8
|
type InvalidPullError,
|
|
9
9
|
type IsOfflineError,
|
|
10
|
+
LogConfig,
|
|
10
11
|
type MaterializeError,
|
|
11
12
|
type MigrationsReport,
|
|
12
13
|
provideOtel,
|
|
@@ -23,8 +24,6 @@ import {
|
|
|
23
24
|
Fiber,
|
|
24
25
|
identity,
|
|
25
26
|
Layer,
|
|
26
|
-
Logger,
|
|
27
|
-
LogLevel,
|
|
28
27
|
OtelTracer,
|
|
29
28
|
Queue,
|
|
30
29
|
Runtime,
|
|
@@ -42,6 +41,7 @@ import type {
|
|
|
42
41
|
OtelOptions,
|
|
43
42
|
ShutdownDeferred,
|
|
44
43
|
} from './store-types.ts'
|
|
44
|
+
import { StoreInternalsSymbol } from './store-types.ts'
|
|
45
45
|
|
|
46
46
|
export const DEFAULT_PARAMS = {
|
|
47
47
|
leaderPushBatchSize: 100,
|
|
@@ -117,7 +117,7 @@ export interface CreateStoreOptions<
|
|
|
117
117
|
TSchema extends LiveStoreSchema,
|
|
118
118
|
TContext = {},
|
|
119
119
|
TSyncPayloadSchema extends Schema.Schema<any> = typeof Schema.JsonValue,
|
|
120
|
-
> {
|
|
120
|
+
> extends LogConfig.WithLoggerOptions {
|
|
121
121
|
schema: TSchema
|
|
122
122
|
adapter: Adapter
|
|
123
123
|
storeId: string
|
|
@@ -205,8 +205,7 @@ export const createStorePromise = async <
|
|
|
205
205
|
provideOtel(omitUndefineds({ parentSpanContext: otelOptions?.rootSpanContext, otelTracer: otelOptions?.tracer })),
|
|
206
206
|
Effect.tapCauseLogPretty,
|
|
207
207
|
Effect.annotateLogs({ thread: 'window' }),
|
|
208
|
-
|
|
209
|
-
Logger.withMinimumLogLevel(LogLevel.Debug),
|
|
208
|
+
LogConfig.withLoggerConfig(options, { threadName: 'window' }),
|
|
210
209
|
Effect.runPromise,
|
|
211
210
|
)
|
|
212
211
|
|
|
@@ -348,7 +347,7 @@ export const createStore = <
|
|
|
348
347
|
})
|
|
349
348
|
|
|
350
349
|
// Starts background fibers (syncing, event processing, etc) for store
|
|
351
|
-
yield* store.boot
|
|
350
|
+
yield* store[StoreInternalsSymbol].boot
|
|
352
351
|
|
|
353
352
|
if (boot !== undefined) {
|
|
354
353
|
// TODO also incorporate `boot` function progress into `bootStatusQueue`
|
|
@@ -366,7 +365,7 @@ export const createStore = <
|
|
|
366
365
|
|
|
367
366
|
if (batchUpdates !== undefined) {
|
|
368
367
|
// Replacing the default batchUpdates function with the provided one after boot
|
|
369
|
-
store.reactivityGraph.context!.effectsWrapper = batchUpdates
|
|
368
|
+
store[StoreInternalsSymbol].reactivityGraph.context!.effectsWrapper = batchUpdates
|
|
370
369
|
}
|
|
371
370
|
|
|
372
371
|
yield* Deferred.succeed(storeDeferred, store as any as Store)
|
package/src/store/devtools.ts
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DebugInfo, SyncState } from '@livestore/common'
|
|
2
2
|
import { Devtools, liveStoreVersion, UnexpectedError } from '@livestore/common'
|
|
3
3
|
import { throttle } from '@livestore/utils'
|
|
4
4
|
import type { WebChannel } from '@livestore/utils/effect'
|
|
5
5
|
import { Effect, Stream } from '@livestore/utils/effect'
|
|
6
6
|
import { nanoid } from '@livestore/utils/nanoid'
|
|
7
7
|
|
|
8
|
-
import type { LiveQuery, ReactivityGraph } from '../live-queries/base-class.ts'
|
|
9
8
|
import { NOT_REFRESHED_YET } from '../reactive.ts'
|
|
10
|
-
import type { SqliteDbWrapper } from '../SqliteDbWrapper.ts'
|
|
11
9
|
import { emptyDebugInfo as makeEmptyDebugInfo } from '../SqliteDbWrapper.ts'
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
type IStore = {
|
|
15
|
-
clientSession: ClientSession
|
|
16
|
-
reactivityGraph: ReactivityGraph
|
|
17
|
-
sqliteDbWrapper: SqliteDbWrapper
|
|
18
|
-
activeQueries: ReferenceCountedSet<LiveQuery<any>>
|
|
19
|
-
syncProcessor: ClientSessionSyncProcessor
|
|
20
|
-
}
|
|
10
|
+
import type { Store } from './store.ts'
|
|
11
|
+
import { StoreInternalsSymbol } from './store-types.ts'
|
|
21
12
|
|
|
22
13
|
type Unsub = () => void
|
|
23
14
|
type RequestId = string
|
|
@@ -40,7 +31,7 @@ export const connectDevtoolsToStore = ({
|
|
|
40
31
|
Devtools.ClientSession.MessageToApp,
|
|
41
32
|
Devtools.ClientSession.MessageFromApp
|
|
42
33
|
>
|
|
43
|
-
store:
|
|
34
|
+
store: Store
|
|
44
35
|
}) =>
|
|
45
36
|
Effect.gen(function* () {
|
|
46
37
|
const reactivityGraphSubcriptions: SubMap = new Map()
|
|
@@ -48,7 +39,7 @@ export const connectDevtoolsToStore = ({
|
|
|
48
39
|
const debugInfoHistorySubscriptions: SubMap = new Map()
|
|
49
40
|
const syncHeadClientSessionSubscriptions: SubMap = new Map()
|
|
50
41
|
|
|
51
|
-
const { clientId, sessionId } = store.clientSession
|
|
42
|
+
const { clientId, sessionId } = store[StoreInternalsSymbol].clientSession
|
|
52
43
|
|
|
53
44
|
yield* Effect.addFinalizer(() =>
|
|
54
45
|
Effect.sync(() => {
|
|
@@ -73,7 +64,21 @@ export const connectDevtoolsToStore = ({
|
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
if (decodedMessage._tag === 'LSD.ClientSession.Disconnect') {
|
|
76
|
-
//
|
|
67
|
+
// Gracefully tear down all DevTools subscriptions and close the channel.
|
|
68
|
+
// This prevents background fibers from lingering after DevTools closes
|
|
69
|
+
// (e.g. when a window is closed without sending explicit unsubs).
|
|
70
|
+
for (const unsub of reactivityGraphSubcriptions.values()) unsub()
|
|
71
|
+
reactivityGraphSubcriptions.clear()
|
|
72
|
+
for (const unsub of liveQueriesSubscriptions.values()) unsub()
|
|
73
|
+
liveQueriesSubscriptions.clear()
|
|
74
|
+
for (const unsub of debugInfoHistorySubscriptions.values()) unsub()
|
|
75
|
+
debugInfoHistorySubscriptions.clear()
|
|
76
|
+
for (const unsub of syncHeadClientSessionSubscriptions.values()) unsub()
|
|
77
|
+
syncHeadClientSessionSubscriptions.clear()
|
|
78
|
+
|
|
79
|
+
// Signal the WebChannel to shut down; this causes the `listen` stream
|
|
80
|
+
// to complete and allows the surrounding scoped fiber to exit.
|
|
81
|
+
storeDevtoolsChannel.shutdown.pipe(Effect.runFork)
|
|
77
82
|
return
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -103,7 +108,7 @@ export const connectDevtoolsToStore = ({
|
|
|
103
108
|
() =>
|
|
104
109
|
sendToDevtools(
|
|
105
110
|
Devtools.ClientSession.ReactivityGraphRes.make({
|
|
106
|
-
reactivityGraph: store.reactivityGraph.getSnapshot({ includeResults }),
|
|
111
|
+
reactivityGraph: store[StoreInternalsSymbol].reactivityGraph.getSnapshot({ includeResults }),
|
|
107
112
|
requestId: nanoid(10),
|
|
108
113
|
clientId,
|
|
109
114
|
sessionId,
|
|
@@ -121,14 +126,17 @@ export const connectDevtoolsToStore = ({
|
|
|
121
126
|
// This might need to be tweaked further and possibly be exposed to the user in some way.
|
|
122
127
|
const throttledSend = throttle(send, 20)
|
|
123
128
|
|
|
124
|
-
reactivityGraphSubcriptions.set(
|
|
129
|
+
reactivityGraphSubcriptions.set(
|
|
130
|
+
subscriptionId,
|
|
131
|
+
store[StoreInternalsSymbol].reactivityGraph.subscribeToRefresh(throttledSend),
|
|
132
|
+
)
|
|
125
133
|
|
|
126
134
|
break
|
|
127
135
|
}
|
|
128
136
|
case 'LSD.ClientSession.DebugInfoReq': {
|
|
129
137
|
sendToDevtools(
|
|
130
138
|
Devtools.ClientSession.DebugInfoRes.make({
|
|
131
|
-
debugInfo: store.sqliteDbWrapper.debugInfo,
|
|
139
|
+
debugInfo: store[StoreInternalsSymbol].sqliteDbWrapper.debugInfo,
|
|
132
140
|
requestId,
|
|
133
141
|
clientId,
|
|
134
142
|
sessionId,
|
|
@@ -144,13 +152,13 @@ export const connectDevtoolsToStore = ({
|
|
|
144
152
|
let tickHandle: number | undefined
|
|
145
153
|
|
|
146
154
|
const tick = () => {
|
|
147
|
-
buffer.push(store.sqliteDbWrapper.debugInfo)
|
|
155
|
+
buffer.push(store[StoreInternalsSymbol].sqliteDbWrapper.debugInfo)
|
|
148
156
|
|
|
149
157
|
// NOTE this resets the debug info, so all other "readers" e.g. in other `requestAnimationFrame` loops,
|
|
150
158
|
// will get the empty debug info
|
|
151
159
|
// TODO We need to come up with a more graceful way to do store. Probably via a single global
|
|
152
160
|
// `requestAnimationFrame` loop that is passed in somehow.
|
|
153
|
-
store.sqliteDbWrapper.debugInfo = makeEmptyDebugInfo()
|
|
161
|
+
store[StoreInternalsSymbol].sqliteDbWrapper.debugInfo = makeEmptyDebugInfo()
|
|
154
162
|
|
|
155
163
|
if (buffer.length > 10) {
|
|
156
164
|
sendToDevtools(
|
|
@@ -194,7 +202,7 @@ export const connectDevtoolsToStore = ({
|
|
|
194
202
|
break
|
|
195
203
|
}
|
|
196
204
|
case 'LSD.ClientSession.DebugInfoResetReq': {
|
|
197
|
-
store.sqliteDbWrapper.debugInfo.slowQueries.clear()
|
|
205
|
+
store[StoreInternalsSymbol].sqliteDbWrapper.debugInfo.slowQueries.clear()
|
|
198
206
|
sendToDevtools(
|
|
199
207
|
Devtools.ClientSession.DebugInfoResetRes.make({ requestId, clientId, sessionId, liveStoreVersion }),
|
|
200
208
|
)
|
|
@@ -202,7 +210,10 @@ export const connectDevtoolsToStore = ({
|
|
|
202
210
|
}
|
|
203
211
|
case 'LSD.ClientSession.DebugInfoRerunQueryReq': {
|
|
204
212
|
const { queryStr, bindValues, queriedTables } = decodedMessage
|
|
205
|
-
store.sqliteDbWrapper.cachedSelect(queryStr, bindValues, {
|
|
213
|
+
store[StoreInternalsSymbol].sqliteDbWrapper.cachedSelect(queryStr, bindValues, {
|
|
214
|
+
queriedTables,
|
|
215
|
+
skipCache: true,
|
|
216
|
+
})
|
|
206
217
|
sendToDevtools(
|
|
207
218
|
Devtools.ClientSession.DebugInfoRerunQueryRes.make({ requestId, clientId, sessionId, liveStoreVersion }),
|
|
208
219
|
)
|
|
@@ -223,7 +234,7 @@ export const connectDevtoolsToStore = ({
|
|
|
223
234
|
() =>
|
|
224
235
|
sendToDevtools(
|
|
225
236
|
Devtools.ClientSession.LiveQueriesRes.make({
|
|
226
|
-
liveQueries: [...store.activeQueries].map((q) => ({
|
|
237
|
+
liveQueries: [...store[StoreInternalsSymbol].activeQueries].map((q) => ({
|
|
227
238
|
_tag: q._tag,
|
|
228
239
|
id: q.id,
|
|
229
240
|
label: q.label,
|
|
@@ -251,7 +262,10 @@ export const connectDevtoolsToStore = ({
|
|
|
251
262
|
// Same as in the reactivity graph subscription case above, we need to throttle the updates
|
|
252
263
|
const throttledSend = throttle(send, 20)
|
|
253
264
|
|
|
254
|
-
liveQueriesSubscriptions.set(
|
|
265
|
+
liveQueriesSubscriptions.set(
|
|
266
|
+
subscriptionId,
|
|
267
|
+
store[StoreInternalsSymbol].reactivityGraph.subscribeToRefresh(throttledSend),
|
|
268
|
+
)
|
|
255
269
|
|
|
256
270
|
break
|
|
257
271
|
}
|
|
@@ -278,11 +292,11 @@ export const connectDevtoolsToStore = ({
|
|
|
278
292
|
}),
|
|
279
293
|
)
|
|
280
294
|
|
|
281
|
-
send(store.syncProcessor.syncState.pipe(Effect.runSync))
|
|
295
|
+
send(store[StoreInternalsSymbol].syncProcessor.syncState.pipe(Effect.runSync))
|
|
282
296
|
|
|
283
297
|
syncHeadClientSessionSubscriptions.set(
|
|
284
298
|
subscriptionId,
|
|
285
|
-
store.syncProcessor.syncState.changes.pipe(
|
|
299
|
+
store[StoreInternalsSymbol].syncProcessor.syncState.changes.pipe(
|
|
286
300
|
Stream.tap((syncState) => send(syncState)),
|
|
287
301
|
Stream.runDrain,
|
|
288
302
|
Effect.interruptible,
|
package/src/store/store-types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ClientSession,
|
|
3
|
+
type ClientSessionSyncProcessor,
|
|
3
4
|
type ClientSessionSyncProcessorSimulationParams,
|
|
4
5
|
type IntentionalShutdownCause,
|
|
5
6
|
type InvalidPullError,
|
|
@@ -12,13 +13,20 @@ import {
|
|
|
12
13
|
type UnexpectedError,
|
|
13
14
|
} from '@livestore/common'
|
|
14
15
|
import type { EventSequenceNumber, LiveStoreEvent, LiveStoreSchema } from '@livestore/common/schema'
|
|
15
|
-
import type { Effect, Runtime, Scope } from '@livestore/utils/effect'
|
|
16
|
+
import type { Effect, Runtime, Schema, Scope } from '@livestore/utils/effect'
|
|
16
17
|
import { Deferred, Predicate } from '@livestore/utils/effect'
|
|
17
18
|
import type * as otel from '@opentelemetry/api'
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
import type {
|
|
20
|
+
LiveQuery,
|
|
21
|
+
LiveQueryDef,
|
|
22
|
+
ReactivityGraph,
|
|
23
|
+
ReactivityGraphContext,
|
|
24
|
+
SignalDef,
|
|
25
|
+
} from '../live-queries/base-class.ts'
|
|
20
26
|
import { TypeId } from '../live-queries/base-class.ts'
|
|
21
|
-
import type { DebugRefreshReasonBase } from '../reactive.ts'
|
|
27
|
+
import type { DebugRefreshReasonBase, Ref } from '../reactive.ts'
|
|
28
|
+
import type { SqliteDbWrapper } from '../SqliteDbWrapper.ts'
|
|
29
|
+
import type { ReferenceCountedSet } from '../utils/data-structures.ts'
|
|
22
30
|
import type { StackInfo } from '../utils/stack-info.ts'
|
|
23
31
|
import type { Store } from './store.ts'
|
|
24
32
|
|
|
@@ -52,6 +60,101 @@ export type OtelOptions = {
|
|
|
52
60
|
rootSpanContext: otel.Context
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
export const StoreInternalsSymbol = Symbol.for('livestore.StoreInternals')
|
|
64
|
+
export type StoreInternalsSymbol = typeof StoreInternalsSymbol
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Opaque bag containing the Store's implementation details.
|
|
68
|
+
*
|
|
69
|
+
* Not part of the public API — shapes and semantics may change without notice.
|
|
70
|
+
* Access only from within the @livestore/livestore package (and Devtools) via
|
|
71
|
+
* `StoreInternalsSymbol` to avoid accidental coupling in application code.
|
|
72
|
+
*/
|
|
73
|
+
export type StoreInternals = {
|
|
74
|
+
/**
|
|
75
|
+
* Runtime event schema used for encoding/decoding events.
|
|
76
|
+
*
|
|
77
|
+
* Exposed primarily for Devtools (e.g. databrowser) to validate ad‑hoc
|
|
78
|
+
* event payloads. Application code should not depend on it directly.
|
|
79
|
+
*/
|
|
80
|
+
readonly eventSchema: Schema.Schema<LiveStoreEvent.AnyDecoded, LiveStoreEvent.AnyEncoded>
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The active client session backing this Store. Provides access to the
|
|
84
|
+
* leader thread, network status, and shutdown signaling.
|
|
85
|
+
*
|
|
86
|
+
* Do not close or mutate directly — use `store.shutdown(...)`.
|
|
87
|
+
*/
|
|
88
|
+
readonly clientSession: ClientSession
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Wrapper around the local SQLite state database. Centralizes query
|
|
92
|
+
* planning, caching, and change tracking used by reads and materializers.
|
|
93
|
+
*/
|
|
94
|
+
readonly sqliteDbWrapper: SqliteDbWrapper
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Effect runtime and scope used to fork background fibers for the Store.
|
|
98
|
+
*
|
|
99
|
+
* - `runtime` executes effects from imperative Store APIs.
|
|
100
|
+
* - `lifetimeScope` owns forked fibers; closed during Store shutdown.
|
|
101
|
+
*/
|
|
102
|
+
readonly effectContext: {
|
|
103
|
+
/** Effect runtime to run Store effects with proper environment. */
|
|
104
|
+
readonly runtime: Runtime.Runtime<Scope.Scope>
|
|
105
|
+
/** Scope that owns all long‑lived fibers spawned by the Store. */
|
|
106
|
+
readonly lifetimeScope: Scope.Scope
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* OpenTelemetry primitives used for instrumentation of commits, queries,
|
|
111
|
+
* and Store boot lifecycle.
|
|
112
|
+
*/
|
|
113
|
+
readonly otel: StoreOtel
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The Store's reactive graph instance used to model dependencies and
|
|
117
|
+
* propagate updates. Provides APIs to create refs/thunks/effects and to
|
|
118
|
+
* subscribe to refresh cycles.
|
|
119
|
+
*/
|
|
120
|
+
readonly reactivityGraph: ReactivityGraph
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Per‑table reactive refs used to broadcast invalidations when materializers
|
|
124
|
+
* write to tables. Values are always `null`; equality is intentionally
|
|
125
|
+
* `false` to force recomputation.
|
|
126
|
+
*
|
|
127
|
+
* Keys are SQLite table names (user tables; some system tables may be
|
|
128
|
+
* intentionally excluded from refresh).
|
|
129
|
+
*/
|
|
130
|
+
readonly tableRefs: Readonly<Record<string, Ref<null, ReactivityGraphContext, RefreshReason>>>
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Set of currently subscribed LiveQuery instances (reference‑counted).
|
|
134
|
+
* Used for Devtools and diagnostics.
|
|
135
|
+
*/
|
|
136
|
+
readonly activeQueries: ReferenceCountedSet<LiveQuery<any>>
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Client‑session sync processor orchestrating push/pull and materialization
|
|
140
|
+
* of events into local state.
|
|
141
|
+
*/
|
|
142
|
+
readonly syncProcessor: ClientSessionSyncProcessor
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Starts background fibers for sync and observation. Must be run exactly
|
|
146
|
+
* once per Store instance. Scoped; installs finalizers to end spans and
|
|
147
|
+
* detach reactive refs.
|
|
148
|
+
*/
|
|
149
|
+
readonly boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Tracks whether the Store has been shut down. When true, mutating APIs
|
|
153
|
+
* should reject via `checkShutdown`.
|
|
154
|
+
*/
|
|
155
|
+
isShutdown: boolean
|
|
156
|
+
}
|
|
157
|
+
|
|
55
158
|
export type StoreOptions<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}> = {
|
|
56
159
|
clientSession: ClientSession
|
|
57
160
|
schema: TSchema
|