@livestore/livestore 0.3.0-dev.4 → 0.3.0-dev.40

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 (170) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/QueryCache.d.ts.map +1 -1
  3. package/dist/SqliteDbWrapper.d.ts +60 -0
  4. package/dist/SqliteDbWrapper.d.ts.map +1 -0
  5. package/dist/{SynchronousDatabaseWrapper.js → SqliteDbWrapper.js} +69 -34
  6. package/dist/SqliteDbWrapper.js.map +1 -0
  7. package/dist/effect/LiveStore.d.ts +6 -34
  8. package/dist/effect/LiveStore.d.ts.map +1 -1
  9. package/dist/effect/LiveStore.js +10 -12
  10. package/dist/effect/LiveStore.js.map +1 -1
  11. package/dist/effect/mod.d.ts +3 -0
  12. package/dist/effect/mod.d.ts.map +1 -0
  13. package/dist/effect/mod.js +3 -0
  14. package/dist/effect/mod.js.map +1 -0
  15. package/dist/internal/mod.d.ts +3 -0
  16. package/dist/internal/mod.d.ts.map +1 -0
  17. package/dist/internal/mod.js +3 -0
  18. package/dist/internal/mod.js.map +1 -0
  19. package/dist/live-queries/base-class.d.ts +65 -27
  20. package/dist/live-queries/base-class.d.ts.map +1 -1
  21. package/dist/live-queries/base-class.js +54 -13
  22. package/dist/live-queries/base-class.js.map +1 -1
  23. package/dist/live-queries/client-document-get-query.d.ts +12 -0
  24. package/dist/live-queries/client-document-get-query.d.ts.map +1 -0
  25. package/dist/live-queries/client-document-get-query.js +18 -0
  26. package/dist/live-queries/client-document-get-query.js.map +1 -0
  27. package/dist/live-queries/computed.d.ts +12 -14
  28. package/dist/live-queries/computed.d.ts.map +1 -1
  29. package/dist/live-queries/computed.js +37 -15
  30. package/dist/live-queries/computed.js.map +1 -1
  31. package/dist/live-queries/db-query.d.ts +64 -0
  32. package/dist/live-queries/db-query.d.ts.map +1 -0
  33. package/dist/live-queries/{db.js → db-query.js} +83 -41
  34. package/dist/live-queries/db-query.js.map +1 -0
  35. package/dist/live-queries/db-query.test.d.ts +2 -0
  36. package/dist/live-queries/db-query.test.d.ts.map +1 -0
  37. package/dist/live-queries/db-query.test.js +133 -0
  38. package/dist/live-queries/db-query.test.js.map +1 -0
  39. package/dist/live-queries/mod.d.ts +5 -0
  40. package/dist/live-queries/mod.d.ts.map +1 -0
  41. package/dist/live-queries/mod.js +5 -0
  42. package/dist/live-queries/mod.js.map +1 -0
  43. package/dist/live-queries/signal.d.ts +20 -0
  44. package/dist/live-queries/signal.d.ts.map +1 -0
  45. package/dist/live-queries/signal.js +33 -0
  46. package/dist/live-queries/signal.js.map +1 -0
  47. package/dist/live-queries/signal.test.d.ts +2 -0
  48. package/dist/live-queries/signal.test.d.ts.map +1 -0
  49. package/dist/live-queries/signal.test.js +17 -0
  50. package/dist/live-queries/signal.test.js.map +1 -0
  51. package/dist/mod.d.ts +14 -0
  52. package/dist/mod.d.ts.map +1 -0
  53. package/dist/mod.js +13 -0
  54. package/dist/mod.js.map +1 -0
  55. package/dist/reactive.d.ts +23 -17
  56. package/dist/reactive.d.ts.map +1 -1
  57. package/dist/reactive.js +23 -19
  58. package/dist/reactive.js.map +1 -1
  59. package/dist/reactive.test.js +1 -1
  60. package/dist/reactive.test.js.map +1 -1
  61. package/dist/store/create-store.d.ts +70 -12
  62. package/dist/store/create-store.d.ts.map +1 -1
  63. package/dist/store/create-store.js +69 -19
  64. package/dist/store/create-store.js.map +1 -1
  65. package/dist/store/devtools.d.ts +5 -4
  66. package/dist/store/devtools.d.ts.map +1 -1
  67. package/dist/store/devtools.js +103 -47
  68. package/dist/store/devtools.js.map +1 -1
  69. package/dist/store/store-types.d.ts +32 -42
  70. package/dist/store/store-types.d.ts.map +1 -1
  71. package/dist/store/store-types.js +2 -5
  72. package/dist/store/store-types.js.map +1 -1
  73. package/dist/store/store.d.ts +104 -39
  74. package/dist/store/store.d.ts.map +1 -1
  75. package/dist/store/store.js +261 -214
  76. package/dist/store/store.js.map +1 -1
  77. package/dist/utils/data-structures.d.ts.map +1 -1
  78. package/dist/utils/dev.d.ts.map +1 -1
  79. package/dist/utils/dev.js +6 -1
  80. package/dist/utils/dev.js.map +1 -1
  81. package/dist/utils/function-string.d.ts +7 -0
  82. package/dist/utils/function-string.d.ts.map +1 -0
  83. package/dist/utils/function-string.js +9 -0
  84. package/dist/utils/function-string.js.map +1 -0
  85. package/dist/utils/stack-info.d.ts.map +1 -1
  86. package/dist/utils/stack-info.js +6 -1
  87. package/dist/utils/stack-info.js.map +1 -1
  88. package/dist/utils/stack-info.test.js +54 -1
  89. package/dist/utils/stack-info.test.js.map +1 -1
  90. package/dist/utils/tests/fixture.d.ts +59 -216
  91. package/dist/utils/tests/fixture.d.ts.map +1 -1
  92. package/dist/utils/tests/fixture.js +23 -18
  93. package/dist/utils/tests/fixture.js.map +1 -1
  94. package/dist/utils/tests/mod.d.ts +1 -0
  95. package/dist/utils/tests/mod.d.ts.map +1 -1
  96. package/dist/utils/tests/mod.js +1 -0
  97. package/dist/utils/tests/mod.js.map +1 -1
  98. package/dist/utils/tests/otel.d.ts.map +1 -1
  99. package/dist/utils/tests/otel.js +8 -3
  100. package/dist/utils/tests/otel.js.map +1 -1
  101. package/package.json +29 -26
  102. package/src/{SynchronousDatabaseWrapper.ts → SqliteDbWrapper.ts} +92 -42
  103. package/src/effect/LiveStore.ts +27 -64
  104. package/src/effect/{index.ts → mod.ts} +2 -3
  105. package/src/internal/mod.ts +2 -0
  106. package/src/live-queries/__snapshots__/{db.test.ts.snap → db-query.test.ts.snap} +220 -45
  107. package/src/live-queries/base-class.ts +152 -50
  108. package/src/live-queries/client-document-get-query.ts +52 -0
  109. package/src/live-queries/computed.ts +51 -33
  110. package/src/live-queries/db-query.test.ts +192 -0
  111. package/src/live-queries/{db.ts → db-query.ts} +140 -82
  112. package/src/live-queries/mod.ts +4 -0
  113. package/src/live-queries/signal.test.ts +25 -0
  114. package/src/live-queries/signal.ts +47 -0
  115. package/src/mod.ts +42 -0
  116. package/src/reactive.test.ts +1 -1
  117. package/src/reactive.ts +66 -43
  118. package/src/store/create-store.ts +187 -59
  119. package/src/store/devtools.ts +136 -54
  120. package/src/store/store-types.ts +31 -43
  121. package/src/store/store.ts +385 -309
  122. package/src/utils/dev.ts +6 -1
  123. package/src/utils/function-string.ts +12 -0
  124. package/src/utils/stack-info.test.ts +58 -1
  125. package/src/utils/stack-info.ts +6 -1
  126. package/src/utils/tests/fixture.ts +22 -31
  127. package/src/utils/tests/mod.ts +1 -0
  128. package/src/utils/tests/otel.ts +10 -3
  129. package/dist/SynchronousDatabaseWrapper.d.ts +0 -41
  130. package/dist/SynchronousDatabaseWrapper.d.ts.map +0 -1
  131. package/dist/SynchronousDatabaseWrapper.js.map +0 -1
  132. package/dist/effect/index.d.ts +0 -2
  133. package/dist/effect/index.d.ts.map +0 -1
  134. package/dist/effect/index.js +0 -2
  135. package/dist/effect/index.js.map +0 -1
  136. package/dist/global-state.d.ts +0 -14
  137. package/dist/global-state.d.ts.map +0 -1
  138. package/dist/global-state.js +0 -16
  139. package/dist/global-state.js.map +0 -1
  140. package/dist/index.d.ts +0 -20
  141. package/dist/index.d.ts.map +0 -1
  142. package/dist/index.js +0 -16
  143. package/dist/index.js.map +0 -1
  144. package/dist/live-queries/db.d.ts +0 -66
  145. package/dist/live-queries/db.d.ts.map +0 -1
  146. package/dist/live-queries/db.js.map +0 -1
  147. package/dist/live-queries/db.test.d.ts +0 -2
  148. package/dist/live-queries/db.test.d.ts.map +0 -1
  149. package/dist/live-queries/db.test.js +0 -118
  150. package/dist/live-queries/db.test.js.map +0 -1
  151. package/dist/live-queries/graphql.d.ts +0 -49
  152. package/dist/live-queries/graphql.d.ts.map +0 -1
  153. package/dist/live-queries/graphql.js +0 -122
  154. package/dist/live-queries/graphql.js.map +0 -1
  155. package/dist/row-query-utils.d.ts +0 -17
  156. package/dist/row-query-utils.d.ts.map +0 -1
  157. package/dist/row-query-utils.js +0 -31
  158. package/dist/row-query-utils.js.map +0 -1
  159. package/dist/utils/otel.d.ts +0 -4
  160. package/dist/utils/otel.d.ts.map +0 -1
  161. package/dist/utils/otel.js +0 -6
  162. package/dist/utils/otel.js.map +0 -1
  163. package/src/global-state.ts +0 -20
  164. package/src/index.ts +0 -66
  165. package/src/live-queries/db.test.ts +0 -154
  166. package/src/live-queries/graphql.ts +0 -219
  167. package/src/row-query-utils.ts +0 -66
  168. package/src/utils/otel.ts +0 -9
  169. package/tsconfig.json +0 -18
  170. package/vitest.config.js +0 -9
