@livestore/livestore 0.3.0-dev.11 → 0.3.0-dev.13
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.js +0 -1
- package/dist/SqliteDbWrapper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/live-queries/base-class.d.ts +2 -9
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +3 -5
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +1 -2
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/db-query.d.ts.map +1 -1
- package/dist/live-queries/db-query.js +1 -2
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/db-query.test.js +46 -56
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/live-queries/graphql.d.ts.map +1 -1
- package/dist/live-queries/graphql.js +1 -2
- package/dist/live-queries/graphql.js.map +1 -1
- package/dist/reactive.d.ts +8 -4
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +9 -11
- package/dist/reactive.js.map +1 -1
- package/dist/reactive.test.js +1 -1
- package/dist/reactive.test.js.map +1 -1
- package/dist/row-query-utils.d.ts +2 -2
- package/dist/row-query-utils.d.ts.map +1 -1
- package/dist/row-query-utils.js +3 -3
- package/dist/row-query-utils.js.map +1 -1
- package/dist/store/create-store.d.ts +5 -2
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +15 -1
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts +3 -2
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +30 -8
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store.d.ts +3 -2
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +8 -3
- package/dist/store/store.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +15 -15
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/fixture.js +1 -1
- package/dist/utils/tests/fixture.js.map +1 -1
- package/package.json +5 -6
- package/src/SqliteDbWrapper.ts +0 -1
- package/src/index.ts +1 -2
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +0 -3
- package/src/live-queries/base-class.ts +5 -14
- package/src/live-queries/computed.ts +1 -2
- package/src/live-queries/db-query.test.ts +28 -26
- package/src/live-queries/db-query.ts +1 -2
- package/src/live-queries/graphql.ts +1 -2
- package/src/reactive.test.ts +1 -1
- package/src/reactive.ts +14 -16
- package/src/row-query-utils.ts +5 -5
- package/src/store/create-store.ts +35 -4
- package/src/store/devtools.ts +57 -12
- package/src/store/store.ts +11 -4
- package/src/utils/tests/fixture.ts +1 -1
- package/tsconfig.json +1 -2
package/src/reactive.ts
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
/* eslint-disable prefer-arrow/prefer-arrow-functions */
|
|
25
25
|
|
|
26
26
|
import { BoundArray } from '@livestore/common'
|
|
27
|
-
import type { PrettifyFlat } from '@livestore/utils'
|
|
28
27
|
import { deepEqual, shouldNeverHappen } from '@livestore/utils'
|
|
28
|
+
import type { Types } from '@livestore/utils/effect'
|
|
29
29
|
import type * as otel from '@opentelemetry/api'
|
|
30
30
|
// import { getDurationMsFromSpan } from './otel.js'
|
|
31
31
|
|
|
@@ -144,7 +144,7 @@ const encodedOptionNone = <A>(): EncodedOption<A> => ({ _tag: 'None' })
|
|
|
144
144
|
export type SerializedAtom = SerializedRef | SerializedThunk
|
|
145
145
|
|
|
146
146
|
export type SerializedRef = Readonly<
|
|
147
|
-
|
|
147
|
+
Types.Simplify<
|
|
148
148
|
Pick<Ref<unknown, unknown, any>, '_tag' | 'id' | 'label' | 'meta' | 'isDirty' | 'isDestroyed' | 'refreshes'> & {
|
|
149
149
|
/** Is `None` if `getSnapshot` was called with `includeResults: false` which is the default */
|
|
150
150
|
previousResult: EncodedOption<string>
|
|
@@ -155,7 +155,7 @@ export type SerializedRef = Readonly<
|
|
|
155
155
|
>
|
|
156
156
|
|
|
157
157
|
export type SerializedThunk = Readonly<
|
|
158
|
-
|
|
158
|
+
Types.Simplify<
|
|
159
159
|
Pick<
|
|
160
160
|
Thunk<unknown, unknown, any>,
|
|
161
161
|
'_tag' | 'id' | 'label' | 'meta' | 'isDirty' | 'isDestroyed' | 'recomputations'
|
|
@@ -169,7 +169,7 @@ export type SerializedThunk = Readonly<
|
|
|
169
169
|
>
|
|
170
170
|
|
|
171
171
|
export type SerializedEffect = Readonly<
|
|
172
|
-
|
|
172
|
+
Types.Simplify<
|
|
173
173
|
Pick<Effect<any>, '_tag' | 'id' | 'label' | 'invocations' | 'isDestroyed'> & {
|
|
174
174
|
sub: ReadonlyArray<string>
|
|
175
175
|
}
|
|
@@ -183,18 +183,11 @@ export type ReactiveGraphSnapshot = {
|
|
|
183
183
|
readonly deferredEffects: ReadonlyArray<string>
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
let nodeIdCounter = 0
|
|
187
|
-
const uniqueNodeId = () => `node-${++nodeIdCounter}`
|
|
188
|
-
let refreshInfoIdCounter = 0
|
|
189
|
-
const uniqueRefreshInfoId = () => `refresh-info-${++refreshInfoIdCounter}`
|
|
190
|
-
|
|
191
186
|
let globalGraphIdCounter = 0
|
|
192
187
|
const uniqueGraphId = () => `graph-${++globalGraphIdCounter}`
|
|
193
188
|
|
|
194
189
|
/** Used for testing */
|
|
195
190
|
export const __resetIds = () => {
|
|
196
|
-
nodeIdCounter = 0
|
|
197
|
-
refreshInfoIdCounter = 0
|
|
198
191
|
globalGraphIdCounter = 0
|
|
199
192
|
}
|
|
200
193
|
|
|
@@ -220,13 +213,18 @@ export class ReactiveGraph<
|
|
|
220
213
|
|
|
221
214
|
private refreshCallbacks: Set<() => void> = new Set()
|
|
222
215
|
|
|
216
|
+
private nodeIdCounter = 0
|
|
217
|
+
private uniqueNodeId = () => `node-${++this.nodeIdCounter}`
|
|
218
|
+
private refreshInfoIdCounter = 0
|
|
219
|
+
private uniqueRefreshInfoId = () => `refresh-info-${++this.refreshInfoIdCounter}`
|
|
220
|
+
|
|
223
221
|
makeRef<T>(
|
|
224
222
|
val: T,
|
|
225
223
|
options?: { label?: string; meta?: unknown; equal?: (a: T, b: T) => boolean },
|
|
226
224
|
): Ref<T, TContext, TDebugRefreshReason> {
|
|
227
225
|
const ref: Ref<T, TContext, TDebugRefreshReason> = {
|
|
228
226
|
_tag: 'ref',
|
|
229
|
-
id: uniqueNodeId(),
|
|
227
|
+
id: this.uniqueNodeId(),
|
|
230
228
|
isDirty: false,
|
|
231
229
|
isDestroyed: false,
|
|
232
230
|
previousResult: val,
|
|
@@ -262,7 +260,7 @@ export class ReactiveGraph<
|
|
|
262
260
|
): Thunk<T, TContext, TDebugRefreshReason> {
|
|
263
261
|
const thunk: Thunk<T, TContext, TDebugRefreshReason> = {
|
|
264
262
|
_tag: 'thunk',
|
|
265
|
-
id: uniqueNodeId(),
|
|
263
|
+
id: this.uniqueNodeId(),
|
|
266
264
|
previousResult: NOT_REFRESHED_YET,
|
|
267
265
|
isDirty: true,
|
|
268
266
|
isDestroyed: false,
|
|
@@ -314,7 +312,7 @@ export class ReactiveGraph<
|
|
|
314
312
|
this.currentDebugRefresh = undefined
|
|
315
313
|
|
|
316
314
|
this.debugRefreshInfos.push({
|
|
317
|
-
id: uniqueRefreshInfoId(),
|
|
315
|
+
id: this.uniqueRefreshInfoId(),
|
|
318
316
|
reason: debugRefreshReason ?? ({ _tag: 'makeThunk', label: options?.label } as TDebugRefreshReason),
|
|
319
317
|
skippedRefresh: false,
|
|
320
318
|
refreshedAtoms,
|
|
@@ -387,7 +385,7 @@ export class ReactiveGraph<
|
|
|
387
385
|
): Effect<TDebugRefreshReason> {
|
|
388
386
|
const effect: Effect<TDebugRefreshReason> = {
|
|
389
387
|
_tag: 'effect',
|
|
390
|
-
id: uniqueNodeId(),
|
|
388
|
+
id: this.uniqueNodeId(),
|
|
391
389
|
isDestroyed: false,
|
|
392
390
|
doEffect: (otelContext, debugRefreshReason) => {
|
|
393
391
|
effect.invocations++
|
|
@@ -488,7 +486,7 @@ export class ReactiveGraph<
|
|
|
488
486
|
this.currentDebugRefresh = undefined
|
|
489
487
|
|
|
490
488
|
const refreshDebugInfo: RefreshDebugInfo<TDebugRefreshReason, TDebugThunkInfo> = {
|
|
491
|
-
id: uniqueRefreshInfoId(),
|
|
489
|
+
id: this.uniqueRefreshInfoId(),
|
|
492
490
|
reason: options.debugRefreshReason,
|
|
493
491
|
skippedRefresh: false,
|
|
494
492
|
refreshedAtoms,
|
package/src/row-query-utils.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type * as otel from '@opentelemetry/api'
|
|
|
7
7
|
import type { GetResult, LiveQueryDef, ReactivityGraphContext } from './live-queries/base-class.js'
|
|
8
8
|
import { computed } from './live-queries/computed.js'
|
|
9
9
|
|
|
10
|
-
export const rowQueryLabel = (table: DbSchema.TableDefBase, id: string | SessionIdSymbol | undefined) =>
|
|
10
|
+
export const rowQueryLabel = (table: DbSchema.TableDefBase, id: string | SessionIdSymbol | number | undefined) =>
|
|
11
11
|
`row:${table.sqliteDef.name}${id === undefined ? '' : id === SessionIdSymbol ? `:sessionId` : `:${id}`}`
|
|
12
12
|
|
|
13
13
|
export const deriveColQuery: {
|
|
@@ -41,7 +41,7 @@ export const makeExecBeforeFirstRun =
|
|
|
41
41
|
table,
|
|
42
42
|
otelContext: otelContext_,
|
|
43
43
|
}: {
|
|
44
|
-
id?: string | SessionIdSymbol
|
|
44
|
+
id?: string | SessionIdSymbol | number
|
|
45
45
|
insertValues?: any
|
|
46
46
|
table: DbSchema.TableDefBase
|
|
47
47
|
otelContext: otel.Context | undefined
|
|
@@ -50,11 +50,11 @@ export const makeExecBeforeFirstRun =
|
|
|
50
50
|
const otelContext = otelContext_ ?? store.otel.queriesSpanContext
|
|
51
51
|
|
|
52
52
|
if (table.options.isSingleton === false) {
|
|
53
|
-
const
|
|
53
|
+
const idVal = id === SessionIdSymbol ? store.sessionId : id!
|
|
54
54
|
const rowExists =
|
|
55
55
|
store.sqliteDbWrapper.select(
|
|
56
56
|
`SELECT 1 FROM '${table.sqliteDef.name}' WHERE id = ?`,
|
|
57
|
-
[
|
|
57
|
+
[idVal] as any as PreparedBindValues,
|
|
58
58
|
{ otelContext },
|
|
59
59
|
).length === 1
|
|
60
60
|
|
|
@@ -69,7 +69,7 @@ export const makeExecBeforeFirstRun =
|
|
|
69
69
|
// It's important that we only mutate and don't refresh here, as this function might be called during a render
|
|
70
70
|
// and otherwise we might end up in a "reactive loop"
|
|
71
71
|
store.mutate(
|
|
72
|
-
{ otelContext, skipRefresh: true, label: `rowQuery:${table.sqliteDef.name}:${
|
|
72
|
+
{ otelContext, skipRefresh: true, label: `rowQuery:${table.sqliteDef.name}:${idVal}` },
|
|
73
73
|
table.insert({ id, ...insertValues }),
|
|
74
74
|
)
|
|
75
75
|
}
|
|
@@ -2,8 +2,9 @@ import type {
|
|
|
2
2
|
Adapter,
|
|
3
3
|
BootStatus,
|
|
4
4
|
ClientSession,
|
|
5
|
+
ClientSessionDevtoolsChannel,
|
|
5
6
|
IntentionalShutdownCause,
|
|
6
|
-
|
|
7
|
+
MigrationsReport,
|
|
7
8
|
} from '@livestore/common'
|
|
8
9
|
import { provideOtel, UnexpectedError } from '@livestore/common'
|
|
9
10
|
import type { EventId, LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
|
|
@@ -39,7 +40,10 @@ export interface CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext,
|
|
|
39
40
|
graphQLOptions?: GraphQLOptions<TGraphQLContext>
|
|
40
41
|
boot?: (
|
|
41
42
|
store: Store<TGraphQLContext, TSchema>,
|
|
42
|
-
|
|
43
|
+
ctx: {
|
|
44
|
+
migrationsReport: MigrationsReport
|
|
45
|
+
parentSpan: otel.Span
|
|
46
|
+
},
|
|
43
47
|
) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer | LiveStoreContextRunning>
|
|
44
48
|
batchUpdates?: (run: () => void) => void
|
|
45
49
|
disableDevtools?: boolean
|
|
@@ -107,6 +111,8 @@ export const createStore = <
|
|
|
107
111
|
Effect.gen(function* () {
|
|
108
112
|
const lifetimeScope = yield* Scope.make()
|
|
109
113
|
|
|
114
|
+
yield* validateStoreId(storeId)
|
|
115
|
+
|
|
110
116
|
yield* Effect.addFinalizer((_) => Scope.close(lifetimeScope, _))
|
|
111
117
|
|
|
112
118
|
const debugInstanceId = debug?.instanceId ?? nanoid(10)
|
|
@@ -128,7 +134,7 @@ export const createStore = <
|
|
|
128
134
|
|
|
129
135
|
const storeDeferred = yield* Deferred.make<Store>()
|
|
130
136
|
|
|
131
|
-
const connectDevtoolsToStore_ = (storeDevtoolsChannel:
|
|
137
|
+
const connectDevtoolsToStore_ = (storeDevtoolsChannel: ClientSessionDevtoolsChannel) =>
|
|
132
138
|
Effect.gen(function* () {
|
|
133
139
|
const store = yield* storeDeferred
|
|
134
140
|
yield* connectDevtoolsToStore({ storeDevtoolsChannel, store })
|
|
@@ -158,6 +164,16 @@ export const createStore = <
|
|
|
158
164
|
debugInstanceId,
|
|
159
165
|
}).pipe(Effect.withPerformanceMeasure('livestore:makeAdapter'), Effect.withSpan('createStore:makeAdapter'))
|
|
160
166
|
|
|
167
|
+
if (LS_DEV && clientSession.leaderThread.initialState.migrationsReport.migrations.length > 0) {
|
|
168
|
+
yield* Effect.logDebug(
|
|
169
|
+
'[@livestore/livestore:createStore] migrationsReport',
|
|
170
|
+
...clientSession.leaderThread.initialState.migrationsReport.migrations.map(
|
|
171
|
+
(m) =>
|
|
172
|
+
`Schema hash mismatch for table '${m.tableName}' (DB: ${m.hashes.actual}, expected: ${m.hashes.expected}), migrating table...`,
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
161
177
|
// TODO fill up with unsynced mutation events from the client session
|
|
162
178
|
const unsyncedMutationEvents = MutableHashMap.empty<EventId.EventId, MutationEvent.ForSchema<TSchema>>()
|
|
163
179
|
|
|
@@ -176,11 +192,14 @@ export const createStore = <
|
|
|
176
192
|
storeId,
|
|
177
193
|
})
|
|
178
194
|
|
|
195
|
+
// Starts background fibers (syncing, mutation processing, etc) for store
|
|
179
196
|
yield* store.boot
|
|
180
197
|
|
|
181
198
|
if (boot !== undefined) {
|
|
182
199
|
// TODO also incorporate `boot` function progress into `bootStatusQueue`
|
|
183
|
-
yield* Effect.tryAll(() =>
|
|
200
|
+
yield* Effect.tryAll(() =>
|
|
201
|
+
boot(store, { migrationsReport: clientSession.leaderThread.initialState.migrationsReport, parentSpan: span }),
|
|
202
|
+
).pipe(
|
|
184
203
|
UnexpectedError.mapToUnexpectedError,
|
|
185
204
|
Effect.provide(Layer.succeed(LiveStoreContextRunning, { stage: 'running', store: store as any as Store })),
|
|
186
205
|
Effect.withSpan('createStore:boot'),
|
|
@@ -205,3 +224,15 @@ export const createStore = <
|
|
|
205
224
|
Scope.extend(lifetimeScope),
|
|
206
225
|
)
|
|
207
226
|
})
|
|
227
|
+
|
|
228
|
+
const validateStoreId = (storeId: string) =>
|
|
229
|
+
Effect.gen(function* () {
|
|
230
|
+
const validChars = /^[a-zA-Z0-9_-]+$/
|
|
231
|
+
|
|
232
|
+
if (!validChars.test(storeId)) {
|
|
233
|
+
return yield* UnexpectedError.make({
|
|
234
|
+
cause: `Invalid storeId: ${storeId}. Only alphanumeric characters, underscores, and hyphens are allowed.`,
|
|
235
|
+
payload: { storeId },
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
})
|
package/src/store/devtools.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientSession, DebugInfo } from '@livestore/common'
|
|
1
|
+
import type { ClientSession, ClientSessionSyncProcessor, 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'
|
|
@@ -15,6 +15,7 @@ type IStore = {
|
|
|
15
15
|
reactivityGraph: ReactivityGraph
|
|
16
16
|
sqliteDbWrapper: SqliteDbWrapper
|
|
17
17
|
activeQueries: ReferenceCountedSet<LiveQuery<any>>
|
|
18
|
+
syncProcessor: ClientSessionSyncProcessor
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
type Unsub = () => void
|
|
@@ -34,13 +35,17 @@ export const connectDevtoolsToStore = ({
|
|
|
34
35
|
storeDevtoolsChannel,
|
|
35
36
|
store,
|
|
36
37
|
}: {
|
|
37
|
-
storeDevtoolsChannel: WebChannel.WebChannel<
|
|
38
|
+
storeDevtoolsChannel: WebChannel.WebChannel<
|
|
39
|
+
Devtools.ClientSession.MessageToApp,
|
|
40
|
+
Devtools.ClientSession.MessageFromApp
|
|
41
|
+
>
|
|
38
42
|
store: IStore
|
|
39
43
|
}) =>
|
|
40
44
|
Effect.gen(function* () {
|
|
41
45
|
const reactivityGraphSubcriptions: SubMap = new Map()
|
|
42
46
|
const liveQueriesSubscriptions: SubMap = new Map()
|
|
43
47
|
const debugInfoHistorySubscriptions: SubMap = new Map()
|
|
48
|
+
const syncHeadClientSessionSubscriptions: SubMap = new Map()
|
|
44
49
|
|
|
45
50
|
const { clientId, sessionId } = store.clientSession
|
|
46
51
|
|
|
@@ -49,13 +54,14 @@ export const connectDevtoolsToStore = ({
|
|
|
49
54
|
reactivityGraphSubcriptions.forEach((unsub) => unsub())
|
|
50
55
|
liveQueriesSubscriptions.forEach((unsub) => unsub())
|
|
51
56
|
debugInfoHistorySubscriptions.forEach((unsub) => unsub())
|
|
57
|
+
syncHeadClientSessionSubscriptions.forEach((unsub) => unsub())
|
|
52
58
|
}),
|
|
53
59
|
)
|
|
54
60
|
|
|
55
|
-
const sendToDevtools = (message: Devtools.
|
|
61
|
+
const sendToDevtools = (message: Devtools.ClientSession.MessageFromApp) =>
|
|
56
62
|
storeDevtoolsChannel.send(message).pipe(Effect.tapCauseLogPretty, Effect.runFork)
|
|
57
63
|
|
|
58
|
-
const onMessage = (decodedMessage: typeof Devtools.
|
|
64
|
+
const onMessage = (decodedMessage: typeof Devtools.ClientSession.MessageToApp.Type) => {
|
|
59
65
|
// console.debug('@livestore/livestore:store:devtools:onMessage', decodedMessage)
|
|
60
66
|
|
|
61
67
|
if (decodedMessage.clientId !== clientId || decodedMessage.sessionId !== sessionId) {
|
|
@@ -63,7 +69,7 @@ export const connectDevtoolsToStore = ({
|
|
|
63
69
|
return
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
if (decodedMessage._tag === 'LSD.Disconnect') {
|
|
72
|
+
if (decodedMessage._tag === 'LSD.ClientSession.Disconnect') {
|
|
67
73
|
// console.error('TODO handle disconnect properly in store')
|
|
68
74
|
return
|
|
69
75
|
}
|
|
@@ -82,7 +88,7 @@ export const connectDevtoolsToStore = ({
|
|
|
82
88
|
requestIdleCallback(
|
|
83
89
|
() =>
|
|
84
90
|
sendToDevtools(
|
|
85
|
-
Devtools.ReactivityGraphRes.make({
|
|
91
|
+
Devtools.ClientSession.ReactivityGraphRes.make({
|
|
86
92
|
reactivityGraph: store.reactivityGraph.getSnapshot({ includeResults }),
|
|
87
93
|
requestId,
|
|
88
94
|
clientId,
|
|
@@ -106,7 +112,7 @@ export const connectDevtoolsToStore = ({
|
|
|
106
112
|
}
|
|
107
113
|
case 'LSD.ClientSession.DebugInfoReq': {
|
|
108
114
|
sendToDevtools(
|
|
109
|
-
Devtools.DebugInfoRes.make({
|
|
115
|
+
Devtools.ClientSession.DebugInfoRes.make({
|
|
110
116
|
debugInfo: store.sqliteDbWrapper.debugInfo,
|
|
111
117
|
requestId,
|
|
112
118
|
clientId,
|
|
@@ -132,7 +138,7 @@ export const connectDevtoolsToStore = ({
|
|
|
132
138
|
|
|
133
139
|
if (buffer.length > 10) {
|
|
134
140
|
sendToDevtools(
|
|
135
|
-
Devtools.DebugInfoHistoryRes.make({
|
|
141
|
+
Devtools.ClientSession.DebugInfoHistoryRes.make({
|
|
136
142
|
debugInfoHistory: buffer,
|
|
137
143
|
requestId,
|
|
138
144
|
clientId,
|
|
@@ -171,13 +177,17 @@ export const connectDevtoolsToStore = ({
|
|
|
171
177
|
}
|
|
172
178
|
case 'LSD.ClientSession.DebugInfoResetReq': {
|
|
173
179
|
store.sqliteDbWrapper.debugInfo.slowQueries.clear()
|
|
174
|
-
sendToDevtools(
|
|
180
|
+
sendToDevtools(
|
|
181
|
+
Devtools.ClientSession.DebugInfoResetRes.make({ requestId, clientId, sessionId, liveStoreVersion }),
|
|
182
|
+
)
|
|
175
183
|
break
|
|
176
184
|
}
|
|
177
185
|
case 'LSD.ClientSession.DebugInfoRerunQueryReq': {
|
|
178
186
|
const { queryStr, bindValues, queriedTables } = decodedMessage
|
|
179
187
|
store.sqliteDbWrapper.select(queryStr, bindValues, { queriedTables, skipCache: true })
|
|
180
|
-
sendToDevtools(
|
|
188
|
+
sendToDevtools(
|
|
189
|
+
Devtools.ClientSession.DebugInfoRerunQueryRes.make({ requestId, clientId, sessionId, liveStoreVersion }),
|
|
190
|
+
)
|
|
181
191
|
break
|
|
182
192
|
}
|
|
183
193
|
case 'LSD.ClientSession.ReactivityGraphUnsubscribe': {
|
|
@@ -191,7 +201,7 @@ export const connectDevtoolsToStore = ({
|
|
|
191
201
|
requestIdleCallback(
|
|
192
202
|
() =>
|
|
193
203
|
sendToDevtools(
|
|
194
|
-
Devtools.LiveQueriesRes.make({
|
|
204
|
+
Devtools.ClientSession.LiveQueriesRes.make({
|
|
195
205
|
liveQueries: [...store.activeQueries].map((q) => ({
|
|
196
206
|
_tag: q._tag,
|
|
197
207
|
id: q.id,
|
|
@@ -229,7 +239,42 @@ export const connectDevtoolsToStore = ({
|
|
|
229
239
|
liveQueriesSubscriptions.delete(requestId)
|
|
230
240
|
break
|
|
231
241
|
}
|
|
232
|
-
|
|
242
|
+
case 'LSD.ClientSession.SyncHeadSubscribe': {
|
|
243
|
+
const send = (syncState: SyncState.SyncState) =>
|
|
244
|
+
sendToDevtools(
|
|
245
|
+
Devtools.ClientSession.SyncHeadRes.make({
|
|
246
|
+
local: syncState.localHead,
|
|
247
|
+
upstream: syncState.upstreamHead,
|
|
248
|
+
requestId,
|
|
249
|
+
clientId,
|
|
250
|
+
sessionId,
|
|
251
|
+
liveStoreVersion,
|
|
252
|
+
}),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
send(store.syncProcessor.syncState.pipe(Effect.runSync))
|
|
256
|
+
|
|
257
|
+
syncHeadClientSessionSubscriptions.set(
|
|
258
|
+
requestId,
|
|
259
|
+
store.syncProcessor.syncState.changes.pipe(
|
|
260
|
+
Stream.tap((syncState) => send(syncState)),
|
|
261
|
+
Stream.runDrain,
|
|
262
|
+
Effect.interruptible,
|
|
263
|
+
Effect.tapCauseLogPretty,
|
|
264
|
+
Effect.runCallback,
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
break
|
|
269
|
+
}
|
|
270
|
+
case 'LSD.ClientSession.SyncHeadUnsubscribe': {
|
|
271
|
+
syncHeadClientSessionSubscriptions.get(requestId)?.()
|
|
272
|
+
syncHeadClientSessionSubscriptions.delete(requestId)
|
|
273
|
+
break
|
|
274
|
+
}
|
|
275
|
+
default: {
|
|
276
|
+
console.warn(`[LSD.ClientSession] Unknown message`, decodedMessage)
|
|
277
|
+
}
|
|
233
278
|
}
|
|
234
279
|
}
|
|
235
280
|
|
package/src/store/store.ts
CHANGED
|
@@ -96,7 +96,7 @@ export class Store<
|
|
|
96
96
|
// NOTE this is currently exposed for the Devtools databrowser to emit mutation events
|
|
97
97
|
readonly __mutationEventSchema
|
|
98
98
|
private unsyncedMutationEvents
|
|
99
|
-
|
|
99
|
+
readonly syncProcessor: ClientSessionSyncProcessor
|
|
100
100
|
readonly lifetimeScope: Scope.Scope
|
|
101
101
|
|
|
102
102
|
readonly boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
|
|
@@ -195,7 +195,7 @@ export class Store<
|
|
|
195
195
|
this.reactivityGraph = reactivityGraph
|
|
196
196
|
this.reactivityGraph.context = {
|
|
197
197
|
store: this as unknown as Store<BaseGraphQLContext, LiveStoreSchema>,
|
|
198
|
-
|
|
198
|
+
defRcMap: new Map(),
|
|
199
199
|
reactivityGraph: new WeakRef(reactivityGraph),
|
|
200
200
|
otelTracer: otelOptions.tracer,
|
|
201
201
|
rootOtelContext: otelQueriesSpanContext,
|
|
@@ -262,6 +262,10 @@ export class Store<
|
|
|
262
262
|
return this.clientSession.sessionId
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
get clientId(): string {
|
|
266
|
+
return this.clientSession.clientId
|
|
267
|
+
}
|
|
268
|
+
|
|
265
269
|
/**
|
|
266
270
|
* Subscribe to the results of a query
|
|
267
271
|
* Returns a function to cancel the subscription.
|
|
@@ -594,15 +598,16 @@ export class Store<
|
|
|
594
598
|
|
|
595
599
|
hardReset: (mode: 'all-data' | 'only-app-db' = 'all-data') => {
|
|
596
600
|
Effect.gen(this, function* () {
|
|
601
|
+
const clientId = this.clientSession.clientId
|
|
597
602
|
yield* this.clientSession.leaderThread.sendDevtoolsMessage(
|
|
598
|
-
Devtools.
|
|
603
|
+
Devtools.Leader.ResetAllData.Request.make({ liveStoreVersion, mode, requestId: nanoid(), clientId }),
|
|
599
604
|
)
|
|
600
605
|
}).pipe(this.runEffectFork)
|
|
601
606
|
},
|
|
602
607
|
|
|
603
608
|
syncStates: () => {
|
|
604
609
|
Effect.gen(this, function* () {
|
|
605
|
-
const session = this.syncProcessor.
|
|
610
|
+
const session = yield* this.syncProcessor.syncState
|
|
606
611
|
console.log('Session sync state:', session.toJSON())
|
|
607
612
|
const leader = yield* this.clientSession.leaderThread.getSyncState
|
|
608
613
|
console.log('Leader sync state:', leader.toJSON())
|
|
@@ -614,6 +619,8 @@ export class Store<
|
|
|
614
619
|
.shutdown(cause ?? Cause.fail(IntentionalShutdownCause.make({ reason: 'manual' })))
|
|
615
620
|
.pipe(Effect.tapCauseLogPretty, Effect.provide(this.runtime), Effect.runFork)
|
|
616
621
|
},
|
|
622
|
+
|
|
623
|
+
version: liveStoreVersion,
|
|
617
624
|
}
|
|
618
625
|
|
|
619
626
|
// NOTE This is needed because when booting a Store via Effect it seems to call `toJSON` in the error path
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { makeInMemoryAdapter } from '@livestore/adapter-web'
|
|
1
2
|
import { provideOtel } from '@livestore/common'
|
|
2
3
|
import type { FromInputSchema } from '@livestore/common/schema'
|
|
3
4
|
import type { Store } from '@livestore/livestore'
|
|
4
5
|
import { createStore, DbSchema, makeSchema } from '@livestore/livestore'
|
|
5
6
|
import { Effect } from '@livestore/utils/effect'
|
|
6
|
-
import { makeInMemoryAdapter } from '@livestore/web'
|
|
7
7
|
import type * as otel from '@opentelemetry/api'
|
|
8
8
|
|
|
9
9
|
export type Todo = {
|