@livestore/livestore 0.3.1 → 0.3.2-dev.1

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.
Files changed (100) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/QueryCache.d.ts +4 -6
  3. package/dist/QueryCache.d.ts.map +1 -1
  4. package/dist/QueryCache.js +10 -6
  5. package/dist/QueryCache.js.map +1 -1
  6. package/dist/SqliteDbWrapper.d.ts +3 -2
  7. package/dist/SqliteDbWrapper.d.ts.map +1 -1
  8. package/dist/SqliteDbWrapper.js +9 -6
  9. package/dist/SqliteDbWrapper.js.map +1 -1
  10. package/dist/SqliteDbWrapper.test.js +1 -1
  11. package/dist/effect/LiveStore.d.ts +2 -2
  12. package/dist/effect/LiveStore.js +2 -2
  13. package/dist/effect/LiveStore.js.map +1 -1
  14. package/dist/effect/mod.d.ts +2 -2
  15. package/dist/effect/mod.d.ts.map +1 -1
  16. package/dist/effect/mod.js +2 -2
  17. package/dist/effect/mod.js.map +1 -1
  18. package/dist/internal/mod.d.ts +2 -2
  19. package/dist/internal/mod.js +2 -2
  20. package/dist/live-queries/base-class.d.ts +4 -4
  21. package/dist/live-queries/base-class.d.ts.map +1 -1
  22. package/dist/live-queries/base-class.js +1 -1
  23. package/dist/live-queries/base-class.js.map +1 -1
  24. package/dist/live-queries/client-document-get-query.d.ts +1 -1
  25. package/dist/live-queries/computed.d.ts +4 -4
  26. package/dist/live-queries/computed.js +2 -2
  27. package/dist/live-queries/db-query.d.ts +4 -4
  28. package/dist/live-queries/db-query.js +4 -4
  29. package/dist/live-queries/db-query.test.js +96 -6
  30. package/dist/live-queries/db-query.test.js.map +1 -1
  31. package/dist/live-queries/mod.d.ts +4 -4
  32. package/dist/live-queries/mod.js +4 -4
  33. package/dist/live-queries/signal.d.ts +4 -4
  34. package/dist/live-queries/signal.js +1 -1
  35. package/dist/live-queries/signal.test.js +3 -3
  36. package/dist/mod.d.ts +10 -10
  37. package/dist/mod.d.ts.map +1 -1
  38. package/dist/mod.js +9 -9
  39. package/dist/mod.js.map +1 -1
  40. package/dist/reactive.d.ts.map +1 -1
  41. package/dist/reactive.js +2 -3
  42. package/dist/reactive.js.map +1 -1
  43. package/dist/reactive.test.js +1 -1
  44. package/dist/store/create-store.d.ts +6 -3
  45. package/dist/store/create-store.d.ts.map +1 -1
  46. package/dist/store/create-store.js +3 -2
  47. package/dist/store/create-store.js.map +1 -1
  48. package/dist/store/devtools.d.ts +3 -3
  49. package/dist/store/devtools.js +2 -2
  50. package/dist/store/store-types.d.ts +7 -4
  51. package/dist/store/store-types.d.ts.map +1 -1
  52. package/dist/store/store-types.js.map +1 -1
  53. package/dist/store/store.d.ts +12 -11
  54. package/dist/store/store.d.ts.map +1 -1
  55. package/dist/store/store.js +23 -15
  56. package/dist/store/store.js.map +1 -1
  57. package/dist/utils/dev.d.ts.map +1 -1
  58. package/dist/utils/dev.js +0 -1
  59. package/dist/utils/dev.js.map +1 -1
  60. package/dist/utils/stack-info.d.ts.map +1 -1
  61. package/dist/utils/stack-info.js +5 -1
  62. package/dist/utils/stack-info.js.map +1 -1
  63. package/dist/utils/stack-info.test.js +7 -3
  64. package/dist/utils/stack-info.test.js.map +1 -1
  65. package/dist/utils/tests/mod.d.ts +3 -3
  66. package/dist/utils/tests/mod.d.ts.map +1 -1
  67. package/dist/utils/tests/mod.js +3 -3
  68. package/dist/utils/tests/mod.js.map +1 -1
  69. package/dist/utils/tests/otel.d.ts.map +1 -1
  70. package/dist/utils/tests/otel.js +1 -1
  71. package/dist/utils/tests/otel.js.map +1 -1
  72. package/package.json +7 -7
  73. package/src/QueryCache.ts +14 -11
  74. package/src/SqliteDbWrapper.test.ts +1 -1
  75. package/src/SqliteDbWrapper.ts +13 -12
  76. package/src/ambient.d.ts +0 -2
  77. package/src/effect/LiveStore.ts +3 -3
  78. package/src/effect/mod.ts +4 -4
  79. package/src/internal/mod.ts +2 -2
  80. package/src/live-queries/__snapshots__/db-query.test.ts.snap +389 -0
  81. package/src/live-queries/base-class.ts +11 -10
  82. package/src/live-queries/client-document-get-query.ts +1 -1
  83. package/src/live-queries/computed.ts +5 -5
  84. package/src/live-queries/db-query.test.ts +149 -6
  85. package/src/live-queries/db-query.ts +7 -7
  86. package/src/live-queries/mod.ts +4 -4
  87. package/src/live-queries/signal.test.ts +3 -3
  88. package/src/live-queries/signal.ts +4 -4
  89. package/src/mod.ts +33 -39
  90. package/src/reactive.test.ts +1 -1
  91. package/src/reactive.ts +2 -4
  92. package/src/store/create-store.ts +8 -3
  93. package/src/store/devtools.ts +5 -5
  94. package/src/store/store-types.ts +13 -4
  95. package/src/store/store.ts +28 -21
  96. package/src/utils/dev.ts +0 -1
  97. package/src/utils/stack-info.test.ts +7 -3
  98. package/src/utils/stack-info.ts +5 -1
  99. package/src/utils/tests/mod.ts +3 -3
  100. package/src/utils/tests/otel.ts +2 -1
