@livestore/livestore 0.3.1-dev.0 → 0.3.2-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/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js +8 -3
- package/dist/QueryCache.js.map +1 -1
- package/dist/SqliteDbWrapper.d.ts +17 -4
- package/dist/SqliteDbWrapper.d.ts.map +1 -1
- package/dist/SqliteDbWrapper.js +14 -6
- package/dist/SqliteDbWrapper.js.map +1 -1
- package/dist/SqliteDbWrapper.test.d.ts +2 -0
- package/dist/SqliteDbWrapper.test.d.ts.map +1 -0
- package/dist/SqliteDbWrapper.test.js +25 -0
- package/dist/SqliteDbWrapper.test.js.map +1 -0
- package/dist/effect/LiveStore.js +1 -1
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/live-queries/client-document-get-query.js +1 -1
- package/dist/live-queries/client-document-get-query.js.map +1 -1
- package/dist/live-queries/db-query.js +1 -1
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/db-query.test.js +91 -1
- package/dist/live-queries/db-query.test.js.map +1 -1
- package/dist/reactive.js +1 -1
- package/dist/reactive.js.map +1 -1
- package/dist/store/create-store.js +1 -1
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/devtools.js +9 -5
- package/dist/store/devtools.js.map +1 -1
- package/dist/store/store.d.ts +5 -4
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +42 -17
- package/dist/store/store.js.map +1 -1
- package/dist/utils/stack-info.d.ts.map +1 -1
- package/dist/utils/stack-info.js +5 -1
- package/dist/utils/stack-info.js.map +1 -1
- package/dist/utils/stack-info.test.js +6 -2
- package/dist/utils/stack-info.test.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +44 -54
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/dist/utils/tests/otel.js +1 -1
- package/dist/utils/tests/otel.js.map +1 -1
- package/package.json +7 -7
- package/src/QueryCache.ts +9 -4
- package/src/SqliteDbWrapper.test.ts +38 -0
- package/src/SqliteDbWrapper.ts +24 -15
- package/src/effect/LiveStore.ts +1 -1
- package/src/live-queries/__snapshots__/db-query.test.ts.snap +389 -0
- package/src/live-queries/client-document-get-query.ts +1 -1
- package/src/live-queries/db-query.test.ts +144 -1
- package/src/live-queries/db-query.ts +1 -1
- package/src/reactive.ts +1 -1
- package/src/store/create-store.ts +1 -1
- package/src/store/devtools.ts +5 -5
- package/src/store/store.ts +55 -23
- package/src/utils/stack-info.test.ts +6 -2
- package/src/utils/stack-info.ts +5 -1
- package/src/utils/tests/otel.ts +1 -1
@@ -0,0 +1,38 @@
|
|
1
|
+
import { Effect } from '@livestore/utils/effect'
|
2
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
3
|
+
import { expect } from 'vitest'
|
4
|
+
|
5
|
+
import { makeTodoMvc } from './utils/tests/fixture.js'
|
6
|
+
|
7
|
+
Vitest.describe('SqliteDbWrapper', () => {
|
8
|
+
Vitest.describe('getTablesUsed', () => {
|
9
|
+
const getTablesUsed = (query: string) =>
|
10
|
+
Effect.gen(function* () {
|
11
|
+
const store = yield* makeTodoMvc({})
|
12
|
+
return store.sqliteDbWrapper.getTablesUsed(query)
|
13
|
+
})
|
14
|
+
|
15
|
+
Vitest.scopedLive('should return the correct tables used', (_test) =>
|
16
|
+
Effect.gen(function* () {
|
17
|
+
const tablesUsed = yield* getTablesUsed('select * from todos')
|
18
|
+
expect(tablesUsed).toEqual(new Set(['todos']))
|
19
|
+
}),
|
20
|
+
)
|
21
|
+
|
22
|
+
Vitest.scopedLive('should handle DELETE FROM statement without WHERE clause', (_test) =>
|
23
|
+
Effect.gen(function* () {
|
24
|
+
const tablesUsed = yield* getTablesUsed('DELETE FROM todos')
|
25
|
+
expect(tablesUsed).toEqual(new Set(['todos']))
|
26
|
+
}),
|
27
|
+
)
|
28
|
+
|
29
|
+
Vitest.scopedLive('should handle INSERT with ON CONFLICT clause', (_test) =>
|
30
|
+
Effect.gen(function* () {
|
31
|
+
const tablesUsed = yield* getTablesUsed(
|
32
|
+
'INSERT INTO todos (id, text, completed) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET text = ?',
|
33
|
+
)
|
34
|
+
expect(tablesUsed).toEqual(new Set(['todos']))
|
35
|
+
}),
|
36
|
+
)
|
37
|
+
})
|
38
|
+
})
|
package/src/SqliteDbWrapper.ts
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
/* eslint-disable prefer-arrow/prefer-arrow-functions */
|
2
2
|
|
3
|
-
import type {
|
4
|
-
DebugInfo,
|
5
|
-
MutableDebugInfo,
|
6
|
-
PreparedBindValues,
|
7
|
-
PreparedStatement,
|
8
|
-
SqliteDb,
|
9
|
-
SqliteDbChangeset,
|
10
|
-
SqliteDbSession,
|
11
|
-
} from '@livestore/common'
|
12
3
|
import {
|
13
4
|
BoundArray,
|
14
5
|
BoundMap,
|
6
|
+
type DebugInfo,
|
15
7
|
getDurationMsFromSpan,
|
16
8
|
getStartTimeHighResFromSpan,
|
17
|
-
|
9
|
+
type MutableDebugInfo,
|
10
|
+
type PreparedBindValues,
|
11
|
+
type PreparedStatement,
|
12
|
+
type SqliteDb,
|
13
|
+
type SqliteDbChangeset,
|
14
|
+
SqliteDbHelper,
|
15
|
+
type SqliteDbSession,
|
18
16
|
SqliteError,
|
17
|
+
sql,
|
19
18
|
} from '@livestore/common'
|
20
19
|
import { isDevEnv, LS_DEV } from '@livestore/utils'
|
21
20
|
import type * as otel from '@opentelemetry/api'
|
@@ -67,7 +66,12 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
67
66
|
|
68
67
|
configureSQLite(this)
|
69
68
|
}
|
70
|
-
|
69
|
+
get debug() {
|
70
|
+
return this.db.debug
|
71
|
+
}
|
72
|
+
get metadata() {
|
73
|
+
return this.db.metadata
|
74
|
+
}
|
71
75
|
prepare(queryStr: string): PreparedStatement {
|
72
76
|
return this.db.prepare(queryStr)
|
73
77
|
}
|
@@ -75,10 +79,10 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
75
79
|
return this.db.import(data)
|
76
80
|
}
|
77
81
|
close(): void {
|
78
|
-
|
82
|
+
this.db.close()
|
79
83
|
}
|
80
84
|
destroy(): void {
|
81
|
-
|
85
|
+
this.db.destroy()
|
82
86
|
}
|
83
87
|
session(): SqliteDbSession {
|
84
88
|
return this.db.session()
|
@@ -157,7 +161,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
157
161
|
return tablesUsed
|
158
162
|
}
|
159
163
|
|
160
|
-
|
164
|
+
cachedExecute(
|
161
165
|
queryStr: string,
|
162
166
|
bindValues?: PreparedBindValues | undefined,
|
163
167
|
options?: {
|
@@ -213,6 +217,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
213
217
|
span.recordException(cause)
|
214
218
|
span.end()
|
215
219
|
if (LS_DEV) {
|
220
|
+
// biome-ignore lint/suspicious/noDebugger: debug
|
216
221
|
debugger
|
217
222
|
}
|
218
223
|
throw new SqliteError({ cause, query: { bindValues: bindValues ?? {}, sql: queryStr } })
|
@@ -221,7 +226,11 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
221
226
|
)
|
222
227
|
}
|
223
228
|
|
224
|
-
|
229
|
+
execute = SqliteDbHelper.makeExecute((queryStr, bindValues) => this.cachedExecute(queryStr, bindValues))
|
230
|
+
|
231
|
+
select = SqliteDbHelper.makeSelect((queryStr, bindValues) => this.cachedSelect(queryStr, bindValues))
|
232
|
+
|
233
|
+
cachedSelect<T = any>(
|
225
234
|
queryStr: string,
|
226
235
|
bindValues?: PreparedBindValues | undefined,
|
227
236
|
options?: {
|
package/src/effect/LiveStore.ts
CHANGED
@@ -35,7 +35,7 @@ export const makeLiveStoreContext = <TSchema extends LiveStoreSchema, TContext =
|
|
35
35
|
|
36
36
|
globalThis.__debugLiveStore ??= {}
|
37
37
|
if (Object.keys(globalThis.__debugLiveStore).length === 0) {
|
38
|
-
globalThis.__debugLiveStore
|
38
|
+
globalThis.__debugLiveStore._ = store
|
39
39
|
}
|
40
40
|
globalThis.__debugLiveStore[storeId] = store
|
41
41
|
|
@@ -1,5 +1,394 @@
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
2
2
|
|
3
|
+
exports[`otel > QueryBuilder subscription - basic functionality 1`] = `
|
4
|
+
{
|
5
|
+
"_name": "createStore",
|
6
|
+
"attributes": {
|
7
|
+
"debugInstanceId": "test",
|
8
|
+
"storeId": "default",
|
9
|
+
},
|
10
|
+
"children": [
|
11
|
+
{
|
12
|
+
"_name": "livestore.in-memory-db:execute",
|
13
|
+
"attributes": {
|
14
|
+
"sql.query": "
|
15
|
+
PRAGMA page_size=32768;
|
16
|
+
PRAGMA cache_size=10000;
|
17
|
+
PRAGMA synchronous='OFF';
|
18
|
+
PRAGMA temp_store='MEMORY';
|
19
|
+
PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
|
20
|
+
",
|
21
|
+
},
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"_name": "@livestore/common:LeaderSyncProcessor:push",
|
25
|
+
"attributes": {
|
26
|
+
"batch": "undefined",
|
27
|
+
"batchSize": 1,
|
28
|
+
},
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"_name": "client-session-sync-processor:pull",
|
32
|
+
"attributes": {
|
33
|
+
"code.stacktrace": "<STACKTRACE>",
|
34
|
+
"span.label": "⚠︎ Interrupted",
|
35
|
+
"status.interrupted": true,
|
36
|
+
},
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"_name": "LiveStore:sync",
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"_name": "LiveStore:commits",
|
43
|
+
"children": [
|
44
|
+
{
|
45
|
+
"_name": "LiveStore:commit",
|
46
|
+
"attributes": {
|
47
|
+
"livestore.eventTags": [
|
48
|
+
"livestore.RawSql",
|
49
|
+
],
|
50
|
+
"livestore.eventsCount": 1,
|
51
|
+
},
|
52
|
+
"children": [
|
53
|
+
{
|
54
|
+
"_name": "livestore.in-memory-db:execute",
|
55
|
+
"attributes": {
|
56
|
+
"sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
|
57
|
+
},
|
58
|
+
},
|
59
|
+
],
|
60
|
+
},
|
61
|
+
],
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"_name": "LiveStore:queries",
|
65
|
+
"children": [
|
66
|
+
{
|
67
|
+
"_name": "LiveStore.subscribe",
|
68
|
+
"attributes": {
|
69
|
+
"queryLabel": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
70
|
+
},
|
71
|
+
"children": [
|
72
|
+
{
|
73
|
+
"_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
74
|
+
"attributes": {
|
75
|
+
"livestore.debugRefreshReason": "subscribe-initial-run:undefined",
|
76
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
77
|
+
"sql.rowsCount": 0,
|
78
|
+
},
|
79
|
+
"children": [
|
80
|
+
{
|
81
|
+
"_name": "sql-in-memory-select",
|
82
|
+
"attributes": {
|
83
|
+
"sql.cached": false,
|
84
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
85
|
+
"sql.rowsCount": 0,
|
86
|
+
},
|
87
|
+
},
|
88
|
+
],
|
89
|
+
},
|
90
|
+
{
|
91
|
+
"_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
92
|
+
"attributes": {
|
93
|
+
"livestore.debugRefreshReason": "commit",
|
94
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
95
|
+
"sql.rowsCount": 1,
|
96
|
+
},
|
97
|
+
"children": [
|
98
|
+
{
|
99
|
+
"_name": "sql-in-memory-select",
|
100
|
+
"attributes": {
|
101
|
+
"sql.cached": false,
|
102
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
103
|
+
"sql.rowsCount": 1,
|
104
|
+
},
|
105
|
+
},
|
106
|
+
],
|
107
|
+
},
|
108
|
+
],
|
109
|
+
},
|
110
|
+
],
|
111
|
+
},
|
112
|
+
],
|
113
|
+
}
|
114
|
+
`;
|
115
|
+
|
116
|
+
exports[`otel > QueryBuilder subscription - direct table subscription 1`] = `
|
117
|
+
{
|
118
|
+
"_name": "createStore",
|
119
|
+
"attributes": {
|
120
|
+
"debugInstanceId": "test",
|
121
|
+
"storeId": "default",
|
122
|
+
},
|
123
|
+
"children": [
|
124
|
+
{
|
125
|
+
"_name": "livestore.in-memory-db:execute",
|
126
|
+
"attributes": {
|
127
|
+
"sql.query": "
|
128
|
+
PRAGMA page_size=32768;
|
129
|
+
PRAGMA cache_size=10000;
|
130
|
+
PRAGMA synchronous='OFF';
|
131
|
+
PRAGMA temp_store='MEMORY';
|
132
|
+
PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
|
133
|
+
",
|
134
|
+
},
|
135
|
+
},
|
136
|
+
{
|
137
|
+
"_name": "@livestore/common:LeaderSyncProcessor:push",
|
138
|
+
"attributes": {
|
139
|
+
"batch": "undefined",
|
140
|
+
"batchSize": 1,
|
141
|
+
},
|
142
|
+
},
|
143
|
+
{
|
144
|
+
"_name": "client-session-sync-processor:pull",
|
145
|
+
"attributes": {
|
146
|
+
"code.stacktrace": "<STACKTRACE>",
|
147
|
+
"span.label": "⚠︎ Interrupted",
|
148
|
+
"status.interrupted": true,
|
149
|
+
},
|
150
|
+
},
|
151
|
+
{
|
152
|
+
"_name": "LiveStore:sync",
|
153
|
+
},
|
154
|
+
{
|
155
|
+
"_name": "LiveStore:commits",
|
156
|
+
"children": [
|
157
|
+
{
|
158
|
+
"_name": "LiveStore:commit",
|
159
|
+
"attributes": {
|
160
|
+
"livestore.eventTags": [
|
161
|
+
"livestore.RawSql",
|
162
|
+
],
|
163
|
+
"livestore.eventsCount": 1,
|
164
|
+
},
|
165
|
+
"children": [
|
166
|
+
{
|
167
|
+
"_name": "livestore.in-memory-db:execute",
|
168
|
+
"attributes": {
|
169
|
+
"sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t5', 'clean house', 1)",
|
170
|
+
},
|
171
|
+
},
|
172
|
+
],
|
173
|
+
},
|
174
|
+
],
|
175
|
+
},
|
176
|
+
{
|
177
|
+
"_name": "LiveStore:queries",
|
178
|
+
"children": [
|
179
|
+
{
|
180
|
+
"_name": "LiveStore.subscribe",
|
181
|
+
"attributes": {
|
182
|
+
"queryLabel": "SELECT * FROM 'todos'",
|
183
|
+
},
|
184
|
+
"children": [
|
185
|
+
{
|
186
|
+
"_name": "db:SELECT * FROM 'todos'",
|
187
|
+
"attributes": {
|
188
|
+
"livestore.debugRefreshReason": "subscribe-initial-run:undefined",
|
189
|
+
"sql.query": "SELECT * FROM 'todos'",
|
190
|
+
"sql.rowsCount": 0,
|
191
|
+
},
|
192
|
+
"children": [
|
193
|
+
{
|
194
|
+
"_name": "sql-in-memory-select",
|
195
|
+
"attributes": {
|
196
|
+
"sql.cached": false,
|
197
|
+
"sql.query": "SELECT * FROM 'todos'",
|
198
|
+
"sql.rowsCount": 0,
|
199
|
+
},
|
200
|
+
},
|
201
|
+
],
|
202
|
+
},
|
203
|
+
{
|
204
|
+
"_name": "db:SELECT * FROM 'todos'",
|
205
|
+
"attributes": {
|
206
|
+
"livestore.debugRefreshReason": "commit",
|
207
|
+
"sql.query": "SELECT * FROM 'todos'",
|
208
|
+
"sql.rowsCount": 1,
|
209
|
+
},
|
210
|
+
"children": [
|
211
|
+
{
|
212
|
+
"_name": "sql-in-memory-select",
|
213
|
+
"attributes": {
|
214
|
+
"sql.cached": false,
|
215
|
+
"sql.query": "SELECT * FROM 'todos'",
|
216
|
+
"sql.rowsCount": 1,
|
217
|
+
},
|
218
|
+
},
|
219
|
+
],
|
220
|
+
},
|
221
|
+
],
|
222
|
+
},
|
223
|
+
],
|
224
|
+
},
|
225
|
+
],
|
226
|
+
}
|
227
|
+
`;
|
228
|
+
|
229
|
+
exports[`otel > QueryBuilder subscription - unsubscribe functionality 1`] = `
|
230
|
+
{
|
231
|
+
"_name": "createStore",
|
232
|
+
"attributes": {
|
233
|
+
"debugInstanceId": "test",
|
234
|
+
"storeId": "default",
|
235
|
+
},
|
236
|
+
"children": [
|
237
|
+
{
|
238
|
+
"_name": "livestore.in-memory-db:execute",
|
239
|
+
"attributes": {
|
240
|
+
"sql.query": "
|
241
|
+
PRAGMA page_size=32768;
|
242
|
+
PRAGMA cache_size=10000;
|
243
|
+
PRAGMA synchronous='OFF';
|
244
|
+
PRAGMA temp_store='MEMORY';
|
245
|
+
PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
|
246
|
+
",
|
247
|
+
},
|
248
|
+
},
|
249
|
+
{
|
250
|
+
"_name": "@livestore/common:LeaderSyncProcessor:push",
|
251
|
+
"attributes": {
|
252
|
+
"batch": "undefined",
|
253
|
+
"batchSize": 1,
|
254
|
+
},
|
255
|
+
},
|
256
|
+
{
|
257
|
+
"_name": "@livestore/common:LeaderSyncProcessor:push",
|
258
|
+
"attributes": {
|
259
|
+
"batch": "undefined",
|
260
|
+
"batchSize": 1,
|
261
|
+
},
|
262
|
+
},
|
263
|
+
{
|
264
|
+
"_name": "client-session-sync-processor:pull",
|
265
|
+
"attributes": {
|
266
|
+
"code.stacktrace": "<STACKTRACE>",
|
267
|
+
"span.label": "⚠︎ Interrupted",
|
268
|
+
"status.interrupted": true,
|
269
|
+
},
|
270
|
+
},
|
271
|
+
{
|
272
|
+
"_name": "LiveStore:sync",
|
273
|
+
},
|
274
|
+
{
|
275
|
+
"_name": "LiveStore:commits",
|
276
|
+
"children": [
|
277
|
+
{
|
278
|
+
"_name": "LiveStore:commit",
|
279
|
+
"attributes": {
|
280
|
+
"livestore.eventTags": [
|
281
|
+
"livestore.RawSql",
|
282
|
+
],
|
283
|
+
"livestore.eventsCount": 1,
|
284
|
+
},
|
285
|
+
"children": [
|
286
|
+
{
|
287
|
+
"_name": "livestore.in-memory-db:execute",
|
288
|
+
"attributes": {
|
289
|
+
"sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t3', 'read book', 0)",
|
290
|
+
},
|
291
|
+
},
|
292
|
+
],
|
293
|
+
},
|
294
|
+
{
|
295
|
+
"_name": "LiveStore:commit",
|
296
|
+
"attributes": {
|
297
|
+
"livestore.eventTags": [
|
298
|
+
"livestore.RawSql",
|
299
|
+
],
|
300
|
+
"livestore.eventsCount": 1,
|
301
|
+
},
|
302
|
+
"children": [
|
303
|
+
{
|
304
|
+
"_name": "livestore.in-memory-db:execute",
|
305
|
+
"attributes": {
|
306
|
+
"sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t4', 'cook dinner', 0)",
|
307
|
+
},
|
308
|
+
},
|
309
|
+
],
|
310
|
+
},
|
311
|
+
],
|
312
|
+
},
|
313
|
+
{
|
314
|
+
"_name": "LiveStore:queries",
|
315
|
+
"children": [
|
316
|
+
{
|
317
|
+
"_name": "LiveStore.subscribe",
|
318
|
+
"attributes": {
|
319
|
+
"queryLabel": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
320
|
+
},
|
321
|
+
"children": [
|
322
|
+
{
|
323
|
+
"_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
324
|
+
"attributes": {
|
325
|
+
"livestore.debugRefreshReason": "subscribe-initial-run:undefined",
|
326
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
327
|
+
"sql.rowsCount": 0,
|
328
|
+
},
|
329
|
+
"children": [
|
330
|
+
{
|
331
|
+
"_name": "sql-in-memory-select",
|
332
|
+
"attributes": {
|
333
|
+
"sql.cached": false,
|
334
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
335
|
+
"sql.rowsCount": 0,
|
336
|
+
},
|
337
|
+
},
|
338
|
+
],
|
339
|
+
},
|
340
|
+
{
|
341
|
+
"_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
342
|
+
"attributes": {
|
343
|
+
"livestore.debugRefreshReason": "commit",
|
344
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
345
|
+
"sql.rowsCount": 1,
|
346
|
+
},
|
347
|
+
"children": [
|
348
|
+
{
|
349
|
+
"_name": "sql-in-memory-select",
|
350
|
+
"attributes": {
|
351
|
+
"sql.cached": false,
|
352
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
353
|
+
"sql.rowsCount": 1,
|
354
|
+
},
|
355
|
+
},
|
356
|
+
],
|
357
|
+
},
|
358
|
+
],
|
359
|
+
},
|
360
|
+
{
|
361
|
+
"_name": "LiveStore.subscribe",
|
362
|
+
"attributes": {
|
363
|
+
"queryLabel": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
364
|
+
},
|
365
|
+
"children": [
|
366
|
+
{
|
367
|
+
"_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
368
|
+
"attributes": {
|
369
|
+
"livestore.debugRefreshReason": "commit",
|
370
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
371
|
+
"sql.rowsCount": 1,
|
372
|
+
},
|
373
|
+
"children": [
|
374
|
+
{
|
375
|
+
"_name": "sql-in-memory-select",
|
376
|
+
"attributes": {
|
377
|
+
"sql.cached": false,
|
378
|
+
"sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
|
379
|
+
"sql.rowsCount": 1,
|
380
|
+
},
|
381
|
+
},
|
382
|
+
],
|
383
|
+
},
|
384
|
+
],
|
385
|
+
},
|
386
|
+
],
|
387
|
+
},
|
388
|
+
],
|
389
|
+
}
|
390
|
+
`;
|
391
|
+
|
3
392
|
exports[`otel > otel 3`] = `
|
4
393
|
{
|
5
394
|
"_name": "createStore",
|
@@ -34,7 +34,7 @@ export const makeExecBeforeFirstRun =
|
|
34
34
|
|
35
35
|
const idVal = id === SessionIdSymbol ? store.sessionId : id!
|
36
36
|
const rowExists =
|
37
|
-
store.sqliteDbWrapper.
|
37
|
+
store.sqliteDbWrapper.cachedSelect(
|
38
38
|
`SELECT 1 FROM '${table.sqliteDef.name}' WHERE id = ?`,
|
39
39
|
[idVal] as any as PreparedBindValues,
|
40
40
|
{ otelContext },
|
@@ -155,7 +155,9 @@ Vitest.describe('otel', () => {
|
|
155
155
|
const defaultTodo = { id: '', text: '', completed: false }
|
156
156
|
|
157
157
|
const filter = computed(() => ({ completed: false }))
|
158
|
-
const query$ = queryDb((get) =>
|
158
|
+
const query$ = queryDb((get) =>
|
159
|
+
tables.todos.where(get(filter)).first({ behaviour: 'fallback', fallback: () => defaultTodo }),
|
160
|
+
)
|
159
161
|
|
160
162
|
expect(store.query(query$)).toMatchInlineSnapshot(`
|
161
163
|
{
|
@@ -189,4 +191,145 @@ Vitest.describe('otel', () => {
|
|
189
191
|
),
|
190
192
|
),
|
191
193
|
)
|
194
|
+
|
195
|
+
Vitest.scopedLive('QueryBuilder subscription - basic functionality', () =>
|
196
|
+
Effect.gen(function* () {
|
197
|
+
const { store, exporter, span, provider } = yield* makeQuery
|
198
|
+
|
199
|
+
const callbackResults: any[] = []
|
200
|
+
const defaultTodo = { id: '', text: '', completed: false }
|
201
|
+
|
202
|
+
const queryBuilder = tables.todos
|
203
|
+
.where({ completed: false })
|
204
|
+
.first({ behaviour: 'fallback', fallback: () => defaultTodo })
|
205
|
+
|
206
|
+
const unsubscribe = store.subscribe(queryBuilder, {
|
207
|
+
onUpdate: (result) => {
|
208
|
+
callbackResults.push(result)
|
209
|
+
},
|
210
|
+
})
|
211
|
+
|
212
|
+
expect(callbackResults).toHaveLength(1)
|
213
|
+
expect(callbackResults[0]).toMatchObject(defaultTodo)
|
214
|
+
|
215
|
+
store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
|
216
|
+
|
217
|
+
expect(callbackResults).toHaveLength(2)
|
218
|
+
expect(callbackResults[1]).toMatchObject({
|
219
|
+
id: 't1',
|
220
|
+
text: 'buy milk',
|
221
|
+
completed: false,
|
222
|
+
})
|
223
|
+
|
224
|
+
unsubscribe()
|
225
|
+
span.end()
|
226
|
+
|
227
|
+
return { exporter, provider }
|
228
|
+
}).pipe(
|
229
|
+
Effect.scoped,
|
230
|
+
Effect.tap(({ exporter, provider }) =>
|
231
|
+
Effect.promise(async () => {
|
232
|
+
await provider.forceFlush()
|
233
|
+
expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
|
234
|
+
await provider.shutdown()
|
235
|
+
}),
|
236
|
+
),
|
237
|
+
),
|
238
|
+
)
|
239
|
+
|
240
|
+
Vitest.scopedLive('QueryBuilder subscription - unsubscribe functionality', () =>
|
241
|
+
Effect.gen(function* () {
|
242
|
+
const { store, exporter, span, provider } = yield* makeQuery
|
243
|
+
|
244
|
+
const callbackResults1: any[] = []
|
245
|
+
const callbackResults2: any[] = []
|
246
|
+
const defaultTodo = { id: '', text: '', completed: false }
|
247
|
+
|
248
|
+
const queryBuilder = tables.todos
|
249
|
+
.where({ completed: false })
|
250
|
+
.first({ behaviour: 'fallback', fallback: () => defaultTodo })
|
251
|
+
|
252
|
+
const unsubscribe1 = store.subscribe(queryBuilder, {
|
253
|
+
onUpdate: (result) => {
|
254
|
+
callbackResults1.push(result)
|
255
|
+
},
|
256
|
+
})
|
257
|
+
|
258
|
+
const unsubscribe2 = store.subscribe(queryBuilder, {
|
259
|
+
onUpdate: (result) => {
|
260
|
+
callbackResults2.push(result)
|
261
|
+
},
|
262
|
+
})
|
263
|
+
|
264
|
+
expect(callbackResults1).toHaveLength(1)
|
265
|
+
expect(callbackResults2).toHaveLength(1)
|
266
|
+
|
267
|
+
store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t3', 'read book', 0)` }))
|
268
|
+
|
269
|
+
expect(callbackResults1).toHaveLength(2)
|
270
|
+
expect(callbackResults2).toHaveLength(2)
|
271
|
+
|
272
|
+
unsubscribe1()
|
273
|
+
|
274
|
+
store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t4', 'cook dinner', 0)` }))
|
275
|
+
|
276
|
+
expect(callbackResults1).toHaveLength(2)
|
277
|
+
expect(callbackResults2).toHaveLength(3)
|
278
|
+
|
279
|
+
unsubscribe2()
|
280
|
+
span.end()
|
281
|
+
|
282
|
+
return { exporter, provider }
|
283
|
+
}).pipe(
|
284
|
+
Effect.scoped,
|
285
|
+
Effect.tap(({ exporter, provider }) =>
|
286
|
+
Effect.promise(async () => {
|
287
|
+
await provider.forceFlush()
|
288
|
+
expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
|
289
|
+
await provider.shutdown()
|
290
|
+
}),
|
291
|
+
),
|
292
|
+
),
|
293
|
+
)
|
294
|
+
|
295
|
+
Vitest.scopedLive('QueryBuilder subscription - direct table subscription', () =>
|
296
|
+
Effect.gen(function* () {
|
297
|
+
const { store, exporter, span, provider } = yield* makeQuery
|
298
|
+
|
299
|
+
const callbackResults: any[] = []
|
300
|
+
|
301
|
+
const unsubscribe = store.subscribe(tables.todos, {
|
302
|
+
onUpdate: (result) => {
|
303
|
+
callbackResults.push(result)
|
304
|
+
},
|
305
|
+
})
|
306
|
+
|
307
|
+
expect(callbackResults).toHaveLength(1)
|
308
|
+
expect(callbackResults[0]).toEqual([])
|
309
|
+
|
310
|
+
store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t5', 'clean house', 1)` }))
|
311
|
+
|
312
|
+
expect(callbackResults).toHaveLength(2)
|
313
|
+
expect(callbackResults[1]).toHaveLength(1)
|
314
|
+
expect(callbackResults[1][0]).toMatchObject({
|
315
|
+
id: 't5',
|
316
|
+
text: 'clean house',
|
317
|
+
completed: true,
|
318
|
+
})
|
319
|
+
|
320
|
+
unsubscribe()
|
321
|
+
span.end()
|
322
|
+
|
323
|
+
return { exporter, provider }
|
324
|
+
}).pipe(
|
325
|
+
Effect.scoped,
|
326
|
+
Effect.tap(({ exporter, provider }) =>
|
327
|
+
Effect.promise(async () => {
|
328
|
+
await provider.forceFlush()
|
329
|
+
expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
|
330
|
+
await provider.shutdown()
|
331
|
+
}),
|
332
|
+
),
|
333
|
+
),
|
334
|
+
)
|
192
335
|
})
|
@@ -372,7 +372,7 @@ export class LiveStoreDbQuery<TResultSchema, TResult = TResultSchema> extends Li
|
|
372
372
|
span.setAttribute('sql.query', sqlString)
|
373
373
|
span.updateName(`db:${sqlString.slice(0, 50)}`)
|
374
374
|
|
375
|
-
const rawDbResults = store.sqliteDbWrapper.
|
375
|
+
const rawDbResults = store.sqliteDbWrapper.cachedSelect<any>(
|
376
376
|
sqlString,
|
377
377
|
bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
|
378
378
|
{
|