@livestore/livestore 0.4.0-dev.9 → 0.5.0-dev.0

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