@@ -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",
@@ -2,10 +2,10 @@ import { isNotNil } from '@livestore/utils'
2
2
  import { Predicate } from '@livestore/utils/effect'
3
3
  import type * as otel from '@opentelemetry/api'
4
4
 
5
- import * as RG from '../reactive.js'
6
- import type { Store } from '../store/store.js'
7
- import type { QueryDebugInfo, RefreshReason } from '../store/store-types.js'
8
- import type { StackInfo } from '../utils/stack-info.js'
5
+ import * as RG from '../reactive.ts'
6
+ import type { Store } from '../store/store.ts'
7
+ import type { QueryDebugInfo, RefreshReason } from '../store/store-types.ts'
8
+ import type { StackInfo } from '../utils/stack-info.ts'
9
9
 
10
10
  export type ReactivityGraph = RG.ReactiveGraph<RefreshReason, QueryDebugInfo, ReactivityGraphContext>
11
11
 
@@ -23,14 +23,15 @@ export type ReactivityGraphContext = {
23
23
  effectsWrapper: (run: () => void) => void
24
24
  }
25
25
 
26
- export type GetResult<TQuery extends LiveQueryDef.Any | LiveQuery.Any | SignalDef<any>> =
27
- TQuery extends LiveQuery<infer TResult>
26
+ export type GetResult<TQuery extends LiveQueryDef.Any | LiveQuery.Any | SignalDef<any>> = TQuery extends LiveQuery<
27
+ infer TResult
28
+ >
29
+ ? TResult
30
+ : TQuery extends LiveQueryDef<infer TResult>
28
31
  ? TResult
29
- : TQuery extends LiveQueryDef<infer TResult>
32
+ : TQuery extends SignalDef<infer TResult>
30
33
  ? TResult
31
- : TQuery extends SignalDef<infer TResult>
32
- ? TResult
33
- : unknown
34
+ : unknown
34
35
 
35
36
  let queryIdCounter = 0
36
37
 
@@ -4,7 +4,7 @@ import { State } from '@livestore/common/schema'
4
4
  import { shouldNeverHappen } from '@livestore/utils'
5
5
  import type * as otel from '@opentelemetry/api'
6
6
 
7
- import type { ReactivityGraphContext } from './base-class.js'
7
+ import type { ReactivityGraphContext } from './base-class.ts'
8
8
 
