@livestore/livestore 0.0.0-snapshot-2cc2dcc1f7d0a534a2247a531ff3ecf6a83f0e63 → 0.0.0-snapshot-846084fc168de30ab64beedc157728bc85d858b0.2

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.
@@ -1,4 +1,5 @@
1
1
  import { isNotNil } from '@livestore/utils'
2
+ import { Predicate } from '@livestore/utils/effect'
2
3
  import type * as otel from '@opentelemetry/api'
3
4
 
4
5
  import * as RG from '../reactive.js'
@@ -22,18 +23,26 @@ export type ReactivityGraphContext = {
22
23
  effectsWrapper: (run: () => void) => void
23
24
  }
24
25
 
25
- export type GetResult<TQuery extends LiveQueryDef.Any | LiveQuery.Any> =
26
- TQuery extends LiveQuery<infer TResult> ? TResult : TQuery extends LiveQueryDef<infer TResult> ? TResult : unknown
26
+ export type GetResult<TQuery extends LiveQueryDef.Any | LiveQuery.Any | SignalDef<any>> =
27
+ TQuery extends LiveQuery<infer TResult>
28
+ ? TResult
29
+ : TQuery extends LiveQueryDef<infer TResult>
30
+ ? TResult
31
+ : TQuery extends SignalDef<infer TResult>
32
+ ? TResult
33
+ : unknown
27
34
 
28
35
  let queryIdCounter = 0
29
36
 
