@livestore/livestore 0.3.0-dev.27 → 0.3.0-dev.29

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 (70) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/SqliteDbWrapper.d.ts.map +1 -1
  3. package/dist/SqliteDbWrapper.js +4 -1
  4. package/dist/SqliteDbWrapper.js.map +1 -1
  5. package/dist/live-queries/base-class.d.ts +8 -12
  6. package/dist/live-queries/base-class.d.ts.map +1 -1
  7. package/dist/live-queries/base-class.js.map +1 -1
  8. package/dist/live-queries/client-document-get-query.d.ts +12 -0
  9. package/dist/live-queries/client-document-get-query.d.ts.map +1 -0
  10. package/dist/live-queries/client-document-get-query.js +18 -0
  11. package/dist/live-queries/client-document-get-query.js.map +1 -0
  12. package/dist/live-queries/computed.d.ts +4 -8
  13. package/dist/live-queries/computed.d.ts.map +1 -1
  14. package/dist/live-queries/computed.js +1 -6
  15. package/dist/live-queries/computed.js.map +1 -1
  16. package/dist/live-queries/db-query.d.ts +13 -18
  17. package/dist/live-queries/db-query.d.ts.map +1 -1
  18. package/dist/live-queries/db-query.js +34 -34
  19. package/dist/live-queries/db-query.js.map +1 -1
  20. package/dist/live-queries/db-query.test.js +32 -23
  21. package/dist/live-queries/db-query.test.js.map +1 -1
  22. package/dist/live-queries/make-ref.d.ts.map +1 -1
  23. package/dist/live-queries/make-ref.js +1 -0
  24. package/dist/live-queries/make-ref.js.map +1 -1
  25. package/dist/mod.d.ts +0 -1
  26. package/dist/mod.d.ts.map +1 -1
  27. package/dist/mod.js +0 -1
  28. package/dist/mod.js.map +1 -1
  29. package/dist/reactive.d.ts.map +1 -1
  30. package/dist/reactive.js +1 -1
  31. package/dist/reactive.js.map +1 -1
  32. package/dist/store/create-store.js +2 -2
  33. package/dist/store/create-store.js.map +1 -1
  34. package/dist/store/store-types.d.ts +5 -5
  35. package/dist/store/store-types.d.ts.map +1 -1
  36. package/dist/store/store.d.ts +27 -26
  37. package/dist/store/store.d.ts.map +1 -1
  38. package/dist/store/store.js +74 -69
  39. package/dist/store/store.js.map +1 -1
  40. package/dist/utils/stack-info.test.js +6 -6
  41. package/dist/utils/tests/fixture.d.ts +54 -207
  42. package/dist/utils/tests/fixture.d.ts.map +1 -1
  43. package/dist/utils/tests/fixture.js +20 -13
  44. package/dist/utils/tests/fixture.js.map +1 -1
  45. package/dist/utils/tests/otel.d.ts.map +1 -1
  46. package/dist/utils/tests/otel.js +8 -3
  47. package/dist/utils/tests/otel.js.map +1 -1
  48. package/package.json +7 -7
  49. package/src/SqliteDbWrapper.ts +4 -1
  50. package/src/live-queries/__snapshots__/db-query.test.ts.snap +9 -9
  51. package/src/live-queries/base-class.ts +8 -25
  52. package/src/live-queries/client-document-get-query.ts +52 -0
  53. package/src/live-queries/computed.ts +4 -18
  54. package/src/live-queries/db-query.test.ts +38 -24
  55. package/src/live-queries/db-query.ts +60 -66
  56. package/src/live-queries/make-ref.ts +2 -0
  57. package/src/mod.ts +0 -2
  58. package/src/reactive.ts +1 -1
  59. package/src/store/create-store.ts +2 -2
  60. package/src/store/store-types.ts +5 -5
  61. package/src/store/store.ts +97 -91
  62. package/src/utils/stack-info.test.ts +6 -6
  63. package/src/utils/tests/fixture.ts +21 -25
  64. package/src/utils/tests/otel.ts +10 -3
  65. package/tmp/pack.tgz +0 -0
  66. package/dist/row-query-utils.d.ts +0 -17
  67. package/dist/row-query-utils.d.ts.map +0 -1
  68. package/dist/row-query-utils.js +0 -34
  69. package/dist/row-query-utils.js.map +0 -1
  70. package/src/row-query-utils.ts +0 -76