9
9
  export const rowQueryLabel = (
10
10
  table: State.SQLite.ClientDocumentTableDef.Any,
@@ -1,11 +1,11 @@
1
1
  import { getDurationMsFromSpan } from '@livestore/common'
2
2
  import * as otel from '@opentelemetry/api'
3
3
 
4
- import type { Thunk } from '../reactive.js'
5
- import type { RefreshReason } from '../store/store-types.js'
6
- import { isValidFunctionString } from '../utils/function-string.js'
7
- import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
8
- import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
4
+ import type { Thunk } from '../reactive.ts'
5
+ import type { RefreshReason } from '../store/store-types.ts'
6
+ import { isValidFunctionString } from '../utils/function-string.ts'
7
+ import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.ts'
8
+ import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.ts'
9
9
 
10
10
  export const computed = <TResult>(
11
11
  fn: (get: GetAtomResult) => TResult,
@@ -6,11 +6,11 @@ import * as otel from '@opentelemetry/api'
6
6
  import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
7
7
  import { expect } from 'vitest'
8
8
 
9
- import * as RG from '../reactive.js'
10
- import { makeTodoMvc, tables } from '../utils/tests/fixture.js'
11
- import { getSimplifiedRootSpan } from '../utils/tests/otel.js'
12
- import { computed } from './computed.js'
13
- import { queryDb } from './db-query.js'
9
+ import * as RG from '../reactive.ts'
10
+ import { makeTodoMvc, tables } from '../utils/tests/fixture.ts'
11
+ import { getSimplifiedRootSpan } from '../utils/tests/otel.ts'
12
+ import { computed } from './computed.ts'
13
+ import { queryDb } from './db-query.ts'
14
14
 
15
15
  /*
16
16
  TODO write tests for:
@@ -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) => tables.todos.where(get(filter)).first({ fallback: () => defaultTodo }))
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
  })
@@ -13,13 +13,13 @@ import { deepEqual, shouldNeverHappen } from '@livestore/utils'
13
13
  import { Predicate, Schema, TreeFormatter } from '@livestore/utils/effect'
14
14
  import * as otel from '@opentelemetry/api'
15
15
 
16
- import type { Thunk } from '../reactive.js'
17
- import { isThunk, NOT_REFRESHED_YET } from '../reactive.js'
18
- import type { RefreshReason } from '../store/store-types.js'
19
- import { isValidFunctionString } from '../utils/function-string.js'
20
- import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
21
- import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
22
- import { makeExecBeforeFirstRun, rowQueryLabel } from './client-document-get-query.js'
16
+ import type { Thunk } from '../reactive.ts'
17
+ import { isThunk, NOT_REFRESHED_YET } from '../reactive.ts'
18
+ import type { RefreshReason } from '../store/store-types.ts'
19
+ import { isValidFunctionString } from '../utils/function-string.ts'
20
+ import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.ts'
21
+ import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.ts'
22
+ import { makeExecBeforeFirstRun, rowQueryLabel } from './client-document-get-query.ts'
23
23
 
24
24
  export type QueryInputRaw<TDecoded, TEncoded> = {
25
25
  query: string
@@ -1,4 +1,4 @@
1
- export * from './base-class.js'
2
- export * from './computed.js'
3
- export * from './db-query.js'
4
- export * from './signal.js'
1
+ export * from './base-class.ts'
2
+ export * from './computed.ts'
3
+ export * from './db-query.ts'
4
+ export * from './signal.ts'
@@ -2,9 +2,9 @@ import { Effect } from '@livestore/utils/effect'
2
2
  import { Vitest } from '@livestore/utils-dev/node-vitest'
3
3
  import { expect } from 'vitest'
4
4
 
5
- import { makeTodoMvc } from '../utils/tests/fixture.js'
6
- import { computed } from './computed.js'
7
- import { signal } from './signal.js'
5
+ import { makeTodoMvc } from '../utils/tests/fixture.ts'
6
+ import { computed } from './computed.ts'
7
+ import { signal } from './signal.ts'
8
8
 
9
9
  Vitest.describe('signal', () => {
10
10
  Vitest.scopedLive('should be able to create a signal', () =>