@livestore/livestore 0.0.0-snapshot-abe9ae4963ab9d3948906a6642c39bc33295e9f6 → 0.0.0-snapshot-484c9684bac8056d764aa460fd025c45f5856aa5
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.d.ts.map +1 -1
- package/dist/SqliteDbWrapper.js +32 -25
- package/dist/SqliteDbWrapper.js.map +1 -1
- package/dist/live-queries/db-query.test.js +12 -4
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/store/create-store.d.ts +7 -1
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +22 -3
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/store-types.d.ts +3 -0
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store.d.ts +1 -1
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +5 -6
- package/dist/store/store.js.map +1 -1
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +6 -1
- package/dist/utils/dev.js.map +1 -1
- package/package.json +4 -4
- package/src/SqliteDbWrapper.ts +33 -27
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +24 -0
- package/src/live-queries/db-query.test.ts +13 -4
- package/src/store/create-store.ts +38 -13
- package/src/store/store-types.ts +3 -0
- package/src/store/store.ts +6 -5
- package/src/utils/dev.ts +6 -1
package/src/SqliteDbWrapper.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
SqliteDbChangeset,
|
|
10
10
|
SqliteDbSession,
|
|
11
11
|
} from '@livestore/common'
|
|
12
|
-
import { BoundArray, BoundMap, sql } from '@livestore/common'
|
|
12
|
+
import { BoundArray, BoundMap, sql, SqliteError } from '@livestore/common'
|
|
13
13
|
import { isDevEnv } from '@livestore/utils'
|
|
14
14
|
import type * as otel from '@opentelemetry/api'
|
|
15
15
|
|
|
@@ -163,39 +163,45 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
163
163
|
{ attributes: { 'sql.query': queryStr } },
|
|
164
164
|
options?.otelContext ?? this.otelRootSpanContext,
|
|
165
165
|
(span) => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
stmt
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
try {
|
|
167
|
+
let stmt = this.cachedStmts.get(queryStr)
|
|
168
|
+
if (stmt === undefined) {
|
|
169
|
+
stmt = this.db.prepare(queryStr)
|
|
170
|
+
this.cachedStmts.set(queryStr, stmt)
|
|
171
|
+
}
|
|
171
172
|
|
|
172
|
-
|
|
173
|
+
stmt.execute(bindValues)
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
if (options?.hasNoEffects !== true && !this.resultCache.ignoreQuery(queryStr)) {
|
|
176
|
+
// TODO use write tables instead
|
|
177
|
+
// check what queries actually end up here.
|
|
178
|
+
this.resultCache.invalidate(options?.writeTables ?? this.getTablesUsed(queryStr))
|
|
179
|
+
}
|
|
179
180
|
|
|
180
|
-
|
|
181
|
+
span.end()
|
|
182
|
+
|
|
183
|
+
const durationMs = getDurationMsFromSpan(span)
|
|
181
184
|
|
|
182
|
-
|
|
185
|
+
this.debugInfo.queryFrameDuration += durationMs
|
|
186
|
+
this.debugInfo.queryFrameCount++
|
|
183
187
|
|
|
184
|
-
|
|
185
|
-
|
|
188
|
+
if (durationMs > 5 && isDevEnv()) {
|
|
189
|
+
this.debugInfo.slowQueries.push({
|
|
190
|
+
queryStr,
|
|
191
|
+
bindValues,
|
|
192
|
+
durationMs,
|
|
193
|
+
rowsCount: undefined,
|
|
194
|
+
queriedTables: new Set(),
|
|
195
|
+
startTimePerfNow: getStartTimeHighResFromSpan(span),
|
|
196
|
+
})
|
|
197
|
+
}
|
|
186
198
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
rowsCount: undefined,
|
|
193
|
-
queriedTables: new Set(),
|
|
194
|
-
startTimePerfNow: getStartTimeHighResFromSpan(span),
|
|
195
|
-
})
|
|
199
|
+
return { durationMs }
|
|
200
|
+
} catch (cause: any) {
|
|
201
|
+
span.recordException(cause)
|
|
202
|
+
span.end()
|
|
203
|
+
throw new SqliteError({ cause, query: { bindValues: bindValues ?? {}, sql: queryStr } })
|
|
196
204
|
}
|
|
197
|
-
|
|
198
|
-
return { durationMs }
|
|
199
205
|
},
|
|
200
206
|
)
|
|
201
207
|
}
|
|
@@ -20,6 +20,14 @@ exports[`otel > otel 3`] = `
|
|
|
20
20
|
",
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
"_name": "client-session-sync-processor:pull",
|
|
25
|
+
"attributes": {
|
|
26
|
+
"code.stacktrace": "<STACKTRACE>",
|
|
27
|
+
"span.label": "⚠︎ Interrupted",
|
|
28
|
+
"status.interrupted": true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
23
31
|
{
|
|
24
32
|
"_name": "LiveStore:sync",
|
|
25
33
|
},
|
|
@@ -280,6 +288,14 @@ exports[`otel > with thunks 7`] = `
|
|
|
280
288
|
",
|
|
281
289
|
},
|
|
282
290
|
},
|
|
291
|
+
{
|
|
292
|
+
"_name": "client-session-sync-processor:pull",
|
|
293
|
+
"attributes": {
|
|
294
|
+
"code.stacktrace": "<STACKTRACE>",
|
|
295
|
+
"span.label": "⚠︎ Interrupted",
|
|
296
|
+
"status.interrupted": true,
|
|
297
|
+
},
|
|
298
|
+
},
|
|
283
299
|
{
|
|
284
300
|
"_name": "LiveStore:sync",
|
|
285
301
|
},
|
|
@@ -374,6 +390,14 @@ exports[`otel > with thunks with query builder and without labels 3`] = `
|
|
|
374
390
|
",
|
|
375
391
|
},
|
|
376
392
|
},
|
|
393
|
+
{
|
|
394
|
+
"_name": "client-session-sync-processor:pull",
|
|
395
|
+
"attributes": {
|
|
396
|
+
"code.stacktrace": "<STACKTRACE>",
|
|
397
|
+
"span.label": "⚠︎ Interrupted",
|
|
398
|
+
"status.interrupted": true,
|
|
399
|
+
},
|
|
400
|
+
},
|
|
377
401
|
{
|
|
378
402
|
"_name": "LiveStore:sync",
|
|
379
403
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Schema } from '@livestore/utils/effect'
|
|
1
|
+
import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
|
2
2
|
import { Vitest } from '@livestore/utils/node-vitest'
|
|
3
3
|
import * as otel from '@opentelemetry/api'
|
|
4
4
|
import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
|
|
@@ -19,6 +19,15 @@ TODO write tests for:
|
|
|
19
19
|
Vitest.describe('otel', () => {
|
|
20
20
|
let cachedProvider: BasicTracerProvider | undefined
|
|
21
21
|
|
|
22
|
+
const mapAttributes = (attributes: otel.Attributes) => {
|
|
23
|
+
return ReadonlyRecord.map(attributes, (val, key) => {
|
|
24
|
+
if (key === 'code.stacktrace') {
|
|
25
|
+
return '<STACKTRACE>'
|
|
26
|
+
}
|
|
27
|
+
return val
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
const makeQuery = Effect.gen(function* () {
|
|
23
32
|
const exporter = new InMemorySpanExporter()
|
|
24
33
|
|
|
@@ -74,7 +83,7 @@ Vitest.describe('otel', () => {
|
|
|
74
83
|
return { exporter }
|
|
75
84
|
}).pipe(
|
|
76
85
|
Effect.scoped,
|
|
77
|
-
Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter)).toMatchSnapshot()),
|
|
86
|
+
Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()),
|
|
78
87
|
),
|
|
79
88
|
)
|
|
80
89
|
|
|
@@ -124,7 +133,7 @@ Vitest.describe('otel', () => {
|
|
|
124
133
|
return { exporter }
|
|
125
134
|
}).pipe(
|
|
126
135
|
Effect.scoped,
|
|
127
|
-
Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter)).toMatchSnapshot()),
|
|
136
|
+
Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()),
|
|
128
137
|
),
|
|
129
138
|
)
|
|
130
139
|
|
|
@@ -160,7 +169,7 @@ Vitest.describe('otel', () => {
|
|
|
160
169
|
return { exporter }
|
|
161
170
|
}).pipe(
|
|
162
171
|
Effect.scoped,
|
|
163
|
-
Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter)).toMatchSnapshot()),
|
|
172
|
+
Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()),
|
|
164
173
|
),
|
|
165
174
|
)
|
|
166
175
|
})
|
|
@@ -33,6 +33,10 @@ import { connectDevtoolsToStore } from './devtools.js'
|
|
|
33
33
|
import { Store } from './store.js'
|
|
34
34
|
import type { BaseGraphQLContext, GraphQLOptions, OtelOptions, ShutdownDeferred } from './store-types.js'
|
|
35
35
|
|
|
36
|
+
export const DEFAULT_PARAMS = {
|
|
37
|
+
leaderPushBatchSize: 1,
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
export interface CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext, TSchema extends LiveStoreSchema> {
|
|
37
41
|
schema: TSchema
|
|
38
42
|
adapter: Adapter
|
|
@@ -49,6 +53,9 @@ export interface CreateStoreOptions<TGraphQLContext extends BaseGraphQLContext,
|
|
|
49
53
|
disableDevtools?: boolean
|
|
50
54
|
onBootStatus?: (status: BootStatus) => void
|
|
51
55
|
shutdownDeferred?: ShutdownDeferred
|
|
56
|
+
params?: {
|
|
57
|
+
leaderPushBatchSize?: number
|
|
58
|
+
}
|
|
52
59
|
debug?: {
|
|
53
60
|
instanceId?: string
|
|
54
61
|
}
|
|
@@ -102,6 +109,7 @@ export const createStore = <
|
|
|
102
109
|
disableDevtools,
|
|
103
110
|
onBootStatus,
|
|
104
111
|
shutdownDeferred,
|
|
112
|
+
params,
|
|
105
113
|
debug,
|
|
106
114
|
}: CreateStoreOptions<TGraphQLContext, TSchema>): Effect.Effect<
|
|
107
115
|
Store<TGraphQLContext, TSchema>,
|
|
@@ -142,17 +150,30 @@ export const createStore = <
|
|
|
142
150
|
|
|
143
151
|
const runtime = yield* Effect.runtime<Scope.Scope>()
|
|
144
152
|
|
|
145
|
-
const shutdown = (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
Effect.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
const shutdown = (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) => {
|
|
154
|
+
Effect.gen(function* () {
|
|
155
|
+
yield* Scope.close(lifetimeScope, Exit.failCause(cause)).pipe(
|
|
156
|
+
Effect.logWarnIfTakesLongerThan({ label: '@livestore/livestore:shutdown', duration: 500 }),
|
|
157
|
+
Effect.timeout(1000),
|
|
158
|
+
Effect.catchTag('TimeoutException', () =>
|
|
159
|
+
Effect.logError('@livestore/livestore:shutdown: Timed out after 1 second'),
|
|
160
|
+
),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if (shutdownDeferred) {
|
|
164
|
+
yield* Deferred.failCause(shutdownDeferred, cause)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
yield* Effect.logDebug('LiveStore shutdown complete')
|
|
168
|
+
}).pipe(
|
|
169
|
+
Effect.withSpan('@livestore/livestore:shutdown'),
|
|
170
|
+
Effect.provide(runtime),
|
|
171
|
+
Effect.tapCauseLogPretty,
|
|
172
|
+
// Given that the shutdown flow might also interrupt the effect that is calling the shutdown,
|
|
173
|
+
// we want to detach the shutdown effect so it's not interrupted by itself
|
|
174
|
+
Effect.runFork,
|
|
155
175
|
)
|
|
176
|
+
}
|
|
156
177
|
|
|
157
178
|
const clientSession: ClientSession = yield* adapter({
|
|
158
179
|
schema,
|
|
@@ -167,9 +188,10 @@ export const createStore = <
|
|
|
167
188
|
if (LS_DEV && clientSession.leaderThread.initialState.migrationsReport.migrations.length > 0) {
|
|
168
189
|
yield* Effect.logDebug(
|
|
169
190
|
'[@livestore/livestore:createStore] migrationsReport',
|
|
170
|
-
...clientSession.leaderThread.initialState.migrationsReport.migrations.map(
|
|
171
|
-
|
|
172
|
-
`
|
|
191
|
+
...clientSession.leaderThread.initialState.migrationsReport.migrations.map((m) =>
|
|
192
|
+
m.hashes.actual === undefined
|
|
193
|
+
? `Table '${m.tableName}' doesn't exist yet. Creating table...`
|
|
194
|
+
: `Schema hash mismatch for table '${m.tableName}' (DB: ${m.hashes.actual}, expected: ${m.hashes.expected}), migrating table...`,
|
|
173
195
|
),
|
|
174
196
|
)
|
|
175
197
|
}
|
|
@@ -190,6 +212,9 @@ export const createStore = <
|
|
|
190
212
|
// but only set the provided `batchUpdates` function after boot
|
|
191
213
|
batchUpdates: (run) => run(),
|
|
192
214
|
storeId,
|
|
215
|
+
params: {
|
|
216
|
+
leaderPushBatchSize: params?.leaderPushBatchSize ?? DEFAULT_PARAMS.leaderPushBatchSize,
|
|
217
|
+
},
|
|
193
218
|
})
|
|
194
219
|
|
|
195
220
|
// Starts background fibers (syncing, mutation processing, etc) for store
|
package/src/store/store-types.ts
CHANGED
|
@@ -66,6 +66,9 @@ export type StoreOptions<
|
|
|
66
66
|
batchUpdates: (runUpdates: () => void) => void
|
|
67
67
|
// TODO validate whether we still need this
|
|
68
68
|
unsyncedMutationEvents: MutableHashMap.MutableHashMap<EventId.EventId, MutationEvent.ForSchema<TSchema>>
|
|
69
|
+
params: {
|
|
70
|
+
leaderPushBatchSize: number
|
|
71
|
+
}
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
export type RefreshReason =
|
package/src/store/store.ts
CHANGED
|
@@ -113,6 +113,7 @@ export class Store<
|
|
|
113
113
|
storeId,
|
|
114
114
|
lifetimeScope,
|
|
115
115
|
runtime,
|
|
116
|
+
params,
|
|
116
117
|
}: StoreOptions<TGraphQLContext, TSchema>) {
|
|
117
118
|
super()
|
|
118
119
|
|
|
@@ -178,6 +179,9 @@ export class Store<
|
|
|
178
179
|
reactivityGraph.setRefs(tablesToUpdate)
|
|
179
180
|
},
|
|
180
181
|
span: syncSpan,
|
|
182
|
+
params: {
|
|
183
|
+
leaderPushBatchSize: params.leaderPushBatchSize,
|
|
184
|
+
},
|
|
181
185
|
})
|
|
182
186
|
|
|
183
187
|
this.__mutationEventSchema = MutationEvent.makeMutationEventSchemaMemo(schema)
|
|
@@ -614,11 +618,8 @@ export class Store<
|
|
|
614
618
|
}).pipe(this.runEffectFork)
|
|
615
619
|
},
|
|
616
620
|
|
|
617
|
-
shutdown: (cause?: Cause.Cause<UnexpectedError>) =>
|
|
618
|
-
this.clientSession
|
|
619
|
-
.shutdown(cause ?? Cause.fail(IntentionalShutdownCause.make({ reason: 'manual' })))
|
|
620
|
-
.pipe(Effect.tapCauseLogPretty, Effect.provide(this.runtime), Effect.runFork)
|
|
621
|
-
},
|
|
621
|
+
shutdown: (cause?: Cause.Cause<UnexpectedError>) =>
|
|
622
|
+
this.clientSession.shutdown(cause ?? Cause.fail(IntentionalShutdownCause.make({ reason: 'manual' }))),
|
|
622
623
|
|
|
623
624
|
version: liveStoreVersion,
|
|
624
625
|
}
|
package/src/utils/dev.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDevEnv } from '@livestore/utils'
|
|
2
|
+
import { Effect } from '@livestore/utils/effect'
|
|
2
3
|
|
|
3
4
|
/* eslint-disable unicorn/prefer-global-this */
|
|
4
5
|
export const downloadBlob = (
|
|
@@ -27,6 +28,10 @@ export const downloadURL = (data: string, fileName: string) => {
|
|
|
27
28
|
|
|
28
29
|
export const exposeDebugUtils = () => {
|
|
29
30
|
if (isDevEnv()) {
|
|
30
|
-
globalThis.__debugLiveStoreUtils = {
|
|
31
|
+
globalThis.__debugLiveStoreUtils = {
|
|
32
|
+
downloadBlob,
|
|
33
|
+
runSync: (effect: Effect.Effect<any, any, never>) => Effect.runSync(effect),
|
|
34
|
+
runFork: (effect: Effect.Effect<any, any, never>) => Effect.runFork(effect),
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
}
|