@@ -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
+ }
@@ -1,4 +1,3 @@
1
- import type { QueryInfo } from '@livestore/common'
2
1
  import { getDurationMsFromSpan } from '@livestore/common'
3
2
  import * as otel from '@opentelemetry/api'
4
3
 
@@ -8,29 +7,25 @@ import { isValidFunctionString } from '../utils/function-string.js'
8
7
  import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
9
8
  import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
10
9
 
11
- export const computed = <TResult, TQueryInfo extends QueryInfo = QueryInfo.None>(
10
+ export const computed = <TResult>(
12
11
  fn: (get: GetAtomResult) => TResult,
13
12
  options?: {
14
13
  label?: string
15
- queryInfo?: TQueryInfo
16
14
  deps?: DepKey
17
15
  },
18
- ): LiveQueryDef<TResult, TQueryInfo> => {
16
+ ): LiveQueryDef<TResult> => {
19
17
  const hash = options?.deps ? depsToString(options.deps) : fn.toString()
20
18
  if (isValidFunctionString(hash)._tag === 'invalid') {
21
19
  throw new Error(`On Expo/React Native, computed queries must provide a \`deps\` option`)
22
20
  }
23
21
 
24
- const queryInfo = options?.queryInfo ?? ({ _tag: 'None' } as TQueryInfo)
25
-
26
22
  return {
27
23
  _tag: 'def',
28
24
  make: withRCMap(hash, (ctx, _otelContext) => {
29
25
  // TODO onDestroy
30
- return new LiveStoreComputedQuery<TResult, TQueryInfo>({
26
+ return new LiveStoreComputedQuery<TResult>({
31
27
  fn,
32
28
  label: options?.label ?? fn.toString(),
33
- queryInfo: options?.queryInfo,
34
29
  reactivityGraph: ctx.reactivityGraph.deref()!,
35
30
  })
36
31
  }),
@@ -39,14 +34,10 @@ export const computed = <TResult, TQueryInfo extends QueryInfo = QueryInfo.None>
39
34
  // TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
40
35
  // NOTE `fn.toString()` doesn't work in Expo as it always produces `[native code]`
41
36
  hash,
42
- queryInfo,
43
37
  }
44
38
  }
45
39
 
46
- export class LiveStoreComputedQuery<TResult, TQueryInfo extends QueryInfo = QueryInfo.None> extends LiveStoreQueryBase<
47
- TResult,
48
- TQueryInfo
49
- > {
40
+ export class LiveStoreComputedQuery<TResult> extends LiveStoreQueryBase<TResult> {
50
41
  _tag: 'computed' = 'computed'
51
42
 
52
43
  /** A reactive thunk representing the query results */
@@ -56,24 +47,19 @@ export class LiveStoreComputedQuery<TResult, TQueryInfo extends QueryInfo = Quer
56
47
 
57
48
  reactivityGraph: ReactivityGraph
58
49
 
59
- queryInfo: TQueryInfo
60
-
61
50
  constructor({
62
51
  fn,
63
52
  label,
64
53
  reactivityGraph,
65
- queryInfo,
66
54
  }: {
67
55
  label: string
68
56
  fn: (get: GetAtomResult) => TResult
69
57
  reactivityGraph: ReactivityGraph
70
- queryInfo?: TQueryInfo
71
58
  }) {
72
59
  super()
73
60
 
74
61
  this.label = label
75
62
  this.reactivityGraph = reactivityGraph
76
- this.queryInfo = queryInfo ?? ({ _tag: 'None' } as TQueryInfo)
77
63
 
78
64
  const queryLabel = `${label}:results`
79
65
 
@@ -1,5 +1,5 @@
1
1
  import { sql } from '@livestore/common'
2
- import { rawSqlMutation } from '@livestore/common/schema'
2
+ import { rawSqlEvent } from '@livestore/common/schema'
3
3
  import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
4
4
  import { Vitest } from '@livestore/utils/node-vitest'
5
5
  import * as otel from '@opentelemetry/api'
@@ -20,8 +20,6 @@ TODO write tests for:
20
20
  */
21
21
 
22
22
  Vitest.describe('otel', () => {
23
- let cachedProvider: BasicTracerProvider | undefined
24
-
25
23
  const mapAttributes = (attributes: otel.Attributes) => {
26
24
  return ReadonlyRecord.map(attributes, (val, key) => {
27
25
  if (key === 'code.stacktrace') {
@@ -36,13 +34,11 @@ Vitest.describe('otel', () => {
36
34
 
37
35
  RG.__resetIds()
38
36
 
39
- // const provider = cachedProvider ?? new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] })
40
- const provider = cachedProvider ?? new BasicTracerProvider()
41
- cachedProvider = provider
42
- provider.addSpanProcessor(new SimpleSpanProcessor(exporter))
43
- provider.register()
37
+ const provider = new BasicTracerProvider({
38
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
39
+ })
44
40
 
45
- const otelTracer = otel.trace.getTracer('test')
41
+ const otelTracer = provider.getTracer('test')
46
42
 
47
43
  const span = otelTracer.startSpan('test-root')
48
44
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
@@ -60,16 +56,16 @@ Vitest.describe('otel', () => {
60
56
 
61
57
  Vitest.scopedLive('otel', () =>
62
58
  Effect.gen(function* () {
63
- const { store, exporter, span } = yield* makeQuery
59
+ const { store, exporter, span, provider } = yield* makeQuery
64
60
 
65
61
  const query$ = queryDb({
66
62
  query: `select * from todos`,
67
- schema: Schema.Array(tables.todos.schema),
63
+ schema: Schema.Array(tables.todos.rowSchema),
68
64
  queriedTables: new Set(['todos']),
69
65
  })
70
66
  expect(store.query(query$)).toMatchInlineSnapshot('[]')
71
67
 
72
- store.commit(rawSqlMutation({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
68
+ store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
73
69
 
74
70
  expect(store.query(query$)).toMatchInlineSnapshot(`
75
71
  [
@@ -83,16 +79,22 @@ Vitest.describe('otel', () => {
83
79
 
84
80
  span.end()
85
81
 
86
- return { exporter }
82
+ return { exporter, provider }
87
83
  }).pipe(
88
84
  Effect.scoped,
89
- Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()),
85
+ Effect.tap(({ exporter, provider }) =>
86
+ Effect.promise(async () => {
87
+ await provider.forceFlush()
88
+ expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
89
+ await provider.shutdown()
90
+ }),
91
+ ),
90
92
  ),
91
93
  )
92
94
 
93
95
  Vitest.scopedLive('with thunks', () =>
94
96
  Effect.gen(function* () {
95
- const { store, exporter, span } = yield* makeQuery
97
+ const { store, exporter, span, provider } = yield* makeQuery
96
98
 
97
99
  const defaultTodo = { id: '', text: '', completed: false }
98
100
 
@@ -100,7 +102,7 @@ Vitest.describe('otel', () => {
100
102
  const query$ = queryDb(
101
103
  (get) => ({
102
104
  query: `select * from todos ${get(filter)}`,
103
- schema: Schema.Array(tables.todos.schema).pipe(Schema.headOrElse(() => defaultTodo)),
105
+ schema: Schema.Array(tables.todos.rowSchema).pipe(Schema.headOrElse(() => defaultTodo)),
104
106
  }),
105
107
  { label: 'all todos' },
106
108
  )
@@ -117,7 +119,7 @@ Vitest.describe('otel', () => {
117
119
 
118
120
  expect(store.reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
119
121
 
120
- store.commit(rawSqlMutation({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
122
+ store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
121
123
 
122
124
  expect(store.reactivityGraph.getSnapshot({ includeResults: true })).toMatchSnapshot()
123
125
 
@@ -133,21 +135,27 @@ Vitest.describe('otel', () => {
133
135
 
134
136
  span.end()
135
137
 
136
- return { exporter }
138
+ return { exporter, provider }
137
139
  }).pipe(
138
140
  Effect.scoped,
139
- Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()),
141
+ Effect.tap(({ exporter, provider }) =>
142
+ Effect.promise(async () => {
143
+ await provider.forceFlush()
144
+ expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
145
+ await provider.shutdown()
146
+ }),
147
+ ),
140
148
  ),
141
149
  )
142
150
 
143
151
  Vitest.scopedLive('with thunks with query builder and without labels', () =>
144
152
  Effect.gen(function* () {
145
- const { store, exporter, span } = yield* makeQuery
153
+ const { store, exporter, span, provider } = yield* makeQuery
146
154
 
147
155
  const defaultTodo = { id: '', text: '', completed: false }
148
156
 
149
157
  const filter = computed(() => ({ completed: false }))
150
- const query$ = queryDb((get) => tables.todos.query.where(get(filter)).first({ fallback: () => defaultTodo }))
158
+ const query$ = queryDb((get) => tables.todos.where(get(filter)).first({ fallback: () => defaultTodo }))
151
159
 
152
160
  expect(store.query(query$)).toMatchInlineSnapshot(`
153
161
  {
@@ -157,7 +165,7 @@ Vitest.describe('otel', () => {
157
165
  }
158
166
  `)
159
167
 
160
- store.commit(rawSqlMutation({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
168
+ store.commit(rawSqlEvent({ sql: sql`INSERT INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)` }))
161
169
 
162
170
  expect(store.query(query$)).toMatchInlineSnapshot(`
163
171
  {
@@ -169,10 +177,16 @@ Vitest.describe('otel', () => {
169
177
 
170
178
  span.end()
171
179
 
172
- return { exporter }
180
+ return { exporter, provider }
173
181
  }).pipe(
174
182
  Effect.scoped,
175
- Effect.tap(({ exporter }) => expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()),
183
+ Effect.tap(({ exporter, provider }) =>
184
+ Effect.promise(async () => {
185
+ await provider.forceFlush()
186
+ expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
187
+ await provider.shutdown()
188
+ }),
189
+ ),
176
190
  ),
177
191
  )
178
192
  })
@@ -1,4 +1,4 @@
1
- import type { Bindable, QueryBuilder, QueryInfo } from '@livestore/common'
1
+ import type { Bindable, QueryBuilder } from '@livestore/common'
2
2
  import {
3
3
  getDurationMsFromSpan,
4
4
  getResultSchema,
@@ -6,6 +6,7 @@ import {
6
6
  prepareBindValues,
7
7
  QueryBuilderAstSymbol,
8
8
  replaceSessionIdSymbol,
9
+ SessionIdSymbol,
9
10
  UnexpectedError,
10
11
  } from '@livestore/common'
11
12
  import { deepEqual, shouldNeverHappen } from '@livestore/utils'
@@ -14,13 +15,13 @@ import * as otel from '@opentelemetry/api'
14
15
 
15
16
  import type { Thunk } from '../reactive.js'
16
17
  import { isThunk, NOT_REFRESHED_YET } from '../reactive.js'
17
- import { makeExecBeforeFirstRun, rowQueryLabel } from '../row-query-utils.js'
18
18
  import type { RefreshReason } from '../store/store-types.js'
19
19
  import { isValidFunctionString } from '../utils/function-string.js'
20
20
  import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
21
21
  import { depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
22
+ import { makeExecBeforeFirstRun, rowQueryLabel } from './client-document-get-query.js'
22
23
 
23
- export type QueryInputRaw<TDecoded, TEncoded, TQueryInfo extends QueryInfo> = {
24
+ export type QueryInputRaw<TDecoded, TEncoded> = {
24
25
  query: string
25
26
  schema: Schema.Schema<TDecoded, TEncoded>
26
27
  bindValues?: Bindable
@@ -30,25 +31,20 @@ export type QueryInputRaw<TDecoded, TEncoded, TQueryInfo extends QueryInfo> = {
30
31
  * NOTE In the future we want to do this automatically at build time
31
32
  */
32
33
  queriedTables?: Set<string>
33
- queryInfo?: TQueryInfo
34
34
  execBeforeFirstRun?: (ctx: ReactivityGraphContext) => void
35
35
  }
36
36
 
37
- export const isQueryInputRaw = (value: unknown): value is QueryInputRaw<any, any, any> =>
37
+ export const isQueryInputRaw = (value: unknown): value is QueryInputRaw<any, any> =>
38
38
  Predicate.hasProperty(value, 'query') && Predicate.hasProperty(value, 'schema')
39
39
 
40
- export type QueryInput<TDecoded, TEncoded, TQueryInfo extends QueryInfo> =
41
- | QueryInputRaw<TDecoded, TEncoded, TQueryInfo>
42
- | QueryBuilder<TDecoded, any, any, TQueryInfo>
40
+ export type QueryInput<TDecoded, TEncoded> = QueryInputRaw<TDecoded, TEncoded> | QueryBuilder<TDecoded, any, any>
43
41
 
44
42
  /**
45
- * NOTE `query` is only supposed to read data. Don't use it to insert/update/delete data but use mutations instead.
43
+ * NOTE `queryDb` is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
46
44
  */
47
45
  export const queryDb: {
48
- <TResultSchema, TResult = TResultSchema, TQueryInfo extends QueryInfo = QueryInfo.None>(
49
- queryInput:
50
- | QueryInputRaw<TResultSchema, ReadonlyArray<any>, TQueryInfo>
51
- | QueryBuilder<TResultSchema, any, any, TQueryInfo>,
46
+ <TResultSchema, TResult = TResultSchema>(
47
+ queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>,
52
48
  options?: {
53
49
  map?: (rows: TResultSchema) => TResult
54
50
  /**
@@ -56,16 +52,15 @@ export const queryDb: {
56
52
  */
57
53
  label?: string
58
54
  deps?: DepKey
59
- queryInfo?: TQueryInfo
60
55
  },
61
- ): LiveQueryDef<TResult, TQueryInfo>
56
+ ): LiveQueryDef<TResult>
62
57
  // NOTE in this "thunk case", we can't directly derive label/queryInfo from the queryInput,
63
58
  // so the caller needs to provide them explicitly otherwise queryInfo will be set to `None`,
64
59
  // and label will be set during the query execution
65
- <TResultSchema, TResult = TResultSchema, TQueryInfo extends QueryInfo = QueryInfo.None>(
60
+ <TResultSchema, TResult = TResultSchema>(
66
61
  queryInput:
67
- | ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>, TQueryInfo>)
68
- | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any, TQueryInfo>),
62
+ | ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>)
63
+ | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>),
69
64
  options?: {
70
65
  map?: (rows: TResultSchema) => TResult
71
66
  /**
@@ -73,23 +68,21 @@ export const queryDb: {
73
68
  */
74
69
  label?: string
75
70
  deps?: DepKey
76
- queryInfo?: TQueryInfo
77
71
  },
78
- ): LiveQueryDef<TResult, TQueryInfo>
72
+ ): LiveQueryDef<TResult>
79
73
  } = (queryInput, options) => {
80
- const queryString = isQueryBuilder(queryInput)
81
- ? queryInput.toString()
82
- : isQueryInputRaw(queryInput)
83
- ? queryInput.query
84
- : typeof queryInput === 'function'
85
- ? queryInput.toString()
86
- : shouldNeverHappen(`Invalid query input: ${queryInput}`)
87
-
88
- const hash = options?.deps ? queryString + '-' + depsToString(options.deps) : queryString
74
+ const { queryString, extraDeps } = getQueryStringAndExtraDeps(queryInput)
75
+
76
+ const hash =
77
+ (options?.deps ? queryString + '-' + depsToString(options.deps) : queryString) + '-' + depsToString(extraDeps)
89
78
  if (isValidFunctionString(hash)._tag === 'invalid') {
90
79
  throw new Error(`On Expo/React Native, db queries must provide a \`deps\` option`)
91
80
  }
92
81
 
82
+ if (hash.trim() === '') {
83
+ return shouldNeverHappen(`Invalid query hash for query: ${queryInput}`)
84
+ }
85
+
93
86
  const label = options?.label ?? queryString
94
87
 
95
88
  return {
@@ -101,36 +94,55 @@ export const queryDb: {
101
94
  queryInput,
102
95
  label,
103
96
  map: options?.map,
104
- // We're not falling back to `None` here as the queryInfo will be set dynamically
105
- queryInfo: options?.queryInfo,
106
97
  otelContext,
107
98
  })
108
99
  }),
109
100
  label,
110
101
  hash,
111
- queryInfo:
112
- options?.queryInfo ?? (isQueryBuilder(queryInput) ? queryInfoFromQueryBuilder(queryInput) : { _tag: 'None' }),
113
102
  }
114
103
  }
115
104
 
105
+ const bindValuesToDepKey = (bindValues: Bindable | undefined): DepKey => {
106
+ if (bindValues === undefined) {
107
+ return []
108
+ }
109
+
110
+ return Object.entries(bindValues)
111
+ .map(([key, value]: [string, any]) => `${key}:${value === SessionIdSymbol ? 'SessionIdSymbol' : value}`)
112
+ .join(',')
113
+ }
114
+
115
+ const getQueryStringAndExtraDeps = (
116
+ queryInput: QueryInput<any, any> | ((get: GetAtomResult) => QueryInput<any, any>),
117
+ ): { queryString: string; extraDeps: DepKey } => {
118
+ if (isQueryBuilder(queryInput)) {
119
+ const { query, bindValues } = queryInput.asSql()
120
+ return { queryString: query, extraDeps: bindValuesToDepKey(bindValues) }
121
+ }
122
+
123
+ if (isQueryInputRaw(queryInput)) {
124
+ return { queryString: queryInput.query, extraDeps: bindValuesToDepKey(queryInput.bindValues) }
125
+ }
126
+
127
+ if (typeof queryInput === 'function') {
128
+ return { queryString: queryInput.toString(), extraDeps: [] }
129
+ }
130
+
131
+ return shouldNeverHappen(`Invalid query input: ${queryInput}`)
132
+ }
133
+
116
134
  /* An object encapsulating a reactive SQL query */
117
- export class LiveStoreDbQuery<
118
- TResultSchema,
119
- TResult = TResultSchema,
120
- TQueryInfo extends QueryInfo = QueryInfo.None,
121
- > extends LiveStoreQueryBase<TResult, TQueryInfo> {
135
+ export class LiveStoreDbQuery<TResultSchema, TResult = TResultSchema> extends LiveStoreQueryBase<TResult> {
122
136
  _tag: 'db' = 'db'
123
137
 
124
138
  /** A reactive thunk representing the query text */
125
- queryInput$: Thunk<QueryInputRaw<any, any, QueryInfo>, ReactivityGraphContext, RefreshReason> | undefined
139
+ queryInput$: Thunk<QueryInputRaw<any, any>, ReactivityGraphContext, RefreshReason> | undefined
126
140
 
127
141
  /** A reactive thunk representing the query results */
128
142
  results$: Thunk<TResult, ReactivityGraphContext, RefreshReason>
129
143
 
130
144
  label: string
131
145
 
132
- queryInfo: TQueryInfo
133
-
134
146
  readonly reactivityGraph
135
147
 
136
148
  private mapResult: (rows: TResultSchema) => TResult
@@ -140,23 +152,20 @@ export class LiveStoreDbQuery<
140
152
  label: inputLabel,
141
153
  reactivityGraph,
142
154
  map,
143
- queryInfo: inputQueryInfo,
144
155
  otelContext,
145
156
  }: {
146
157
  label?: string
147
158
  queryInput:
148
- | QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>
149
- | ((get: GetAtomResult, ctx: ReactivityGraphContext) => QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>)
159
+ | QueryInput<TResultSchema, ReadonlyArray<any>>
160
+ | ((get: GetAtomResult, ctx: ReactivityGraphContext) => QueryInput<TResultSchema, ReadonlyArray<any>>)
150
161
  reactivityGraph: ReactivityGraph
151
162
  map?: (rows: TResultSchema) => TResult
152
- queryInfo?: TQueryInfo
153
163
  /** Only used for the initial query execution */
154
164
  otelContext?: otel.Context
155
165
  }) {
156
166
  super()
157
167
 
158
168
  let label = inputLabel ?? 'db(unknown)'
159
- let queryInfo = inputQueryInfo ?? ({ _tag: 'None' } as TQueryInfo)
160
169
  this.reactivityGraph = reactivityGraph
161
170
 
162
171
  this.mapResult = map === undefined ? (rows: any) => rows as TResult : map
@@ -172,7 +181,7 @@ export class LiveStoreDbQuery<
172
181
  current: undefined,
173
182
  }
174
183
 
175
- type TQueryInputRaw = QueryInputRaw<any, any, QueryInfo>
184
+ type TQueryInputRaw = QueryInputRaw<any, any>
176
185
 
177
186
  let queryInputRaw$OrQueryInputRaw: TQueryInputRaw | Thunk<TQueryInputRaw, ReactivityGraphContext, RefreshReason>
178
187
 
@@ -188,14 +197,13 @@ export class LiveStoreDbQuery<
188
197
  schema,
189
198
  bindValues: qbRes.bindValues,
190
199
  queriedTables: new Set([ast.tableDef.sqliteDef.name]),
191
- queryInfo: queryInfoFromQueryBuilder(qb),
192
200
  } satisfies TQueryInputRaw,
193
201
  label: ast._tag === 'RowQuery' ? rowQueryLabel(ast.tableDef, ast.id) : qb.toString(),
194
202
  execBeforeFirstRun:
195
203
  ast._tag === 'RowQuery'
196
204
  ? makeExecBeforeFirstRun({
197
205
  table: ast.tableDef,
198
- insertValues: ast.insertValues,
206
+ explicitDefaultValues: ast.explicitDefaultValues,
199
207
  id: ast.id,
200
208
  otelContext,
201
209
  })
@@ -232,10 +240,6 @@ export class LiveStoreDbQuery<
232
240
 
233
241
  schemaRef.current = queryInputRaw.schema
234
242
 
235
- if (inputQueryInfo === undefined && queryInputRaw.queryInfo !== undefined) {
236
- queryInfo = queryInputRaw.queryInfo as TQueryInfo
237
- }
238
-
239
243
  return queryInputRaw
240
244
  },
241
245
  {
@@ -268,10 +272,6 @@ export class LiveStoreDbQuery<
268
272
  label = `db(${rowQueryLabel(ast.tableDef, ast.id)})`
269
273
  }
270
274
  }
271
-
272
- if (inputQueryInfo === undefined && queryInputRaw.queryInfo !== undefined) {
273
- queryInfo = queryInputRaw.queryInfo as TQueryInfo
274
- }
275
275
  }
276
276
 
277
277
  const queriedTablesRef: { current: Set<string> | undefined } = { current: undefined }
@@ -352,9 +352,9 @@ export class LiveStoreDbQuery<
352
352
  const expectedSchemaStr = String(schemaRef.current!.ast)
353
353
  const bindValuesStr = bindValues === undefined ? '' : `\nBind values: ${JSON.stringify(bindValues)}`
354
354
 
355
- console.error(
355
+ return shouldNeverHappen(
356
356
  `\
357
- Error parsing SQL query result.
357
+ Error parsing SQL query result (${label}).
358
358
 
359
359
  Query: ${sqlString}\
360
360
  ${bindValuesStr}
@@ -365,8 +365,8 @@ Error: ${parseErrorStr}
365
365
 
366
366
  Result:`,
367
367
  rawDbResults,
368
+ '\n',
368
369
  )
369
- return shouldNeverHappen(`Error parsing SQL query result: ${parsedResult.left}`)
370
370
  }
371
371
 
372
372
  const result = this.mapResult(parsedResult.right)
@@ -388,7 +388,6 @@ Result:`,
388
388
  this.results$ = results$
389
389
 
390
390
  this.label = label
391
- this.queryInfo = queryInfo
392
391
  }
393
392
 
394
393
  destroy = () => {
@@ -405,8 +404,3 @@ Result:`,
405
404
  }
406
405
  }
407
406
  }
408
-
409
- const queryInfoFromQueryBuilder = (qb: QueryBuilder.Any): QueryInfo.Row | QueryInfo.None => {
410
- const ast = qb[QueryBuilderAstSymbol]
411
- return ast._tag === 'RowQuery' ? { _tag: 'Row', table: ast.tableDef, id: ast.id } : { _tag: 'None' }
412
- }
@@ -5,6 +5,8 @@ import type { RefreshReason } from '../store/store-types.js'
5
5
  import type { ILiveQueryRef, ILiveQueryRefDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
6
6
  import { withRCMap } from './base-class.js'
7
7
 
8
+ // TODO rename to `signal`
9
+
8
10
  export const makeRef = <T>(
9
11
  defaultValue: T,
10
12
  options?: {
package/src/mod.ts CHANGED
@@ -13,8 +13,6 @@ export {
13
13
 
14
14
  export { SqliteDbWrapper, emptyDebugInfo } from './SqliteDbWrapper.js'
15
15
 
16
- export { deriveColQuery } from './row-query-utils.js'
17
-
18
16
  export { queryDb, computed, makeRef, type LiveQuery, type LiveQueryDef } from './live-queries/mod.js'
19
17
 
20
18
  export * from '@livestore/common/schema'
package/src/reactive.ts CHANGED
@@ -203,7 +203,7 @@ export class ReactiveGraph<
203
203
 
204
204
  context: TContext | undefined
205
205
 
206
- debugRefreshInfos: BoundArray<RefreshDebugInfo<TDebugRefreshReason, TDebugThunkInfo>> = new BoundArray(5000)
206
+ debugRefreshInfos: BoundArray<RefreshDebugInfo<TDebugRefreshReason, TDebugThunkInfo>> = new BoundArray(200)
207
207
 
208
208
  private currentDebugRefresh:
209
209
  | { refreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[]; startMs: DOMHighResTimeStamp }
@@ -267,7 +267,7 @@ export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, T
267
267
  // But for now this is a good enough approximation with little downsides
268
268
  __runningInDevtools: getDevtoolsEnabled(disableDevtools) === false,
269
269
  confirmUnsavedChanges,
270
- // NOTE during boot we're not yet executing mutations in a batched context
270
+ // NOTE during boot we're not yet executing events in a batched context
271
271
  // but only set the provided `batchUpdates` function after boot
272
272
  batchUpdates: (run) => run(),
273
273
  storeId,
@@ -276,7 +276,7 @@ export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, T
276
276
  },
277
277
  })
278
278
 
279
- // Starts background fibers (syncing, mutation processing, etc) for store
279
+ // Starts background fibers (syncing, event processing, etc) for store
280
280
  yield* store.boot
281
281
 
282
282
  if (boot !== undefined) {
@@ -1,5 +1,5 @@
1
1
  import type { ClientSession, IntentionalShutdownCause, StoreInterrupted, UnexpectedError } from '@livestore/common'
2
- import type { LiveStoreSchema, MutationEvent } from '@livestore/common/schema'
2
+ import type { LiveStoreEvent, LiveStoreSchema } from '@livestore/common/schema'
3
3
  import type { Effect, Runtime, Scope } from '@livestore/utils/effect'
4
4
  import { Deferred } from '@livestore/utils/effect'
5
5
  import type * as otel from '@opentelemetry/api'
@@ -57,8 +57,8 @@ export type RefreshReason =
57
57
  | DebugRefreshReasonBase
58
58
  | {
59
59
  _tag: 'commit'
60
- /** The mutations that were applied */
61
- mutations: ReadonlyArray<MutationEvent.AnyDecoded | MutationEvent.PartialAnyDecoded>
60
+ /** The events that were applied */
61
+ events: ReadonlyArray<LiveStoreEvent.AnyDecoded | LiveStoreEvent.PartialAnyDecoded>
62
62
 
63
63
  /** The tables that were written to by the event */
64
64
  writeTables: ReadonlyArray<string>
@@ -83,11 +83,11 @@ export type QueryDebugInfo = {
83
83
 
84
84
  export type StoreOtel = {
85
85
  tracer: otel.Tracer
86
- mutationsSpanContext: otel.Context
86
+ commitsSpanContext: otel.Context
87
87
  queriesSpanContext: otel.Context
88
88
  }
89
89
 
90
- export type StoreMutateOptions = {
90
+ export type StoreCommitOptions = {
91
91
  label?: string
92
92
  skipRefresh?: boolean
93
93
  spanLinks?: otel.Link[]