@@ -14,7 +14,6 @@ exports[`otel > otel 3`] = `
14
14
  "sql.query": "
15
15
  PRAGMA page_size=32768;
16
16
  PRAGMA cache_size=10000;
17
- PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
18
17
  PRAGMA synchronous='OFF';
19
18
  PRAGMA temp_store='MEMORY';
20
19
  PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
@@ -22,33 +21,33 @@ exports[`otel > otel 3`] = `
22
21
  },
23
22
  },
24
23
  {
25
- "_name": "LiveStore:createStore",
24
+ "_name": "client-session-sync-processor:pull",
25
+ "attributes": {
26
+ "code.stacktrace": "<STACKTRACE>",
27
+ "span.label": "⚠︎ Interrupted",
28
+ "status.interrupted": true,
29
+ },
26
30
  },
27
31
  {
28
32
  "_name": "LiveStore:sync",
29
33
  },
30
34
  {
31
- "_name": "LiveStore:mutations",
35
+ "_name": "LiveStore:commits",
32
36
  "children": [
33
37
  {
34
- "_name": "LiveStore:mutate",
38
+ "_name": "LiveStore:commit",
35
39
  "attributes": {
36
- "livestore.mutateLabel": "mutate",
40
+ "livestore.eventTags": [
41
+ "livestore.RawSql",
42
+ ],
43
+ "livestore.eventsCount": 1,
37
44
  },
38
45
  "children": [
39
46
  {
40
- "_name": "LiveStore:mutate:applyMutations",
47
+ "_name": "livestore.in-memory-db:execute",
41
48
  "attributes": {
42
- "livestore.mutateLabel": "mutate",
49
+ "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
43
50
  },
44
- "children": [
45
- {
46
- "_name": "livestore.in-memory-db:execute",
47
- "attributes": {
48
- "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
49
- },
50
- },
51
- ],
52
51
  },
53
52
  ],
54
53
  },
@@ -97,7 +96,179 @@ exports[`otel > otel 3`] = `
97
96
  }
98
97
  `;
99
98
 
99
+ exports[`otel > with thunks 1`] = `
100
+ {
101
+ "atoms": [
102
+ {
103
+ "_tag": "ref",
104
+ "id": "node-1",
105
+ "isDestroyed": false,
106
+ "isDirty": false,
107
+ "label": "tableRef:todos",
108
+ "meta": {
109
+ "liveStoreRefType": "table",
110
+ },
111
+ "previousResult": {
112
+ "_tag": "Some",
113
+ "value": "null",
114
+ },
115
+ "refreshes": 0,
116
+ "sub": [],
117
+ "super": [],
118
+ },
119
+ {
120
+ "_tag": "ref",
121
+ "id": "node-2",
122
+ "isDestroyed": false,
123
+ "isDirty": false,
124
+ "label": "tableRef:app",
125
+ "meta": {
126
+ "liveStoreRefType": "table",
127
+ },
128
+ "previousResult": {
129
+ "_tag": "Some",
130
+ "value": "null",
131
+ },
132
+ "refreshes": 0,
133
+ "sub": [],
134
+ "super": [],
135
+ },
136
+ ],
137
+ "deferredEffects": [],
138
+ "effects": [],
139
+ }
140
+ `;
141
+
100
142
  exports[`otel > with thunks 3`] = `
143
+ {
144
+ "atoms": [
145
+ {
146
+ "_tag": "ref",
147
+ "id": "node-1",
148
+ "isDestroyed": false,
149
+ "isDirty": false,
150
+ "label": "tableRef:todos",
151
+ "meta": {
152
+ "liveStoreRefType": "table",
153
+ },
154
+ "previousResult": {
155
+ "_tag": "Some",
156
+ "value": "null",
157
+ },
158
+ "refreshes": 0,
159
+ "sub": [],
160
+ "super": [],
161
+ },
162
+ {
163
+ "_tag": "ref",
164
+ "id": "node-2",
165
+ "isDestroyed": false,
166
+ "isDirty": false,
167
+ "label": "tableRef:app",
168
+ "meta": {
169
+ "liveStoreRefType": "table",
170
+ },
171
+ "previousResult": {
172
+ "_tag": "Some",
173
+ "value": "null",
174
+ },
175
+ "refreshes": 0,
176
+ "sub": [],
177
+ "super": [],
178
+ },
179
+ ],
180
+ "deferredEffects": [],
181
+ "effects": [],
182
+ }
183
+ `;
184
+
185
+ exports[`otel > with thunks 4`] = `
186
+ {
187
+ "atoms": [
188
+ {
189
+ "_tag": "ref",
190
+ "id": "node-1",
191
+ "isDestroyed": false,
192
+ "isDirty": false,
193
+ "label": "tableRef:todos",
194
+ "meta": {
195
+ "liveStoreRefType": "table",
196
+ },
197
+ "previousResult": {
198
+ "_tag": "Some",
199
+ "value": "null",
200
+ },
201
+ "refreshes": 1,
202
+ "sub": [],
203
+ "super": [],
204
+ },
205
+ {
206
+ "_tag": "ref",
207
+ "id": "node-2",
208
+ "isDestroyed": false,
209
+ "isDirty": false,
210
+ "label": "tableRef:app",
211
+ "meta": {
212
+ "liveStoreRefType": "table",
213
+ },
214
+ "previousResult": {
215
+ "_tag": "Some",
216
+ "value": "null",
217
+ },
218
+ "refreshes": 0,
219
+ "sub": [],
220
+ "super": [],
221
+ },
222
+ ],
223
+ "deferredEffects": [],
224
+ "effects": [],
225
+ }
226
+ `;
227
+
228
+ exports[`otel > with thunks 6`] = `
229
+ {
230
+ "atoms": [
231
+ {
232
+ "_tag": "ref",
233
+ "id": "node-1",
234
+ "isDestroyed": false,
235
+ "isDirty": false,
236
+ "label": "tableRef:todos",
237
+ "meta": {
238
+ "liveStoreRefType": "table",
239
+ },
240
+ "previousResult": {
241
+ "_tag": "Some",
242
+ "value": "null",
243
+ },
244
+ "refreshes": 1,
245
+ "sub": [],
246
+ "super": [],
247
+ },
248
+ {
249
+ "_tag": "ref",
250
+ "id": "node-2",
251
+ "isDestroyed": false,
252
+ "isDirty": false,
253
+ "label": "tableRef:app",
254
+ "meta": {
255
+ "liveStoreRefType": "table",
256
+ },
257
+ "previousResult": {
258
+ "_tag": "Some",
259
+ "value": "null",
260
+ },
261
+ "refreshes": 0,
262
+ "sub": [],
263
+ "super": [],
264
+ },
265
+ ],
266
+ "deferredEffects": [],
267
+ "effects": [],
268
+ }
269
+ `;
270
+
271
+ exports[`otel > with thunks 7`] = `
101
272
  {
102
273
  "_name": "createStore",
103
274
  "attributes": {
@@ -111,7 +282,6 @@ exports[`otel > with thunks 3`] = `
111
282
  "sql.query": "
112
283
  PRAGMA page_size=32768;
113
284
  PRAGMA cache_size=10000;
114
- PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
115
285
  PRAGMA synchronous='OFF';
116
286
  PRAGMA temp_store='MEMORY';
117
287
  PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
@@ -119,33 +289,33 @@ exports[`otel > with thunks 3`] = `
119
289
  },
120
290
  },
121
291
  {
122
- "_name": "LiveStore:createStore",
292
+ "_name": "client-session-sync-processor:pull",
293
+ "attributes": {
294
+ "code.stacktrace": "<STACKTRACE>",
295
+ "span.label": "⚠︎ Interrupted",
296
+ "status.interrupted": true,
297
+ },
123
298
  },
124
299
  {
125
300
  "_name": "LiveStore:sync",
126
301
  },
127
302
  {
128
- "_name": "LiveStore:mutations",
303
+ "_name": "LiveStore:commits",
129
304
  "children": [
130
305
  {
131
- "_name": "LiveStore:mutate",
306
+ "_name": "LiveStore:commit",
132
307
  "attributes": {
133
- "livestore.mutateLabel": "mutate",
308
+ "livestore.eventTags": [
309
+ "livestore.RawSql",
310
+ ],
311
+ "livestore.eventsCount": 1,
134
312
  },
135
313
  "children": [
136
314
  {
137
- "_name": "LiveStore:mutate:applyMutations",
315
+ "_name": "livestore.in-memory-db:execute",
138
316
  "attributes": {
139
- "livestore.mutateLabel": "mutate",
317
+ "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
140
318
  },
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
319
  },
150
320
  ],
151
321
  },
@@ -181,6 +351,9 @@ exports[`otel > with thunks 3`] = `
181
351
  "sql.rowsCount": 1,
182
352
  },
183
353
  "children": [
354
+ {
355
+ "_name": "js:where-filter",
356
+ },
184
357
  {
185
358
  "_name": "sql-in-memory-select",
186
359
  "attributes": {
@@ -211,7 +384,6 @@ exports[`otel > with thunks with query builder and without labels 3`] = `
211
384
  "sql.query": "
212
385
  PRAGMA page_size=32768;
213
386
  PRAGMA cache_size=10000;
214
- PRAGMA journal_mode='MEMORY'; -- we don't flush to disk before committing a write
215
387
  PRAGMA synchronous='OFF';
216
388
  PRAGMA temp_store='MEMORY';
217
389
  PRAGMA foreign_keys='ON'; -- we want foreign key constraints to be enforced
@@ -219,33 +391,33 @@ exports[`otel > with thunks with query builder and without labels 3`] = `
219
391
  },
220
392
  },
221
393
  {
222
- "_name": "LiveStore:createStore",
394
+ "_name": "client-session-sync-processor:pull",
395
+ "attributes": {
396
+ "code.stacktrace": "<STACKTRACE>",
397
+ "span.label": "⚠︎ Interrupted",
398
+ "status.interrupted": true,
399
+ },
223
400
  },
224
401
  {
225
402
  "_name": "LiveStore:sync",
226
403
  },
227
404
  {
228
- "_name": "LiveStore:mutations",
405
+ "_name": "LiveStore:commits",
229
406
  "children": [
230
407
  {
231
- "_name": "LiveStore:mutate",
408
+ "_name": "LiveStore:commit",
232
409
  "attributes": {
233
- "livestore.mutateLabel": "mutate",
410
+ "livestore.eventTags": [
411
+ "livestore.RawSql",
412
+ ],
413
+ "livestore.eventsCount": 1,
234
414
  },
235
415
  "children": [
236
416
  {
237
- "_name": "LiveStore:mutate:applyMutations",
417
+ "_name": "livestore.in-memory-db:execute",
238
418
  "attributes": {
239
- "livestore.mutateLabel": "mutate",
419
+ "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
240
420
  },
241
- "children": [
242
- {
243
- "_name": "livestore.in-memory-db:execute",
244
- "attributes": {
245
- "sql.query": "INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)",
246
- },
247
- },
248
- ],
249
421
  },
250
422
  ],
251
423
  },
@@ -281,6 +453,9 @@ exports[`otel > with thunks with query builder and without labels 3`] = `
281
453
  "sql.rowsCount": 1,
282
454
  },
283
455
  "children": [
456
+ {
457
+ "_name": "js:() => ({ completed: false })",
458
+ },
284
459
  {
285
460
  "_name": "sql-in-memory-select",
286
461
  "attributes": {
@@ -1,72 +1,119 @@
1
- import type { QueryInfo } from '@livestore/common'
1
+ import { isNotNil } from '@livestore/utils'
2
2
  import type * as otel from '@opentelemetry/api'
3
3
 
4
- import { type Atom, type GetAtom, ReactiveGraph, throwContextNotSetError, type Thunk } from '../reactive.js'
4
+ import * as RG from '../reactive.js'
5
5
  import type { Store } from '../store/store.js'
6
6
  import type { QueryDebugInfo, RefreshReason } from '../store/store-types.js'
7
7
  import type { StackInfo } from '../utils/stack-info.js'
8
8
 
9
- export type ReactivityGraph = ReactiveGraph<RefreshReason, QueryDebugInfo, QueryContext>
9
+ export type ReactivityGraph = RG.ReactiveGraph<RefreshReason, QueryDebugInfo, ReactivityGraphContext>
10
10
 
11
11
  export const makeReactivityGraph = (): ReactivityGraph =>
12
- new ReactiveGraph<RefreshReason, QueryDebugInfo, QueryContext>()
12
+ new RG.ReactiveGraph<RefreshReason, QueryDebugInfo, ReactivityGraphContext>()
13
13
 
14
- export type QueryContext = {
14
+ export type ReactivityGraphContext = {
15
15
  store: Store
16
+ /** Maps from the hash of the query definition to the RcRef of the query */
17
+ defRcMap: Map<string, RcRef<LiveQuery.Any | ISignal<any>>>
18
+ /** Back-reference to the reactivity graph for convenience */
19
+ reactivityGraph: WeakRef<ReactivityGraph>
16
20
  otelTracer: otel.Tracer
17
21
  rootOtelContext: otel.Context
18
22
  effectsWrapper: (run: () => void) => void
19
23
  }
20
24
 
21
- export type UnsubscribeQuery = () => void
22
-
23
- export type GetResult<TQuery extends LiveQueryAny> =
24
- TQuery extends LiveQuery<infer TResult, infer _1> ? TResult : unknown
25
+ export type GetResult<TQuery extends LiveQueryDef.Any | LiveQuery.Any> =
26
+ TQuery extends LiveQuery<infer TResult> ? TResult : TQuery extends LiveQueryDef<infer TResult> ? TResult : unknown
25
27
 
26
28
  let queryIdCounter = 0
27
29
 
28
- export type LiveQueryAny = LiveQuery<any, QueryInfo>
30
+ export interface SignalDef<T> {
31
+ _tag: 'signal-def'
32
+ defaultValue: T
33
+ make: (ctx: ReactivityGraphContext) => RcRef<ISignal<T>>
34
+ }
35
+
36
+ export interface ISignal<T> {
37
+ _tag: 'signal'
38
+ reactivityGraph: ReactivityGraph
39
+ ref: RG.Ref<T, ReactivityGraphContext, RefreshReason>
40
+ set: (value: T) => void
41
+ get: () => T
42
+ destroy: () => void
43
+ }
29
44
 
30
45
  export const TypeId = Symbol.for('LiveQuery')
31
46
  export type TypeId = typeof TypeId
32
47
 
33
- export interface LiveQuery<TResult, TQueryInfo extends QueryInfo = QueryInfo.None> {
48
+ export interface RcRef<T> {
49
+ rc: number
50
+ value: T
51
+ deref: () => void
52
+ }
53
+
54
+ export type DepKey = string | number | ReadonlyArray<string | number | undefined | null>
55
+
56
+ export const depsToString = (deps: DepKey): string => {
57
+ if (typeof deps === 'string' || typeof deps === 'number') {
58
+ return deps.toString()
59
+ }
60
+ return deps.filter(isNotNil).join(',')
61
+ }
62
+
63
+ export interface LiveQueryDef<TResult> {
64
+ _tag: 'def'
65
+ /** Creates a new LiveQuery instance bound to a specific store/reactivityGraph */
66
+ make: (ctx: ReactivityGraphContext, otelContext?: otel.Context) => RcRef<LiveQuery<TResult>>
67
+ label: string
68
+ hash: string
69
+ }
70
+
71
+ export namespace LiveQueryDef {
72
+ export type Any = LiveQueryDef<any>
73
+ }
74
+
75
+ /**
76
+ * A LiveQuery is stateful
77
+ */
78
+ export interface LiveQuery<TResult> {
34
79
  id: number
35
80
  _tag: 'computed' | 'db' | 'graphql'
36
81
  [TypeId]: TypeId
37
82
 
83
+ // reactivityGraph: ReactivityGraph
84
+
38
85
  /** This should only be used on a type-level and doesn't hold any value during runtime */
39
86
  '__result!': TResult
40
87
 
41
88
  /** A reactive thunk representing the query results */
42
- results$: Thunk<TResult, QueryContext, RefreshReason>
89
+ results$: RG.Thunk<TResult, ReactivityGraphContext, RefreshReason>
43
90
 
44
91
  label: string
45
92
 
46
- run: (otelContext?: otel.Context, debugRefreshReason?: RefreshReason) => TResult
93
+ run: (args: { otelContext?: otel.Context; debugRefreshReason?: RefreshReason }) => TResult
47
94
 
48
- runAndDestroy: (otelContext?: otel.Context, debugRefreshReason?: RefreshReason) => TResult
95
+ destroy: () => void
96
+ isDestroyed: boolean
49
97
 
50
- destroy(): void
51
-
52
- subscribe(
53
- onNewValue: (value: TResult) => void,
54
- onUnsubsubscribe?: () => void,
55
- options?: { label?: string; otelContext?: otel.Context },
56
- ): () => void
98
+ // subscribe(
99
+ // onNewValue: (value: TResult) => void,
100
+ // onUnsubsubscribe?: () => void,
101
+ // options?: { label?: string; otelContext?: otel.Context },
102
+ // ): () => void
57
103
 
58
104
  activeSubscriptions: Set<StackInfo>
59
105
 
60
- queryInfo: TQueryInfo
61
-
62
106
  runs: number
63
107
 
64
108
  executionTimes: number[]
109
+ def: LiveQueryDef<TResult>
65
110
  }
66
111
 
67
- export abstract class LiveStoreQueryBase<TResult, TQueryInfo extends QueryInfo>
68
- implements LiveQuery<TResult, TQueryInfo>
69
- {
112
+ export namespace LiveQuery {
113
+ export type Any = LiveQuery<any>
114
+ }
115
+
116
+ export abstract class LiveStoreQueryBase<TResult> implements LiveQuery<TResult> {
70
117
  '__result!'!: TResult
71
118
  id = queryIdCounter++;
72
119
  [TypeId]: TypeId = TypeId
@@ -75,13 +122,13 @@ export abstract class LiveStoreQueryBase<TResult, TQueryInfo extends QueryInfo>
75
122
  /** Human-readable label for the query for debugging */
76
123
  abstract label: string
77
124
 
78
- abstract results$: Thunk<TResult, QueryContext, RefreshReason>
125
+ abstract def: LiveQueryDef<TResult>
79
126
 
80
- activeSubscriptions: Set<StackInfo> = new Set()
127
+ abstract results$: RG.Thunk<TResult, ReactivityGraphContext, RefreshReason>
81
128
 
82
- protected abstract reactivityGraph: ReactivityGraph
129
+ activeSubscriptions: Set<StackInfo> = new Set()
83
130
 
84
- abstract queryInfo: TQueryInfo
131
+ abstract readonly reactivityGraph: ReactivityGraph
85
132
 
86
133
  get runs() {
87
134
  return this.results$.recomputations
@@ -89,33 +136,88 @@ export abstract class LiveStoreQueryBase<TResult, TQueryInfo extends QueryInfo>
89
136
 
90
137
  executionTimes: number[] = []
91
138
 
139
+ // TODO double check if this is needed
140
+ isDestroyed = false
92
141
  abstract destroy: () => void
93
142
 
94
- run = (otelContext?: otel.Context, debugRefreshReason?: RefreshReason): TResult =>
95
- this.results$.computeResult(otelContext, debugRefreshReason)
96
-
97
- runAndDestroy = (otelContext?: otel.Context, debugRefreshReason?: RefreshReason): TResult => {
98
- const result = this.run(otelContext, debugRefreshReason)
99
- this.destroy()
100
- return result
143
+ run = (args: { otelContext?: otel.Context; debugRefreshReason?: RefreshReason }): TResult => {
144
+ return this.results$.computeResult(args.otelContext, args.debugRefreshReason)
101
145
  }
102
146
 
103
- subscribe = (
104
- onNewValue: (value: TResult) => void,
105
- onUnsubsubscribe?: () => void,
106
- options?: { label?: string; otelContext?: otel.Context } | undefined,
107
- ): (() => void) =>
108
- this.reactivityGraph.context?.store.subscribe(this, onNewValue, onUnsubsubscribe, options) ??
109
- throwContextNotSetError(this.reactivityGraph)
110
- }
147
+ protected dependencyQueriesRef: DependencyQueriesRef = new Set()
111
148
 
112
- export type GetAtomResult = <T>(atom: Atom<T, any, RefreshReason> | LiveQuery<T, any>) => T
149
+ // subscribe = (
150
+ // onNewValue: (value: TResult) => void,
151
+ // onUnsubsubscribe?: () => void,
152
+ // options?: { label?: string; otelContext?: otel.Context } | undefined,
153
+ // ): (() => void) =>
154
+ // this.reactivityGraph.context?.store.subscribe(this, onNewValue, onUnsubsubscribe, options) ??
155
+ // RG.throwContextNotSetError(this.reactivityGraph)
156
+ }
113
157
 
114
- export const makeGetAtomResult = (get: GetAtom, otelContext: otel.Context) => {
115
- const getAtom: GetAtomResult = (atom) => {
116
- if (atom._tag === 'thunk' || atom._tag === 'ref') return get(atom, otelContext)
117
- return get(atom.results$, otelContext)
158
+ export type GetAtomResult = <T>(
159
+ atom: RG.Atom<T, any, RefreshReason> | LiveQueryDef<T> | LiveQuery<T> | ISignal<T> | SignalDef<T>,
160
+ otelContext?: otel.Context | undefined,
161
+ debugRefreshReason?: RefreshReason | undefined,
162
+ ) => T
163
+
164
+ export type DependencyQueriesRef = Set<RcRef<LiveQuery.Any | ISignal<any>>>
165
+
166
+ export const makeGetAtomResult = (
167
+ get: RG.GetAtom,
168
+ ctx: ReactivityGraphContext,
169
+ otelContext: otel.Context,
170
+ dependencyQueriesRef: DependencyQueriesRef,
171
+ ) => {
172
+ // NOTE we're using the `otelContext` from `makeGetAtomResult` here, not the `otelContext` from `getAtom`
173
+ const getAtom: GetAtomResult = (atom, _otelContext, debugRefreshReason) => {
174
+ // ReactivityGraph atoms case
175
+ if (atom._tag === 'thunk' || atom._tag === 'ref') return get(atom, otelContext, debugRefreshReason)
176
+
177
+ // def case
178
+ if (atom._tag === 'def' || atom._tag === 'signal-def') {
179
+ const query = atom.make(ctx)
180
+ dependencyQueriesRef.add(query)
181
+ // TODO deref the query on destroy
182
+ return getAtom(query.value, _otelContext, debugRefreshReason)
183
+ }
184
+
185
+ // Signal case
186
+ if (atom._tag === 'signal') return get(atom.ref, otelContext, debugRefreshReason)
187
+
188
+ // LiveQuery case
189
+ return get(atom.results$, otelContext, debugRefreshReason)
118
190
  }
119
191
 
120
192
  return getAtom
121
193
  }
194
+
195
+ export const withRCMap = <T extends LiveQuery.Any | ISignal<any>>(
196
+ id: string,
197
+ make: (ctx: ReactivityGraphContext, otelContext?: otel.Context) => T,
198
+ ): ((ctx: ReactivityGraphContext, otelContext?: otel.Context) => RcRef<T>) => {
199
+ return (ctx, otelContext) => {
200
+ let item = ctx.defRcMap.get(id)
201
+ if (item) {
202
+ item.rc++
203
+ return item as RcRef<T>
204
+ }
205
+
206
+ const query$ = make(ctx, otelContext)
207
+
208
+ item = {
209
+ rc: 1,
210
+ value: query$,
211
+ deref: () => {
212
+ item!.rc--
213
+ if (item!.rc === 0) {
214
+ item!.value.destroy()
215
+ ctx.defRcMap.delete(id)
216
+ }
217
+ },
218
+ }
219
+ ctx.defRcMap.set(id, item)
220
+
221
+ return item as RcRef<T>
222
+ }
223
+ }
@@ -0,0 +1,52 @@
1
+ import type { PreparedBindValues } from '@livestore/common'
2
+ import { SessionIdSymbol } from '@livestore/common'
3
+ import { State } from '@livestore/common/schema'
4
+ import { shouldNeverHappen } from '@livestore/utils'
5
+ import type * as otel from '@opentelemetry/api'
6
+
7
+ import type { ReactivityGraphContext } from './base-class.js'
8
+
9
+ export const rowQueryLabel = (
10
+ table: State.SQLite.ClientDocumentTableDef.Any,
11
+ id: string | SessionIdSymbol | undefined,
12
+ ) => `${table.sqliteDef.name}.get:${id === undefined ? table.default.id : id === SessionIdSymbol ? 'sessionId' : id}`
13
+
14
+ export const makeExecBeforeFirstRun =
15
+ ({
16
+ id,
17
+ explicitDefaultValues,
18
+ table,
19
+ otelContext: otelContext_,
20
+ }: {
21
+ id?: string | SessionIdSymbol
22
+ explicitDefaultValues?: any
23
+ table: State.SQLite.TableDefBase
24
+ otelContext: otel.Context | undefined
25
+ }) =>
26
+ ({ store }: ReactivityGraphContext) => {
27
+ if (State.SQLite.tableIsClientDocumentTable(table) === false) {
28
+ return shouldNeverHappen(
29
+ `Cannot insert row for table "${table.sqliteDef.name}" which does not have 'deriveEvents: true' set`,
30
+ )
31
+ }
32
+
33
+ const otelContext = otelContext_ ?? store.otel.queriesSpanContext
34
+
35
+ const idVal = id === SessionIdSymbol ? store.sessionId : id!
36
+ const rowExists =
37
+ store.sqliteDbWrapper.select(
38
+ `SELECT 1 FROM '${table.sqliteDef.name}' WHERE id = ?`,
39
+ [idVal] as any as PreparedBindValues,
40
+ { otelContext },
41
+ ).length === 1
42
+
43
+ if (rowExists) return
44
+
45
+ // It's important that we only commit and don't refresh here, as this function might be called during a render
46
+ // and otherwise we might end up in a "reactive loop"
47
+
48
+ store.commit(
49
+ { otelContext, skipRefresh: true, label: `${table.sqliteDef.name}.set:${idVal}` },
50
+ table.set(explicitDefaultValues, idVal as TODO),
51
+ )
52
+ }