30
- export interface SignalDef<T> {
37
+ export interface SignalDef<T> extends LiveQueryDef<T, 'signal-def'> {
31
38
  _tag: 'signal-def'
32
39
  defaultValue: T
40
+ hash: string
41
+ label: string
33
42
  make: (ctx: ReactivityGraphContext) => RcRef<ISignal<T>>
34
43
  }
35
44
 
36
- export interface ISignal<T> {
45
+ export interface ISignal<T> extends LiveQuery<T> {
37
46
  _tag: 'signal'
38
47
  reactivityGraph: ReactivityGraph
39
48
  ref: RG.Ref<T, ReactivityGraphContext, RefreshReason>
@@ -60,16 +69,16 @@ export const depsToString = (deps: DepKey): string => {
60
69
  return deps.filter(isNotNil).join(',')
61
70
  }
62
71
 
63
- export interface LiveQueryDef<TResult> {
64
- _tag: 'def'
72
+ export interface LiveQueryDef<TResult, TTag extends string = 'def'> {
73
+ _tag: TTag
65
74
  /** Creates a new LiveQuery instance bound to a specific store/reactivityGraph */
66
- make: (ctx: ReactivityGraphContext, otelContext?: otel.Context) => RcRef<LiveQuery<TResult>>
75
+ make: (ctx: ReactivityGraphContext, otelContext?: otel.Context) => RcRef<LiveQuery<TResult> | ISignal<TResult>>
67
76
  label: string
68
77
  hash: string
69
78
  }
70
79
 
71
80
  export namespace LiveQueryDef {
72
- export type Any = LiveQueryDef<any>
81
+ export type Any = LiveQueryDef<any, 'def' | 'signal-def'>
73
82
  }
74
83
 
75
84
  /**
@@ -77,7 +86,7 @@ export namespace LiveQueryDef {
77
86
  */
78
87
  export interface LiveQuery<TResult> {
79
88
  id: number
80
- _tag: 'computed' | 'db' | 'graphql'
89
+ _tag: 'computed' | 'db' | 'graphql' | 'signal'
81
90
  [TypeId]: TypeId
82
91
 
83
92
  // reactivityGraph: ReactivityGraph
@@ -86,7 +95,7 @@ export interface LiveQuery<TResult> {
86
95
  '__result!': TResult
87
96
 
88
97
  /** A reactive thunk representing the query results */
89
- results$: RG.Thunk<TResult, ReactivityGraphContext, RefreshReason>
98
+ results$: RG.Atom<TResult, ReactivityGraphContext, RefreshReason>
90
99
 
91
100
  label: string
92
101
 
@@ -106,7 +115,7 @@ export interface LiveQuery<TResult> {
106
115
  runs: number
107
116
 
108
117
  executionTimes: number[]
109
- def: LiveQueryDef<TResult>
118
+ def: LiveQueryDef<TResult> | SignalDef<TResult>
110
119
  }
111
120
 
112
121
  export namespace LiveQuery {
@@ -117,21 +126,24 @@ export abstract class LiveStoreQueryBase<TResult> implements LiveQuery<TResult>
117
126
  '__result!'!: TResult
118
127
  id = queryIdCounter++;
119
128
  [TypeId]: TypeId = TypeId
120
- abstract _tag: 'computed' | 'db' | 'graphql'
129
+ abstract _tag: 'computed' | 'db' | 'graphql' | 'signal'
121
130
 
122
131
  /** Human-readable label for the query for debugging */
123
132
  abstract label: string
124
133
 
125
- abstract def: LiveQueryDef<TResult>
134
+ abstract def: LiveQueryDef<TResult> | SignalDef<TResult>
126
135
 
127
- abstract results$: RG.Thunk<TResult, ReactivityGraphContext, RefreshReason>
136
+ abstract results$: RG.Atom<TResult, ReactivityGraphContext, RefreshReason>
128
137
 
129
138
  activeSubscriptions: Set<StackInfo> = new Set()
130
139
 
131
140
  abstract readonly reactivityGraph: ReactivityGraph
132
141
 
133
142
  get runs() {
134
- return this.results$.recomputations
143
+ if (this.results$._tag === 'thunk') {
144
+ return this.results$.recomputations
145
+ }
146
+ return 0
135
147
  }
136
148
 
137
149
  executionTimes: number[] = []
@@ -183,7 +195,9 @@ export const makeGetAtomResult = (
183
195
  }
184
196
 
185
197
  // Signal case
186
- if (atom._tag === 'signal') return get(atom.ref, otelContext, debugRefreshReason)
198
+ if (atom._tag === 'signal' && Predicate.hasProperty(atom, 'ref')) {
199
+ return get(atom.ref, otelContext, debugRefreshReason)
200
+ }
187
201
 
188
202
  // LiveQuery case
189
203
  return get(atom.results$, otelContext, debugRefreshReason)
@@ -19,7 +19,7 @@ export const computed = <TResult>(
19
19
  throw new Error(`On Expo/React Native, computed queries must provide a \`deps\` option`)
20
20
  }
21
21
 
22
- const def: LiveQueryDef.Any = {
22
+ const def: LiveQueryDef<any> = {
23
23
  _tag: 'def',
24
24
  make: withRCMap(hash, (ctx, _otelContext) => {
25
25
  // TODO onDestroy
@@ -41,7 +41,7 @@ export const computed = <TResult>(
41
41
  }
42
42
 
43
43
  export class LiveStoreComputedQuery<TResult> extends LiveStoreQueryBase<TResult> {
44
- _tag: 'computed' = 'computed'
44
+ _tag = 'computed' as const
45
45
 
46
46
  /** A reactive thunk representing the query results */
47
47
  results$: Thunk<TResult, ReactivityGraphContext, RefreshReason>
@@ -102,8 +102,10 @@ export const queryDb: {
102
102
  } = (queryInput, options) => {
103
103
  const { queryString, extraDeps } = getQueryStringAndExtraDeps(queryInput)
104
104
 
105
- const hash =
106
- (options?.deps ? queryString + '-' + depsToString(options.deps) : queryString) + '-' + depsToString(extraDeps)
105
+ const hash = [queryString, options?.deps ? depsToString(options.deps) : undefined, depsToString(extraDeps)]
106
+ .filter(Boolean)
107
+ .join('-')
108
+
107
109
  if (isValidFunctionString(hash)._tag === 'invalid') {
108
110
  throw new Error(`On Expo/React Native, db queries must provide a \`deps\` option`)
109
111
  }
@@ -114,7 +116,7 @@ export const queryDb: {
114
116
 
115
117
  const label = options?.label ?? queryString
116
118
 
117
- const def: LiveQueryDef.Any = {
119
+ const def: LiveQueryDef<any> = {
118
120
  _tag: 'def',
119
121
  make: withRCMap(hash, (ctx, otelContext) => {
120
122
  // TODO onDestroy
@@ -165,7 +167,7 @@ const getQueryStringAndExtraDeps = (
165
167
 
166
168
  /* An object encapsulating a reactive SQL query */
167
169
  export class LiveStoreDbQuery<TResultSchema, TResult = TResultSchema> extends LiveStoreQueryBase<TResult> {
168
- _tag: 'db' = 'db'
170
+ _tag = 'db' as const
169
171
 
170
172
  /** A reactive thunk representing the query text */
171
173
  queryInput$: Thunk<QueryInputRaw<any, any>, ReactivityGraphContext, RefreshReason> | undefined
@@ -3,7 +3,7 @@ import { nanoid } from '@livestore/utils/nanoid'
3
3
  import type * as RG from '../reactive.js'
4
4
  import type { RefreshReason } from '../store/store-types.js'
5
5
  import type { ISignal, ReactivityGraph, ReactivityGraphContext, SignalDef } from './base-class.js'
6
- import { withRCMap } from './base-class.js'
6
+ import { LiveStoreQueryBase, withRCMap } from './base-class.js'
7
7
 
8
8
  export const signal = <T>(
9
9
  defaultValue: T,
@@ -12,25 +12,59 @@ export const signal = <T>(
12
12
  },
13
13
  ): SignalDef<T> => {
14
14
  const id = nanoid()
15
- return {
15
+ const def: SignalDef<T> = {
16
16
  _tag: 'signal-def',
17
17
  defaultValue,
18
- make: withRCMap(id, (ctx) => new Signal(defaultValue, ctx.reactivityGraph.deref()!, options)),
18
+ hash: id,
19
+ label: options?.label ?? 'Signal',
20
+ make: withRCMap(
21
+ id,
22
+ (ctx) =>
23
+ new Signal({
24
+ defaultValue,
25
+ reactivityGraph: ctx.reactivityGraph.deref()!,
26
+ label: options?.label ?? 'Signal',
27
+ def,
28
+ }),
29
+ ),
19
30
  }
31
+
32
+ return def
20
33
  }
21
34
 
22
- export class Signal<T> implements ISignal<T> {
35
+ export class Signal<T> extends LiveStoreQueryBase<T> implements ISignal<T> {
23
36
  _tag = 'signal' as const
24
37
  readonly ref: RG.Ref<T, ReactivityGraphContext, RefreshReason>
25
-
38
+ label: string
39
+ reactivityGraph: ReactivityGraph
40
+ results$: RG.Ref<T, ReactivityGraphContext, RefreshReason>
41
+ def: SignalDef<T>
26
42
  constructor(
27
- private defaultValue: T,
28
- readonly reactivityGraph: ReactivityGraph,
29
- private options?: {
30
- label?: string
43
+ // private defaultValue: T,
44
+ // readonly reactivityGraph: ReactivityGraph,
45
+ // private options?: {
46
+ // label?: string
47
+ // },
48
+ {
49
+ defaultValue,
50
+ reactivityGraph,
51
+ label,
52
+ def,
53
+ }: {
54
+ defaultValue: T
55
+ reactivityGraph: ReactivityGraph
56
+ label: string
57
+ def: SignalDef<T>
31
58
  },
32
59
  ) {
33
- this.ref = reactivityGraph.makeRef(defaultValue, { label: options?.label })
60
+ super()
61
+
62
+ this.ref = reactivityGraph.makeRef(defaultValue, { label })
63
+ this.label = label
64
+ this.reactivityGraph = reactivityGraph
65
+ this.def = def
66
+
67
+ this.results$ = this.ref
34
68
  }
35
69
 
36
70
  set = (value: T) => {
package/src/mod.ts CHANGED
@@ -13,7 +13,16 @@ export {
13
13
 
14
14
  export { SqliteDbWrapper, emptyDebugInfo } from './SqliteDbWrapper.js'
15
15
 
16
- export { queryDb, computed, signal, type LiveQuery, type LiveQueryDef } from './live-queries/mod.js'
16
+ export {
17
+ queryDb,
18
+ computed,
19
+ signal,
20
+ type LiveQuery,
21
+ type LiveQueryDef,
22
+ type Signal,
23
+ type SignalDef,
24
+ type RcRef,
25
+ } from './live-queries/mod.js'
17
26
 
18
27
  export * from '@livestore/common/schema'
19
28
  export {
@@ -135,7 +135,9 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
135
135
  this.sqliteDbWrapper.execute(statementSql, bindValues, { otelContext, writeTables })
136
136
 
137
137
  // durationMsTotal += durationMs
138
- writeTables.forEach((table) => writeTablesForEvent.add(table))
138
+ for (const table of writeTables) {
139
+ writeTablesForEvent.add(table)
140
+ }
139
141
  }
140
142
  }
141
143
 
@@ -262,7 +264,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
262
264
  * ```
263
265
  */
264
266
  subscribe = <TResult>(
265
- query: LiveQueryDef<TResult> | LiveQuery<TResult>,
267
+ query: LiveQueryDef<TResult, 'def' | 'signal-def'> | LiveQuery<TResult>,
266
268
  options: {
267
269
  /** Called when the query result has changed */
268
270
  onUpdate: (value: TResult) => void
@@ -289,10 +291,10 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
289
291
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
290
292
 
291
293
  const queryRcRef =
292
- query._tag === 'def'
294
+ query._tag === 'def' || query._tag === 'signal-def'
293
295
  ? query.make(this.reactivityGraph.context!)
294
296
  : {
295
- value: query,
297
+ value: query as LiveQuery<TResult>,
296
298
  deref: () => {},
297
299
  }
298
300
  const query$ = queryRcRef.value