@livestore/livestore 0.1.0-dev.9 → 0.2.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.
Files changed (112) hide show
  1. package/README.md +3 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/global-state.d.ts +1 -1
  4. package/dist/global-state.d.ts.map +1 -1
  5. package/dist/global-state.js +1 -1
  6. package/dist/global-state.js.map +1 -1
  7. package/dist/index.d.ts +6 -6
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +5 -5
  10. package/dist/index.js.map +1 -1
  11. package/dist/live-queries/base-class.d.ts +64 -0
  12. package/dist/live-queries/base-class.d.ts.map +1 -0
  13. package/dist/live-queries/base-class.js +31 -0
  14. package/dist/live-queries/base-class.js.map +1 -0
  15. package/dist/live-queries/computed.d.ts +26 -0
  16. package/dist/live-queries/computed.d.ts.map +1 -0
  17. package/dist/{reactiveQueries/js.js → live-queries/computed.js} +7 -26
  18. package/dist/live-queries/computed.js.map +1 -0
  19. package/dist/live-queries/db.d.ts +66 -0
  20. package/dist/live-queries/db.d.ts.map +1 -0
  21. package/dist/live-queries/db.js +199 -0
  22. package/dist/live-queries/db.js.map +1 -0
  23. package/dist/live-queries/db.test.d.ts +2 -0
  24. package/dist/live-queries/db.test.d.ts.map +1 -0
  25. package/dist/live-queries/db.test.js +117 -0
  26. package/dist/live-queries/db.test.js.map +1 -0
  27. package/dist/live-queries/graphql.d.ts +49 -0
  28. package/dist/live-queries/graphql.d.ts.map +1 -0
  29. package/dist/live-queries/graphql.js +122 -0
  30. package/dist/live-queries/graphql.js.map +1 -0
  31. package/dist/live-queries/sql.d.ts +62 -0
  32. package/dist/live-queries/sql.d.ts.map +1 -0
  33. package/dist/live-queries/sql.js +175 -0
  34. package/dist/live-queries/sql.js.map +1 -0
  35. package/dist/live-queries/sql.test.d.ts +2 -0
  36. package/dist/live-queries/sql.test.d.ts.map +1 -0
  37. package/{src/reactiveQueries/sql.test.ts → dist/live-queries/sql.test.js} +68 -91
  38. package/dist/live-queries/sql.test.js.map +1 -0
  39. package/dist/reactiveQueries/base-class.d.ts +6 -2
  40. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  41. package/dist/reactiveQueries/base-class.js +2 -0
  42. package/dist/reactiveQueries/base-class.js.map +1 -1
  43. package/dist/reactiveQueries/computed.d.ts +4 -13
  44. package/dist/reactiveQueries/computed.d.ts.map +1 -1
  45. package/dist/reactiveQueries/computed.js +2 -21
  46. package/dist/reactiveQueries/computed.js.map +1 -1
  47. package/dist/reactiveQueries/graphql.d.ts +4 -8
  48. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  49. package/dist/reactiveQueries/graphql.js +1 -15
  50. package/dist/reactiveQueries/graphql.js.map +1 -1
  51. package/dist/reactiveQueries/sql.d.ts +36 -23
  52. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  53. package/dist/reactiveQueries/sql.js +100 -55
  54. package/dist/reactiveQueries/sql.js.map +1 -1
  55. package/dist/reactiveQueries/sql.test.js +12 -11
  56. package/dist/reactiveQueries/sql.test.js.map +1 -1
  57. package/dist/row-query-utils.d.ts +17 -0
  58. package/dist/row-query-utils.d.ts.map +1 -0
  59. package/dist/row-query-utils.js +30 -0
  60. package/dist/row-query-utils.js.map +1 -0
  61. package/dist/row-query.d.ts +10 -27
  62. package/dist/row-query.d.ts.map +1 -1
  63. package/dist/row-query.js +16 -66
  64. package/dist/row-query.js.map +1 -1
  65. package/dist/store/create-store.d.ts +1 -1
  66. package/dist/store/create-store.d.ts.map +1 -1
  67. package/dist/store/devtools.d.ts +1 -1
  68. package/dist/store/devtools.d.ts.map +1 -1
  69. package/dist/store/devtools.js.map +1 -1
  70. package/dist/store/store-types.d.ts +2 -2
  71. package/dist/store/store-types.d.ts.map +1 -1
  72. package/dist/store/store.d.ts +8 -3
  73. package/dist/store/store.d.ts.map +1 -1
  74. package/dist/store/store.js +32 -4
  75. package/dist/store/store.js.map +1 -1
  76. package/dist/utils/tests/fixture.d.ts +168 -132
  77. package/dist/utils/tests/fixture.d.ts.map +1 -1
  78. package/package.json +5 -5
  79. package/src/global-state.ts +1 -1
  80. package/src/index.ts +8 -5
  81. package/src/live-queries/__snapshots__/db.test.ts.snap +301 -0
  82. package/src/{reactiveQueries → live-queries}/base-class.ts +10 -5
  83. package/src/{reactiveQueries → live-queries}/computed.ts +5 -29
  84. package/src/live-queries/db.test.ts +153 -0
  85. package/src/live-queries/db.ts +350 -0
  86. package/src/{reactiveQueries → live-queries}/graphql.ts +6 -21
  87. package/src/row-query-utils.ts +65 -0
  88. package/src/store/create-store.ts +1 -1
  89. package/src/store/devtools.ts +1 -1
  90. package/src/store/store-types.ts +2 -2
  91. package/src/store/store.ts +44 -7
  92. package/dist/reactiveQueries/js.d.ts +0 -35
  93. package/dist/reactiveQueries/js.d.ts.map +0 -1
  94. package/dist/reactiveQueries/js.js.map +0 -1
  95. package/dist/store/store-context.d.ts +0 -26
  96. package/dist/store/store-context.d.ts.map +0 -1
  97. package/dist/store/store-context.js +0 -6
  98. package/dist/store/store-context.js.map +0 -1
  99. package/dist/store-context.d.ts +0 -26
  100. package/dist/store-context.d.ts.map +0 -1
  101. package/dist/store-context.js +0 -6
  102. package/dist/store-context.js.map +0 -1
  103. package/dist/store-devtools.d.ts +0 -19
  104. package/dist/store-devtools.d.ts.map +0 -1
  105. package/dist/store-devtools.js +0 -141
  106. package/dist/store-devtools.js.map +0 -1
  107. package/dist/store.d.ts +0 -175
  108. package/dist/store.d.ts.map +0 -1
  109. package/dist/store.js +0 -509
  110. package/dist/store.js.map +0 -1
  111. package/src/reactiveQueries/sql.ts +0 -226
  112. package/src/row-query.ts +0 -196
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/livestore",
3
- "version": "0.1.0-dev.9",
3
+ "version": "0.2.0-dev.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -31,9 +31,9 @@
31
31
  "dependencies": {
32
32
  "@graphql-typed-document-node/core": "^3.2.0",
33
33
  "@opentelemetry/api": "^1.9.0",
34
- "@livestore/common": "0.1.0-dev.9",
35
- "@livestore/utils": "0.1.0-dev.9",
36
- "@livestore/db-schema": "0.1.0-dev.9"
34
+ "@livestore/common": "0.2.0-dev.0",
35
+ "@livestore/db-schema": "0.2.0-dev.0",
36
+ "@livestore/utils": "0.2.0-dev.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@opentelemetry/sdk-trace-base": "1.27.0",
@@ -41,7 +41,7 @@
41
41
  "typescript": "5.5.4",
42
42
  "vite": "5.4.10",
43
43
  "vitest": "^2.1.4",
44
- "@livestore/web": "0.1.0-dev.9"
44
+ "@livestore/web": "0.2.0-dev.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "graphql": "16.x"
@@ -13,7 +13,7 @@
13
13
 
14
14
  import { GlobalValue } from '@livestore/utils/effect'
15
15
 
16
- import { makeReactivityGraph } from './reactiveQueries/base-class.js'
16
+ import { makeReactivityGraph } from './live-queries/base-class.js'
17
17
 
18
18
  export const globalReactivityGraph = GlobalValue.globalValue('livestore-global-reactivityGraph', () =>
19
19
  makeReactivityGraph(),
package/src/index.ts CHANGED
@@ -25,9 +25,9 @@ export type {
25
25
  Ref,
26
26
  Effect,
27
27
  } from './reactive.js'
28
- export { LiveStoreJSQuery, computed } from './reactiveQueries/computed.js'
29
- export { LiveStoreSQLQuery, querySQL } from './reactiveQueries/sql.js'
30
- export { LiveStoreGraphQLQuery, queryGraphQL } from './reactiveQueries/graphql.js'
28
+ export { LiveStoreComputedQuery, computed } from './live-queries/computed.js'
29
+ export { LiveStoreDbQuery, queryDb } from './live-queries/db.js'
30
+ export { LiveStoreGraphQLQuery, queryGraphQL } from './live-queries/graphql.js'
31
31
  export {
32
32
  type GetAtomResult,
33
33
  type ReactivityGraph,
@@ -35,11 +35,11 @@ export {
35
35
  type LiveQuery,
36
36
  type GetResult,
37
37
  type LiveQueryAny,
38
- } from './reactiveQueries/base-class.js'
38
+ } from './live-queries/base-class.js'
39
39
 
40
40
  export { globalReactivityGraph } from './global-state.js'
41
41
 
42
- export { type RowResult, type RowResultEncoded, rowQuery, deriveColQuery } from './row-query.js'
42
+ export { deriveColQuery } from './row-query-utils.js'
43
43
 
44
44
  export * from '@livestore/common/schema'
45
45
  export {
@@ -52,6 +52,9 @@ export {
52
52
  prepareBindValues,
53
53
  type Bindable,
54
54
  type PreparedBindValues,
55
+ type QueryBuilderAst,
56
+ type QueryBuilder,
57
+ type RowQuery,
55
58
  } from '@livestore/common'
56
59
 
57
60
  export { SqliteAst, SqliteDsl } from '@livestore/db-schema'
@@ -0,0 +1,301 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`otel > otel 3`] = `
4
+ {
5
+ "_name": "test",
6
+ "children": [
7
+ {
8
+ "_name": "livestore.in-memory-db:execute",
9
+ "attributes": {
10
+ "sql.query": "
11
+ PRAGMA page_size=32768;
12
+ PRAGMA cache_size=10000;
13
+ PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
14
+ PRAGMA synchronous='OFF';
15
+ PRAGMA temp_store='MEMORY';
16
+ PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
17
+ ",
18
+ },
19
+ },
20
+ {
21
+ "_name": "LiveStore:mutations",
22
+ "children": [
23
+ {
24
+ "_name": "LiveStore:mutate",
25
+ "attributes": {
26
+ "livestore.mutateLabel": "mutate",
27
+ },
28
+ "children": [
29
+ {
30
+ "_name": "LiveStore:processWrites",
31
+ "attributes": {
32
+ "livestore.mutateLabel": "mutate",
33
+ },
34
+ "children": [
35
+ {
36
+ "_name": "LiveStore:mutateWithoutRefresh",
37
+ "attributes": {
38
+ "livestore.args": "{
39
+ "sql": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)"
40
+ }",
41
+ "livestore.mutation": "livestore.RawSql",
42
+ },
43
+ "children": [
44
+ {
45
+ "_name": "livestore.in-memory-db:execute",
46
+ "attributes": {
47
+ "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
48
+ },
49
+ },
50
+ ],
51
+ },
52
+ ],
53
+ },
54
+ ],
55
+ },
56
+ ],
57
+ },
58
+ {
59
+ "_name": "LiveStore:queries",
60
+ "children": [
61
+ {
62
+ "_name": "db:select * from todos",
63
+ "attributes": {
64
+ "sql.query": "select * from todos",
65
+ "sql.rowsCount": 0,
66
+ },
67
+ "children": [
68
+ {
69
+ "_name": "sql-in-memory-select",
70
+ "attributes": {
71
+ "sql.cached": false,
72
+ "sql.query": "select * from todos",
73
+ "sql.rowsCount": 0,
74
+ },
75
+ },
76
+ ],
77
+ },
78
+ {
79
+ "_name": "db:select * from todos",
80
+ "attributes": {
81
+ "sql.query": "select * from todos",
82
+ "sql.rowsCount": 1,
83
+ },
84
+ "children": [
85
+ {
86
+ "_name": "sql-in-memory-select",
87
+ "attributes": {
88
+ "sql.cached": false,
89
+ "sql.query": "select * from todos",
90
+ "sql.rowsCount": 1,
91
+ },
92
+ },
93
+ ],
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ }
99
+ `;
100
+
101
+ exports[`otel > with thunks 3`] = `
102
+ {
103
+ "_name": "test",
104
+ "children": [
105
+ {
106
+ "_name": "livestore.in-memory-db:execute",
107
+ "attributes": {
108
+ "sql.query": "
109
+ PRAGMA page_size=32768;
110
+ PRAGMA cache_size=10000;
111
+ PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
112
+ PRAGMA synchronous='OFF';
113
+ PRAGMA temp_store='MEMORY';
114
+ PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
115
+ ",
116
+ },
117
+ },
118
+ {
119
+ "_name": "LiveStore:mutations",
120
+ "children": [
121
+ {
122
+ "_name": "LiveStore:mutate",
123
+ "attributes": {
124
+ "livestore.mutateLabel": "mutate",
125
+ },
126
+ "children": [
127
+ {
128
+ "_name": "LiveStore:processWrites",
129
+ "attributes": {
130
+ "livestore.mutateLabel": "mutate",
131
+ },
132
+ "children": [
133
+ {
134
+ "_name": "LiveStore:mutateWithoutRefresh",
135
+ "attributes": {
136
+ "livestore.args": "{
137
+ "sql": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)"
138
+ }",
139
+ "livestore.mutation": "livestore.RawSql",
140
+ },
141
+ "children": [
142
+ {
143
+ "_name": "livestore.in-memory-db:execute",
144
+ "attributes": {
145
+ "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
146
+ },
147
+ },
148
+ ],
149
+ },
150
+ ],
151
+ },
152
+ ],
153
+ },
154
+ ],
155
+ },
156
+ {
157
+ "_name": "LiveStore:queries",
158
+ "children": [
159
+ {
160
+ "_name": "db:select * from todos where completed = 0",
161
+ "attributes": {
162
+ "sql.query": "select * from todos where completed = 0",
163
+ "sql.rowsCount": 0,
164
+ },
165
+ "children": [
166
+ {
167
+ "_name": "js:where-filter",
168
+ },
169
+ {
170
+ "_name": "sql-in-memory-select",
171
+ "attributes": {
172
+ "sql.cached": false,
173
+ "sql.query": "select * from todos where completed = 0",
174
+ "sql.rowsCount": 0,
175
+ },
176
+ },
177
+ ],
178
+ },
179
+ {
180
+ "_name": "db:select * from todos where completed = 0",
181
+ "attributes": {
182
+ "sql.query": "select * from todos where completed = 0",
183
+ "sql.rowsCount": 1,
184
+ },
185
+ "children": [
186
+ {
187
+ "_name": "sql-in-memory-select",
188
+ "attributes": {
189
+ "sql.cached": false,
190
+ "sql.query": "select * from todos where completed = 0",
191
+ "sql.rowsCount": 1,
192
+ },
193
+ },
194
+ ],
195
+ },
196
+ ],
197
+ },
198
+ ],
199
+ }
200
+ `;
201
+
202
+ exports[`otel > with thunks with query builder and without labels 3`] = `
203
+ {
204
+ "_name": "test",
205
+ "children": [
206
+ {
207
+ "_name": "livestore.in-memory-db:execute",
208
+ "attributes": {
209
+ "sql.query": "
210
+ PRAGMA page_size=32768;
211
+ PRAGMA cache_size=10000;
212
+ PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
213
+ PRAGMA synchronous='OFF';
214
+ PRAGMA temp_store='MEMORY';
215
+ PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
216
+ ",
217
+ },
218
+ },
219
+ {
220
+ "_name": "LiveStore:mutations",
221
+ "children": [
222
+ {
223
+ "_name": "LiveStore:mutate",
224
+ "attributes": {
225
+ "livestore.mutateLabel": "mutate",
226
+ },
227
+ "children": [
228
+ {
229
+ "_name": "LiveStore:processWrites",
230
+ "attributes": {
231
+ "livestore.mutateLabel": "mutate",
232
+ },
233
+ "children": [
234
+ {
235
+ "_name": "LiveStore:mutateWithoutRefresh",
236
+ "attributes": {
237
+ "livestore.args": "{
238
+ "sql": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)"
239
+ }",
240
+ "livestore.mutation": "livestore.RawSql",
241
+ },
242
+ "children": [
243
+ {
244
+ "_name": "livestore.in-memory-db:execute",
245
+ "attributes": {
246
+ "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
247
+ },
248
+ },
249
+ ],
250
+ },
251
+ ],
252
+ },
253
+ ],
254
+ },
255
+ ],
256
+ },
257
+ {
258
+ "_name": "LiveStore:queries",
259
+ "children": [
260
+ {
261
+ "_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
262
+ "attributes": {
263
+ "sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
264
+ "sql.rowsCount": 0,
265
+ },
266
+ "children": [
267
+ {
268
+ "_name": "js:() => ({ completed: false })",
269
+ },
270
+ {
271
+ "_name": "sql-in-memory-select",
272
+ "attributes": {
273
+ "sql.cached": false,
274
+ "sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
275
+ "sql.rowsCount": 0,
276
+ },
277
+ },
278
+ ],
279
+ },
280
+ {
281
+ "_name": "db:SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
282
+ "attributes": {
283
+ "sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
284
+ "sql.rowsCount": 1,
285
+ },
286
+ "children": [
287
+ {
288
+ "_name": "sql-in-memory-select",
289
+ "attributes": {
290
+ "sql.cached": false,
291
+ "sql.query": "SELECT * FROM 'todos' WHERE completed = ? LIMIT ?",
292
+ "sql.rowsCount": 1,
293
+ },
294
+ },
295
+ ],
296
+ },
297
+ ],
298
+ },
299
+ ],
300
+ }
301
+ `;
@@ -1,4 +1,4 @@
1
- import type { QueryInfo, QueryInfoNone } from '@livestore/common'
1
+ import type { QueryInfo } from '@livestore/common'
2
2
  import type * as otel from '@opentelemetry/api'
3
3
 
4
4
  import { type Atom, type GetAtom, ReactiveGraph, throwContextNotSetError, type Thunk } from '../reactive.js'
@@ -27,9 +27,13 @@ let queryIdCounter = 0
27
27
 
28
28
  export type LiveQueryAny = LiveQuery<any, QueryInfo>
29
29
 
30
- export interface LiveQuery<TResult, TQueryInfo extends QueryInfo = QueryInfoNone> {
30
+ export const TypeId = Symbol.for('LiveQuery')
31
+ export type TypeId = typeof TypeId
32
+
33
+ export interface LiveQuery<TResult, TQueryInfo extends QueryInfo = QueryInfo.None> {
31
34
  id: number
32
- _tag: 'computed' | 'sql' | 'graphql'
35
+ _tag: 'computed' | 'db' | 'graphql'
36
+ [TypeId]: TypeId
33
37
 
34
38
  /** This should only be used on a type-level and doesn't hold any value during runtime */
35
39
  '__result!': TResult
@@ -64,8 +68,9 @@ export abstract class LiveStoreQueryBase<TResult, TQueryInfo extends QueryInfo>
64
68
  implements LiveQuery<TResult, TQueryInfo>
65
69
  {
66
70
  '__result!'!: TResult
67
- id = queryIdCounter++
68
- abstract _tag: 'computed' | 'sql' | 'graphql'
71
+ id = queryIdCounter++;
72
+ [TypeId]: TypeId = TypeId
73
+ abstract _tag: 'computed' | 'db' | 'graphql'
69
74
 
70
75
  /** Human-readable label for the query for debugging */
71
76
  abstract label: string
@@ -1,4 +1,4 @@
1
- import type { QueryInfo, QueryInfoNone } from '@livestore/common'
1
+ import type { QueryInfo } from '@livestore/common'
2
2
  import * as otel from '@opentelemetry/api'
3
3
 
4
4
  import { globalReactivityGraph } from '../global-state.js'
@@ -8,7 +8,7 @@ import { getDurationMsFromSpan } from '../utils/otel.js'
8
8
  import type { GetAtomResult, LiveQuery, QueryContext, ReactivityGraph } from './base-class.js'
9
9
  import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
10
10
 
11
- export const computed = <TResult, TQueryInfo extends QueryInfo = QueryInfoNone>(
11
+ export const computed = <TResult, TQueryInfo extends QueryInfo = QueryInfo.None>(
12
12
  fn: (get: GetAtomResult) => TResult,
13
13
  options?: {
14
14
  label: string
@@ -16,14 +16,14 @@ export const computed = <TResult, TQueryInfo extends QueryInfo = QueryInfoNone>(
16
16
  queryInfo?: TQueryInfo
17
17
  },
18
18
  ): LiveQuery<TResult, TQueryInfo> =>
19
- new LiveStoreJSQuery<TResult, TQueryInfo>({
19
+ new LiveStoreComputedQuery<TResult, TQueryInfo>({
20
20
  fn,
21
21
  label: options?.label ?? fn.toString(),
22
22
  reactivityGraph: options?.reactivityGraph,
23
23
  queryInfo: options?.queryInfo,
24
24
  })
25
25
 
26
- export class LiveStoreJSQuery<TResult, TQueryInfo extends QueryInfo = QueryInfoNone> extends LiveStoreQueryBase<
26
+ export class LiveStoreComputedQuery<TResult, TQueryInfo extends QueryInfo = QueryInfo.None> extends LiveStoreQueryBase<
27
27
  TResult,
28
28
  TQueryInfo
29
29
  > {
@@ -38,31 +38,19 @@ export class LiveStoreJSQuery<TResult, TQueryInfo extends QueryInfo = QueryInfoN
38
38
 
39
39
  queryInfo: TQueryInfo
40
40
 
41
- /**
42
- * Currently only used for "nested destruction" of piped queries
43
- *
44
- * i.e. when doing something like `const q = querySQL(...).pipe(...)`
45
- * we need to also destory the SQL query when the JS query `q` is destroyed
46
- */
47
- private onDestroy: (() => void) | undefined
48
-
49
41
  constructor({
50
42
  fn,
51
43
  label,
52
- onDestroy,
53
44
  reactivityGraph,
54
45
  queryInfo,
55
46
  }: {
56
47
  label: string
57
48
  fn: (get: GetAtomResult) => TResult
58
- /** Currently only used for "nested destruction" of piped queries */
59
- onDestroy?: () => void
60
49
  reactivityGraph?: ReactivityGraph
61
50
  queryInfo?: TQueryInfo
62
51
  }) {
63
52
  super()
64
53
 
65
- this.onDestroy = onDestroy
66
54
  this.label = label
67
55
 
68
56
  this.reactivityGraph = reactivityGraph ?? globalReactivityGraph
@@ -86,23 +74,11 @@ export class LiveStoreJSQuery<TResult, TQueryInfo extends QueryInfo = QueryInfoN
86
74
 
87
75
  return res
88
76
  }),
89
- { label: queryLabel, meta: { liveStoreThunkType: 'jsResults' } },
77
+ { label: queryLabel, meta: { liveStoreThunkType: 'computedResults' } },
90
78
  )
91
79
  }
92
80
 
93
- // pipe = <U>(fn: (result: TResult, get: GetAtomResult) => U): LiveStoreJSQuery<U> =>
94
- // new LiveStoreJSQuery({
95
- // fn: (get) => {
96
- // const results = get(this.results$)
97
- // return fn(results, get)
98
- // },
99
- // label: `${this.label}:js`,
100
- // onDestroy: () => this.destroy(),
101
- // reactivityGraph: this.reactivityGraph,
102
- // })
103
-
104
81
  destroy = () => {
105
82
  this.reactivityGraph.destroyNode(this.results$)
106
- this.onDestroy?.()
107
83
  }
108
84
  }
@@ -0,0 +1,153 @@
1
+ import { Effect, Schema } from '@livestore/utils/effect'
2
+ import * as otel from '@opentelemetry/api'
3
+ import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
4
+ import { describe, expect, it } from 'vitest'
5
+
6
+ import { computed, queryDb, rawSqlMutation, sql } from '../index.js'
7
+ import { makeTodoMvc, tables } from '../utils/tests/fixture.js'
8
+ import { getSimplifiedRootSpan } from '../utils/tests/otel.js'
9
+
10
+ /*
11
+ TODO write tests for:
12
+
13
+ - sql queries without and with `map` (incl. callback and schemas)
14
+ - optional and explicit `queriedTables` argument
15
+ */
16
+
17
+ describe('otel', () => {
18
+ let cachedProvider: BasicTracerProvider | undefined
19
+
20
+ const makeQuery = Effect.gen(function* () {
21
+ const exporter = new InMemorySpanExporter()
22
+
23
+ const provider = cachedProvider ?? new BasicTracerProvider()
24
+ cachedProvider = provider
25
+ provider.addSpanProcessor(new SimpleSpanProcessor(exporter))
26
+ provider.register()
27
+
28
+ const otelTracer = otel.trace.getTracer('test')
29
+
30
+ const span = otelTracer.startSpan('test')
31
+ const otelContext = otel.trace.setSpan(otel.context.active(), span)
32
+
33
+ const { store } = yield* makeTodoMvc({ otelTracer, otelContext })
34
+
35
+ return {
36
+ store,
37
+ otelTracer,
38
+ exporter,
39
+ span,
40
+ provider,
41
+ }
42
+ })
43
+
44
+ it('otel', async () => {
45
+ const { exporter } = await Effect.gen(function* () {
46
+ const { store, exporter, span } = yield* makeQuery
47
+
48
+ const query$ = queryDb({
49
+ query: `select * from todos`,
50
+ schema: Schema.Array(tables.todos.schema),
51
+ queriedTables: new Set(['todos']),
52
+ })
53
+ expect(query$.run()).toMatchInlineSnapshot('[]')
54
+
55
+ store.mutate(rawSqlMutation({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
56
+
57
+ expect(query$.run()).toMatchInlineSnapshot(`
58
+ [
59
+ {
60
+ "completed": false,
61
+ "id": "t1",
62
+ "text": "buy milk",
63
+ },
64
+ ]
65
+ `)
66
+
67
+ query$.destroy()
68
+ span.end()
69
+
70
+ return { exporter }
71
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise)
72
+
73
+ expect(getSimplifiedRootSpan(exporter)).toMatchSnapshot()
74
+ })
75
+
76
+ it('with thunks', async () => {
77
+ const { exporter } = await Effect.gen(function* () {
78
+ const { store, exporter, span } = yield* makeQuery
79
+
80
+ const defaultTodo = { id: '', text: '', completed: false }
81
+
82
+ const filter = computed(() => `where completed = 0`, { label: 'where-filter' })
83
+ const query$ = queryDb(
84
+ (get) => ({
85
+ query: `select * from todos ${get(filter)}`,
86
+ schema: Schema.Array(tables.todos.schema).pipe(Schema.headOrElse(() => defaultTodo)),
87
+ }),
88
+ { label: 'all todos' },
89
+ )
90
+
91
+ expect(query$.run()).toMatchInlineSnapshot(`
92
+ {
93
+ "completed": false,
94
+ "id": "",
95
+ "text": "",
96
+ }
97
+ `)
98
+
99
+ store.mutate(rawSqlMutation({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
100
+
101
+ expect(query$.run()).toMatchInlineSnapshot(`
102
+ {
103
+ "completed": false,
104
+ "id": "t1",
105
+ "text": "buy milk",
106
+ }
107
+ `)
108
+
109
+ query$.destroy()
110
+ span.end()
111
+
112
+ return { exporter }
113
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise)
114
+
115
+ expect(getSimplifiedRootSpan(exporter)).toMatchSnapshot()
116
+ })
117
+
118
+ it('with thunks with query builder and without labels', async () => {
119
+ const { exporter } = await Effect.gen(function* () {
120
+ const { store, exporter, span } = yield* makeQuery
121
+
122
+ const defaultTodo = { id: '', text: '', completed: false }
123
+
124
+ const filter = computed(() => ({ completed: false }))
125
+ const query$ = queryDb((get) => tables.todos.query.where(get(filter)).first({ fallback: () => defaultTodo }))
126
+
127
+ expect(query$.run()).toMatchInlineSnapshot(`
128
+ {
129
+ "completed": false,
130
+ "id": "",
131
+ "text": "",
132
+ }
133
+ `)
134
+
135
+ store.mutate(rawSqlMutation({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
136
+
137
+ expect(query$.run()).toMatchInlineSnapshot(`
138
+ {
139
+ "completed": false,
140
+ "id": "t1",
141
+ "text": "buy milk",
142
+ }
143
+ `)
144
+
145
+ query$.destroy()
146
+ span.end()
147
+
148
+ return { exporter }
149
+ }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise)
150
+
151
+ expect(getSimplifiedRootSpan(exporter)).toMatchSnapshot()
152
+ })
153
+ })