@livestore/livestore 0.4.0-dev.9 → 0.5.0-dev.0
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/README.md +0 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.js +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/SqliteDbWrapper.d.ts +5 -5
- package/dist/SqliteDbWrapper.d.ts.map +1 -1
- package/dist/SqliteDbWrapper.js +15 -13
- package/dist/SqliteDbWrapper.js.map +1 -1
- package/dist/SqliteDbWrapper.test.js +24 -6
- package/dist/SqliteDbWrapper.test.js.map +1 -1
- package/dist/effect/LiveStore.d.ts +134 -6
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +190 -11
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/effect/LiveStore.test.d.ts +2 -0
- package/dist/effect/LiveStore.test.d.ts.map +1 -0
- package/dist/effect/LiveStore.test.js +41 -0
- package/dist/effect/LiveStore.test.js.map +1 -0
- package/dist/effect/mod.d.ts +1 -1
- package/dist/effect/mod.d.ts.map +1 -1
- package/dist/effect/mod.js +3 -1
- package/dist/effect/mod.js.map +1 -1
- package/dist/live-queries/base-class.d.ts +129 -9
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +30 -4
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/client-document-get-query.d.ts +1 -1
- package/dist/live-queries/client-document-get-query.d.ts.map +1 -1
- package/dist/live-queries/client-document-get-query.js +4 -3
- package/dist/live-queries/client-document-get-query.js.map +1 -1
- package/dist/live-queries/computed.d.ts +56 -0
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +62 -6
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/db-query.d.ts +2 -2
- package/dist/live-queries/db-query.d.ts.map +1 -1
- package/dist/live-queries/db-query.js +41 -30
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/db-query.test.js +112 -29
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/live-queries/signal.d.ts +49 -0
- package/dist/live-queries/signal.d.ts.map +1 -1
- package/dist/live-queries/signal.js +51 -2
- package/dist/live-queries/signal.js.map +1 -1
- package/dist/live-queries/signal.test.js +4 -4
- package/dist/live-queries/signal.test.js.map +1 -1
- package/dist/mod.d.ts +3 -3
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +3 -2
- package/dist/mod.js.map +1 -1
- package/dist/reactive.d.ts +10 -10
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +29 -27
- package/dist/reactive.js.map +1 -1
- package/dist/reactive.test.js +2 -2
- package/dist/reactive.test.js.map +1 -1
- package/dist/store/StoreRegistry.d.ts +215 -0
- package/dist/store/StoreRegistry.d.ts.map +1 -0
- package/dist/store/StoreRegistry.js +267 -0
- package/dist/store/StoreRegistry.js.map +1 -0
- package/dist/store/StoreRegistry.test.d.ts +2 -0
- package/dist/store/StoreRegistry.test.d.ts.map +1 -0
- package/dist/store/StoreRegistry.test.js +384 -0
- package/dist/store/StoreRegistry.test.js.map +1 -0
- package/dist/store/create-store.d.ts +99 -22
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +85 -37
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.d.ts +6 -18
- package/dist/store/devtools.d.ts.map +1 -1
- package/dist/store/devtools.js +61 -19
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store-eventstream.test.d.ts +2 -0
- package/dist/store/store-eventstream.test.d.ts.map +1 -0
- package/dist/store/store-eventstream.test.js +65 -0
- package/dist/store/store-eventstream.test.js.map +1 -0
- package/dist/store/store-types.d.ts +271 -31
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js +41 -1
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store-types.test.d.ts +2 -0
- package/dist/store/store-types.test.d.ts.map +1 -0
- package/dist/store/store-types.test.js +39 -0
- package/dist/store/store-types.test.js.map +1 -0
- package/dist/store/store.d.ts +255 -68
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +533 -186
- package/dist/store/store.js.map +1 -1
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +1 -1
- package/dist/utils/dev.js.map +1 -1
- package/dist/utils/stack-info.d.ts.map +1 -1
- package/dist/utils/stack-info.js +3 -2
- package/dist/utils/stack-info.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +46 -39
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/fixture.js +7 -0
- package/dist/utils/tests/fixture.js.map +1 -1
- package/dist/utils/tests/otel.d.ts.map +1 -1
- package/dist/utils/tests/otel.js +5 -5
- package/dist/utils/tests/otel.js.map +1 -1
- package/package.json +50 -18
- package/src/QueryCache.ts +1 -1
- package/src/SqliteDbWrapper.test.ts +30 -6
- package/src/SqliteDbWrapper.ts +21 -17
- package/src/ambient.d.ts +0 -7
- package/src/effect/LiveStore.test.ts +61 -0
- package/src/effect/LiveStore.ts +426 -22
- package/src/effect/mod.ts +13 -1
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +814 -178
- package/src/live-queries/base-class.ts +162 -32
- package/src/live-queries/client-document-get-query.ts +6 -4
- package/src/live-queries/computed.ts +65 -8
- package/src/live-queries/db-query.test.ts +168 -30
- package/src/live-queries/db-query.ts +58 -43
- package/src/live-queries/signal.test.ts +5 -4
- package/src/live-queries/signal.ts +52 -3
- package/src/mod.ts +19 -2
- package/src/reactive.test.ts +3 -2
- package/src/reactive.ts +58 -49
- package/src/store/StoreRegistry.test.ts +543 -0
- package/src/store/StoreRegistry.ts +429 -0
- package/src/store/create-store.ts +227 -74
- package/src/store/devtools.ts +305 -261
- package/src/store/store-eventstream.test.ts +123 -0
- package/src/store/store-types.test.ts +52 -0
- package/src/store/store-types.ts +318 -44
- package/src/store/store.ts +695 -283
- package/src/utils/dev.ts +3 -4
- package/src/utils/stack-info.ts +5 -2
- package/src/utils/tests/fixture.ts +9 -1
- package/src/utils/tests/otel.ts +8 -7
|
@@ -1,32 +1,56 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
1
|
+
import * as otel from '@opentelemetry/api'
|
|
3
2
|
import { expect } from 'vitest'
|
|
4
3
|
|
|
4
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
5
|
+
import { Effect } from '@livestore/utils/effect'
|
|
6
|
+
|
|
7
|
+
import { StoreInternalsSymbol } from './store/store-types.ts'
|
|
5
8
|
import { makeTodoMvc } from './utils/tests/fixture.ts'
|
|
6
9
|
|
|
7
10
|
Vitest.describe('SqliteDbWrapper', () => {
|
|
11
|
+
Vitest.live('works with the OpenTelemetry API no-op tracer', (_test) =>
|
|
12
|
+
Effect.gen(function* () {
|
|
13
|
+
const otelTracer = otel.trace.getTracer('sqlite-wrapper-noop-test')
|
|
14
|
+
const probeSpan = otelTracer.startSpan('verify-noop-tracer')
|
|
15
|
+
expect(probeSpan.isRecording()).toBe(false)
|
|
16
|
+
probeSpan.end()
|
|
17
|
+
|
|
18
|
+
// Store creation exercises the configureSQLite execute path.
|
|
19
|
+
const store = yield* makeTodoMvc({ otelTracer })
|
|
20
|
+
const sqliteDbWrapper = store[StoreInternalsSymbol].sqliteDbWrapper
|
|
21
|
+
const { durationMs } = sqliteDbWrapper.cachedExecute('CREATE TABLE noop_timing_test (id INTEGER)', undefined, {
|
|
22
|
+
hasNoEffects: true,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
expect(sqliteDbWrapper.select('SELECT 1 AS value')).toEqual([{ value: 1 }])
|
|
26
|
+
expect(sqliteDbWrapper.debugInfo.queryFrameCount).toBeGreaterThan(0)
|
|
27
|
+
expect(sqliteDbWrapper.debugInfo.queryFrameDuration).toBeGreaterThanOrEqual(0)
|
|
28
|
+
expect(durationMs).toBeGreaterThanOrEqual(0)
|
|
29
|
+
}),
|
|
30
|
+
)
|
|
31
|
+
|
|
8
32
|
Vitest.describe('getTablesUsed', () => {
|
|
9
33
|
const getTablesUsed = (query: string) =>
|
|
10
34
|
Effect.gen(function* () {
|
|
11
35
|
const store = yield* makeTodoMvc({})
|
|
12
|
-
return store.sqliteDbWrapper.getTablesUsed(query)
|
|
36
|
+
return store[StoreInternalsSymbol].sqliteDbWrapper.getTablesUsed(query)
|
|
13
37
|
})
|
|
14
38
|
|
|
15
|
-
Vitest.
|
|
39
|
+
Vitest.live('should return the correct tables used', (_test) =>
|
|
16
40
|
Effect.gen(function* () {
|
|
17
41
|
const tablesUsed = yield* getTablesUsed('select * from todos')
|
|
18
42
|
expect(tablesUsed).toEqual(new Set(['todos']))
|
|
19
43
|
}),
|
|
20
44
|
)
|
|
21
45
|
|
|
22
|
-
Vitest.
|
|
46
|
+
Vitest.live('should handle DELETE FROM statement without WHERE clause', (_test) =>
|
|
23
47
|
Effect.gen(function* () {
|
|
24
48
|
const tablesUsed = yield* getTablesUsed('DELETE FROM todos')
|
|
25
49
|
expect(tablesUsed).toEqual(new Set(['todos']))
|
|
26
50
|
}),
|
|
27
51
|
)
|
|
28
52
|
|
|
29
|
-
Vitest.
|
|
53
|
+
Vitest.live('should handle INSERT with ON CONFLICT clause', (_test) =>
|
|
30
54
|
Effect.gen(function* () {
|
|
31
55
|
const tablesUsed = yield* getTablesUsed(
|
|
32
56
|
'INSERT INTO todos (id, text, completed) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET text = ?',
|
package/src/SqliteDbWrapper.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import type * as otel from '@opentelemetry/api'
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
BoundArray,
|
|
3
5
|
BoundMap,
|
|
4
6
|
type DebugInfo,
|
|
5
|
-
getDurationMsFromSpan,
|
|
6
|
-
getStartTimeHighResFromSpan,
|
|
7
7
|
type MutableDebugInfo,
|
|
8
8
|
type PreparedBindValues,
|
|
9
9
|
type PreparedStatement,
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
sql,
|
|
16
16
|
} from '@livestore/common'
|
|
17
17
|
import { isDevEnv, LS_DEV } from '@livestore/utils'
|
|
18
|
-
import type * as otel from '@opentelemetry/api'
|
|
19
18
|
|
|
20
19
|
import QueryCache from './QueryCache.ts'
|
|
21
20
|
|
|
@@ -103,7 +102,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
103
102
|
throw e
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
if (
|
|
105
|
+
if (errored === false) {
|
|
107
106
|
this.execute(sql`commit;`)
|
|
108
107
|
}
|
|
109
108
|
|
|
@@ -122,7 +121,8 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
122
121
|
|
|
123
122
|
return {
|
|
124
123
|
result,
|
|
125
|
-
changeset:
|
|
124
|
+
changeset:
|
|
125
|
+
changeset !== undefined ? { _tag: 'sessionChangeset', data: changeset, debug: null } : { _tag: 'no-op' },
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -140,7 +140,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
const cached = this.tablesUsedCache.get(query)
|
|
143
|
-
if (cached) {
|
|
143
|
+
if (cached !== undefined) {
|
|
144
144
|
return cached
|
|
145
145
|
}
|
|
146
146
|
const stmt = this.tablesUsedStmt
|
|
@@ -161,7 +161,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
161
161
|
|
|
162
162
|
cachedExecute(
|
|
163
163
|
queryStr: string,
|
|
164
|
-
bindValues?: PreparedBindValues
|
|
164
|
+
bindValues?: PreparedBindValues,
|
|
165
165
|
options?: {
|
|
166
166
|
hasNoEffects?: boolean
|
|
167
167
|
otelContext?: otel.Context
|
|
@@ -177,6 +177,8 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
177
177
|
{ attributes: { 'sql.query': queryStr } },
|
|
178
178
|
options?.otelContext ?? this.otelRootSpanContext,
|
|
179
179
|
(span) => {
|
|
180
|
+
const startTimePerfNow = performance.now()
|
|
181
|
+
|
|
180
182
|
try {
|
|
181
183
|
let stmt = this.cachedStmts.get(queryStr)
|
|
182
184
|
if (stmt === undefined) {
|
|
@@ -186,7 +188,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
186
188
|
|
|
187
189
|
stmt.execute(bindValues)
|
|
188
190
|
|
|
189
|
-
if (options?.hasNoEffects !== true &&
|
|
191
|
+
if (options?.hasNoEffects !== true && this.resultCache.ignoreQuery(queryStr) === false) {
|
|
190
192
|
// TODO use write tables instead
|
|
191
193
|
// check what queries actually end up here.
|
|
192
194
|
this.resultCache.invalidate(options?.writeTables ?? this.getTablesUsed(queryStr))
|
|
@@ -194,19 +196,19 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
194
196
|
|
|
195
197
|
span.end()
|
|
196
198
|
|
|
197
|
-
const durationMs =
|
|
199
|
+
const durationMs = performance.now() - startTimePerfNow
|
|
198
200
|
|
|
199
201
|
this.debugInfo.queryFrameDuration += durationMs
|
|
200
202
|
this.debugInfo.queryFrameCount++
|
|
201
203
|
|
|
202
|
-
if (durationMs > 5 && isDevEnv()) {
|
|
204
|
+
if (durationMs > 5 && isDevEnv() === true) {
|
|
203
205
|
this.debugInfo.slowQueries.push({
|
|
204
206
|
queryStr,
|
|
205
207
|
bindValues,
|
|
206
208
|
durationMs,
|
|
207
209
|
rowsCount: undefined,
|
|
208
210
|
queriedTables: new Set(),
|
|
209
|
-
startTimePerfNow
|
|
211
|
+
startTimePerfNow,
|
|
210
212
|
})
|
|
211
213
|
}
|
|
212
214
|
|
|
@@ -214,8 +216,8 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
214
216
|
} catch (cause: any) {
|
|
215
217
|
span.recordException(cause)
|
|
216
218
|
span.end()
|
|
217
|
-
if (LS_DEV) {
|
|
218
|
-
//
|
|
219
|
+
if (LS_DEV === true) {
|
|
220
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for SQL errors during development
|
|
219
221
|
debugger
|
|
220
222
|
}
|
|
221
223
|
throw new SqliteError({ cause, query: { bindValues: bindValues ?? {}, sql: queryStr } })
|
|
@@ -230,7 +232,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
230
232
|
|
|
231
233
|
cachedSelect<T = any>(
|
|
232
234
|
queryStr: string,
|
|
233
|
-
bindValues?: PreparedBindValues
|
|
235
|
+
bindValues?: PreparedBindValues,
|
|
234
236
|
options?: {
|
|
235
237
|
queriedTables?: ReadonlySet<string>
|
|
236
238
|
skipCache?: boolean
|
|
@@ -246,6 +248,8 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
246
248
|
{},
|
|
247
249
|
otelContext ?? this.otelRootSpanContext,
|
|
248
250
|
(span) => {
|
|
251
|
+
const startTimePerfNow = performance.now()
|
|
252
|
+
|
|
249
253
|
try {
|
|
250
254
|
span.setAttribute('sql.query', queryStr)
|
|
251
255
|
|
|
@@ -274,20 +278,20 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
|
274
278
|
|
|
275
279
|
span.end()
|
|
276
280
|
|
|
277
|
-
const durationMs =
|
|
281
|
+
const durationMs = performance.now() - startTimePerfNow
|
|
278
282
|
|
|
279
283
|
this.debugInfo.queryFrameDuration += durationMs
|
|
280
284
|
this.debugInfo.queryFrameCount++
|
|
281
285
|
|
|
282
286
|
// TODO also enable in non-dev mode
|
|
283
|
-
if (durationMs > 5 && isDevEnv()) {
|
|
287
|
+
if (durationMs > 5 && isDevEnv() === true) {
|
|
284
288
|
this.debugInfo.slowQueries.push({
|
|
285
289
|
queryStr,
|
|
286
290
|
bindValues,
|
|
287
291
|
durationMs,
|
|
288
292
|
rowsCount: result.length,
|
|
289
293
|
queriedTables: queriedTables_,
|
|
290
|
-
startTimePerfNow
|
|
294
|
+
startTimePerfNow,
|
|
291
295
|
})
|
|
292
296
|
}
|
|
293
297
|
|
package/src/ambient.d.ts
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { Effect, Layer } from '@livestore/utils/effect'
|
|
4
|
+
|
|
5
|
+
import { schema } from '../utils/tests/fixture.ts'
|
|
6
|
+
import { Store, type StoreTagClass } from './LiveStore.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Regression test for https://github.com/livestorejs/livestore/issues/1103
|
|
10
|
+
*
|
|
11
|
+
* `yield* MyStore` and `MyStore.query(...)` must produce the same R channel type
|
|
12
|
+
* so that a single layer provision satisfies both.
|
|
13
|
+
*/
|
|
14
|
+
describe('Store.Tag R channel consistency', () => {
|
|
15
|
+
class MainStore extends Store.Tag(schema, 'main') {}
|
|
16
|
+
|
|
17
|
+
const storeLayer = MainStore.layer({ adapter: {} as any, batchUpdates: (_: () => void) => {} })
|
|
18
|
+
|
|
19
|
+
it('yield* and method R channels unify so layer provision eliminates all requirements', () => {
|
|
20
|
+
const prog = Effect.gen(function* () {
|
|
21
|
+
const { store } = yield* MainStore
|
|
22
|
+
void store
|
|
23
|
+
|
|
24
|
+
const _queryResult = yield* MainStore.query({ get: () => 42 } as any)
|
|
25
|
+
void _queryResult
|
|
26
|
+
|
|
27
|
+
yield* MainStore.commit()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const _provided = prog.pipe(Effect.provide(storeLayer))
|
|
31
|
+
type _R = Effect.Services<typeof _provided>
|
|
32
|
+
/** Providing the store layer must fully eliminate MainStore from R */
|
|
33
|
+
type _MainStoreNotInR = StoreTagClass<typeof schema, 'main'> extends _R ? false : true
|
|
34
|
+
const _check: _MainStoreNotInR = true
|
|
35
|
+
void _check
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('use() helper R channel is satisfied by the same layer', () => {
|
|
39
|
+
const prog = MainStore.use(({ store }) => Effect.succeed(store))
|
|
40
|
+
|
|
41
|
+
const _provided = prog.pipe(Effect.provide(storeLayer))
|
|
42
|
+
type _R = Effect.Services<typeof _provided>
|
|
43
|
+
/** Providing the store layer must fully eliminate MainStore from R */
|
|
44
|
+
type _MainStoreNotInR = StoreTagClass<typeof schema, 'main'> extends _R ? false : true
|
|
45
|
+
const _check: _MainStoreNotInR = true
|
|
46
|
+
void _check
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('fromDeferred layer output satisfies the same R channel', () => {
|
|
50
|
+
const prog = MainStore
|
|
51
|
+
|
|
52
|
+
/** fromDeferred + DeferredLayer should satisfy MainStore in R */
|
|
53
|
+
const _provided = prog.pipe(Effect.provide(Layer.merge(MainStore.fromDeferred, MainStore.DeferredLayer)))
|
|
54
|
+
|
|
55
|
+
type _R = Effect.Services<typeof _provided>
|
|
56
|
+
/** MainStore should not be in R after providing fromDeferred */
|
|
57
|
+
type _MainStoreNotInR = StoreTagClass<typeof schema, 'main'> extends _R ? false : true
|
|
58
|
+
const _check: _MainStoreNotInR = true
|
|
59
|
+
void _check
|
|
60
|
+
})
|
|
61
|
+
})
|