@livestore/livestore 0.3.0-dev.11 → 0.3.0-dev.5

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 (122) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/SynchronousDatabaseWrapper.d.ts +5 -14
  3. package/dist/SynchronousDatabaseWrapper.d.ts.map +1 -1
  4. package/dist/SynchronousDatabaseWrapper.js +4 -24
  5. package/dist/SynchronousDatabaseWrapper.js.map +1 -1
  6. package/dist/effect/LiveStore.d.ts +8 -12
  7. package/dist/effect/LiveStore.d.ts.map +1 -1
  8. package/dist/effect/LiveStore.js +3 -13
  9. package/dist/effect/LiveStore.js.map +1 -1
  10. package/dist/index.d.ts +7 -6
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/live-queries/base-class.d.ts +21 -64
  15. package/dist/live-queries/base-class.d.ts.map +1 -1
  16. package/dist/live-queries/base-class.js +13 -56
  17. package/dist/live-queries/base-class.js.map +1 -1
  18. package/dist/live-queries/computed.d.ts +7 -7
  19. package/dist/live-queries/computed.d.ts.map +1 -1
  20. package/dist/live-queries/computed.js +11 -35
  21. package/dist/live-queries/computed.js.map +1 -1
  22. package/dist/live-queries/db.d.ts +15 -12
  23. package/dist/live-queries/db.d.ts.map +1 -1
  24. package/dist/live-queries/db.js +25 -44
  25. package/dist/live-queries/db.js.map +1 -1
  26. package/dist/live-queries/db.test.js +14 -16
  27. package/dist/live-queries/db.test.js.map +1 -1
  28. package/dist/live-queries/graphql.d.ts +8 -8
  29. package/dist/live-queries/graphql.d.ts.map +1 -1
  30. package/dist/live-queries/graphql.js +9 -35
  31. package/dist/live-queries/graphql.js.map +1 -1
  32. package/dist/reactive.d.ts +13 -15
  33. package/dist/reactive.d.ts.map +1 -1
  34. package/dist/reactive.js +9 -15
  35. package/dist/reactive.js.map +1 -1
  36. package/dist/row-query-utils.d.ts +4 -4
  37. package/dist/row-query-utils.d.ts.map +1 -1
  38. package/dist/row-query-utils.js +10 -14
  39. package/dist/row-query-utils.js.map +1 -1
  40. package/dist/store/create-store.d.ts +4 -3
  41. package/dist/store/create-store.d.ts.map +1 -1
  42. package/dist/store/create-store.js +7 -7
  43. package/dist/store/create-store.js.map +1 -1
  44. package/dist/store/devtools.d.ts +2 -2
  45. package/dist/store/devtools.d.ts.map +1 -1
  46. package/dist/store/devtools.js +15 -15
  47. package/dist/store/devtools.js.map +1 -1
  48. package/dist/store/store-types.d.ts +5 -10
  49. package/dist/store/store-types.d.ts.map +1 -1
  50. package/dist/store/store-types.js.map +1 -1
  51. package/dist/store/store.d.ts +16 -34
  52. package/dist/store/store.d.ts.map +1 -1
  53. package/dist/store/store.js +77 -129
  54. package/dist/store/store.js.map +1 -1
  55. package/dist/utils/stack-info.d.ts.map +1 -1
  56. package/dist/utils/stack-info.js +1 -6
  57. package/dist/utils/stack-info.js.map +1 -1
  58. package/dist/utils/stack-info.test.js +1 -54
  59. package/dist/utils/stack-info.test.js.map +1 -1
  60. package/dist/utils/tests/fixture.d.ts +6 -2
  61. package/dist/utils/tests/fixture.d.ts.map +1 -1
  62. package/dist/utils/tests/fixture.js +5 -3
  63. package/dist/utils/tests/fixture.js.map +1 -1
  64. package/dist/utils/tests/mod.d.ts +0 -1
  65. package/dist/utils/tests/mod.d.ts.map +1 -1
  66. package/dist/utils/tests/mod.js +0 -1
  67. package/dist/utils/tests/mod.js.map +1 -1
  68. package/package.json +12 -12
  69. package/src/{SqliteDbWrapper.ts → SynchronousDatabaseWrapper.ts} +11 -41
  70. package/src/effect/LiveStore.ts +15 -26
  71. package/src/global-state.ts +20 -0
  72. package/src/index.ts +7 -14
  73. package/src/live-queries/__snapshots__/{db-query.test.ts.snap → db.test.ts.snap} +42 -196
  74. package/src/live-queries/base-class.ts +40 -160
  75. package/src/live-queries/computed.ts +19 -45
  76. package/src/live-queries/{db-query.test.ts → db.test.ts} +11 -21
  77. package/src/live-queries/{db-query.ts → db.ts} +39 -97
  78. package/src/live-queries/graphql.ts +21 -47
  79. package/src/reactive.ts +27 -52
  80. package/src/row-query-utils.ts +18 -29
  81. package/src/store/create-store.ts +23 -20
  82. package/src/store/devtools.ts +17 -17
  83. package/src/store/store-types.ts +5 -7
  84. package/src/store/store.ts +122 -231
  85. package/src/utils/stack-info.test.ts +1 -58
  86. package/src/utils/stack-info.ts +1 -6
  87. package/src/utils/tests/fixture.ts +7 -2
  88. package/src/utils/tests/mod.ts +0 -1
  89. package/dist/SqliteDbWrapper.d.ts +0 -54
  90. package/dist/SqliteDbWrapper.d.ts.map +0 -1
  91. package/dist/SqliteDbWrapper.js +0 -212
  92. package/dist/SqliteDbWrapper.js.map +0 -1
  93. package/dist/__tests__/fixture.d.ts +0 -252
  94. package/dist/__tests__/fixture.d.ts.map +0 -1
  95. package/dist/__tests__/fixture.js +0 -18
  96. package/dist/__tests__/fixture.js.map +0 -1
  97. package/dist/live-queries/db-query.d.ts +0 -67
  98. package/dist/live-queries/db-query.d.ts.map +0 -1
  99. package/dist/live-queries/db-query.js +0 -244
  100. package/dist/live-queries/db-query.js.map +0 -1
  101. package/dist/live-queries/db-query.test.d.ts +0 -2
  102. package/dist/live-queries/db-query.test.d.ts.map +0 -1
  103. package/dist/live-queries/db-query.test.js +0 -123
  104. package/dist/live-queries/db-query.test.js.map +0 -1
  105. package/dist/live-queries/make-ref.d.ts +0 -20
  106. package/dist/live-queries/make-ref.d.ts.map +0 -1
  107. package/dist/live-queries/make-ref.js +0 -33
  108. package/dist/live-queries/make-ref.js.map +0 -1
  109. package/dist/store/store.test.d.ts +0 -2
  110. package/dist/store/store.test.d.ts.map +0 -1
  111. package/dist/store/store.test.js +0 -27
  112. package/dist/store/store.test.js.map +0 -1
  113. package/dist/utils/expo.d.ts +0 -2
  114. package/dist/utils/expo.d.ts.map +0 -1
  115. package/dist/utils/expo.js +0 -8
  116. package/dist/utils/expo.js.map +0 -1
  117. package/dist/utils/function-string.d.ts +0 -7
  118. package/dist/utils/function-string.d.ts.map +0 -1
  119. package/dist/utils/function-string.js +0 -9
  120. package/dist/utils/function-string.js.map +0 -1
  121. package/src/live-queries/make-ref.ts +0 -47
  122. package/src/utils/function-string.ts +0 -12
@@ -11,14 +11,14 @@ import { deepEqual, shouldNeverHappen } from '@livestore/utils'
11
11
  import { Predicate, Schema, TreeFormatter } from '@livestore/utils/effect'
12
12
  import * as otel from '@opentelemetry/api'
13
13
 
14
+ import { globalReactivityGraph } from '../global-state.js'
14
15
  import type { Thunk } from '../reactive.js'
15
16
  import { isThunk, NOT_REFRESHED_YET } from '../reactive.js'
16
17
  import { makeExecBeforeFirstRun, rowQueryLabel } from '../row-query-utils.js'
17
18
  import type { RefreshReason } from '../store/store-types.js'
18
- import { isValidFunctionString } from '../utils/function-string.js'
19
19
  import { getDurationMsFromSpan } from '../utils/otel.js'
20
- import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
21
- import { defCounterRef, depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
20
+ import type { GetAtomResult, LiveQuery, QueryContext, ReactivityGraph } from './base-class.js'
21
+ import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
22
22
 
23
23
  export type QueryInputRaw<TDecoded, TEncoded, TQueryInfo extends QueryInfo> = {
24
24
  query: string
@@ -31,12 +31,9 @@ export type QueryInputRaw<TDecoded, TEncoded, TQueryInfo extends QueryInfo> = {
31
31
  */
32
32
  queriedTables?: Set<string>
33
33
  queryInfo?: TQueryInfo
34
- execBeforeFirstRun?: (ctx: ReactivityGraphContext) => void
34
+ execBeforeFirstRun?: (ctx: QueryContext) => void
35
35
  }
36
36
 
37
- export const isQueryInputRaw = (value: unknown): value is QueryInputRaw<any, any, any> =>
38
- Predicate.hasProperty(value, 'query') && Predicate.hasProperty(value, 'schema')
39
-
40
37
  export type QueryInput<TDecoded, TEncoded, TQueryInfo extends QueryInfo> =
41
38
  | QueryInputRaw<TDecoded, TEncoded, TQueryInfo>
42
39
  | QueryBuilder<TDecoded, any, any, TQueryInfo>
@@ -55,10 +52,10 @@ export const queryDb: {
55
52
  * Used for debugging / devtools
56
53
  */
57
54
  label?: string
58
- deps?: DepKey
59
- queryInfo?: TQueryInfo
55
+ reactivityGraph?: ReactivityGraph
56
+ otelContext?: otel.Context
60
57
  },
61
- ): LiveQueryDef<TResult, TQueryInfo>
58
+ ): LiveQuery<TResult, TQueryInfo>
62
59
  // NOTE in this "thunk case", we can't directly derive label/queryInfo from the queryInput,
63
60
  // so the caller needs to provide them explicitly otherwise queryInfo will be set to `None`,
64
61
  // and label will be set during the query execution
@@ -72,47 +69,20 @@ export const queryDb: {
72
69
  * Used for debugging / devtools
73
70
  */
74
71
  label?: string
75
- deps?: DepKey
72
+ reactivityGraph?: ReactivityGraph
76
73
  queryInfo?: TQueryInfo
74
+ otelContext?: otel.Context
77
75
  },
78
- ): LiveQueryDef<TResult, TQueryInfo>
79
- } = (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
89
- if (isValidFunctionString(hash)._tag === 'invalid') {
90
- throw new Error(`On Expo/React Native, db queries must provide a \`deps\` option`)
91
- }
92
-
93
- const label = options?.label ?? queryString
94
-
95
- return {
96
- _tag: 'def',
97
- id: ++defCounterRef.current,
98
- make: withRCMap(hash, (ctx, otelContext) => {
99
- // TODO onDestroy
100
- return new LiveStoreDbQuery({
101
- reactivityGraph: ctx.reactivityGraph.deref()!,
102
- queryInput,
103
- label,
104
- map: options?.map,
105
- // We're not falling back to `None` here as the queryInfo will be set dynamically
106
- queryInfo: options?.queryInfo,
107
- otelContext,
108
- })
109
- }),
110
- label,
111
- hash,
112
- queryInfo:
113
- options?.queryInfo ?? (isQueryBuilder(queryInput) ? queryInfoFromQueryBuilder(queryInput) : { _tag: 'None' }),
114
- }
115
- }
76
+ ): LiveQuery<TResult, TQueryInfo>
77
+ } = (queryInput, options) =>
78
+ new LiveStoreDbQuery({
79
+ queryInput,
80
+ label: options?.label,
81
+ reactivityGraph: options?.reactivityGraph,
82
+ map: options?.map,
83
+ queryInfo: Predicate.hasProperty(options, 'queryInfo') ? (options.queryInfo as QueryInfo) : undefined,
84
+ otelContext: options?.otelContext,
85
+ })
116
86
 
117
87
  /* An object encapsulating a reactive SQL query */
118
88
  export class LiveStoreDbQuery<
@@ -123,16 +93,16 @@ export class LiveStoreDbQuery<
123
93
  _tag: 'db' = 'db'
124
94
 
125
95
  /** A reactive thunk representing the query text */
126
- queryInput$: Thunk<QueryInputRaw<any, any, QueryInfo>, ReactivityGraphContext, RefreshReason> | undefined
96
+ queryInput$: Thunk<QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>, QueryContext, RefreshReason> | undefined
127
97
 
128
98
  /** A reactive thunk representing the query results */
129
- results$: Thunk<TResult, ReactivityGraphContext, RefreshReason>
99
+ results$: Thunk<TResult, QueryContext, RefreshReason>
130
100
 
131
101
  label: string
132
102
 
133
103
  queryInfo: TQueryInfo
134
104
 
135
- readonly reactivityGraph
105
+ protected reactivityGraph
136
106
 
137
107
  private mapResult: (rows: TResultSchema) => TResult
138
108
 
@@ -147,18 +117,17 @@ export class LiveStoreDbQuery<
147
117
  label?: string
148
118
  queryInput:
149
119
  | QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>
150
- | ((get: GetAtomResult, ctx: ReactivityGraphContext) => QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>)
151
- reactivityGraph: ReactivityGraph
120
+ | ((get: GetAtomResult, ctx: QueryContext) => QueryInput<TResultSchema, ReadonlyArray<any>, TQueryInfo>)
121
+ reactivityGraph?: ReactivityGraph
152
122
  map?: (rows: TResultSchema) => TResult
153
123
  queryInfo?: TQueryInfo
154
- /** Only used for the initial query execution */
155
124
  otelContext?: otel.Context
156
125
  }) {
157
126
  super()
158
127
 
159
128
  let label = inputLabel ?? 'db(unknown)'
160
129
  let queryInfo = inputQueryInfo ?? ({ _tag: 'None' } as TQueryInfo)
161
- this.reactivityGraph = reactivityGraph
130
+ this.reactivityGraph = reactivityGraph ?? globalReactivityGraph
162
131
 
163
132
  this.mapResult = map === undefined ? (rows: any) => rows as TResult : map
164
133
 
@@ -167,15 +136,13 @@ export class LiveStoreDbQuery<
167
136
  typeof queryInput === 'function' ? undefined : isQueryBuilder(queryInput) ? undefined : queryInput.schema,
168
137
  }
169
138
 
170
- const execBeforeFirstRunRef: {
171
- current: ((ctx: ReactivityGraphContext, otelContext: otel.Context) => void) | undefined
172
- } = {
139
+ const execBeforeFirstRunRef: { current: ((ctx: QueryContext, otelContext: otel.Context) => void) | undefined } = {
173
140
  current: undefined,
174
141
  }
175
142
 
176
143
  type TQueryInputRaw = QueryInputRaw<any, any, QueryInfo>
177
144
 
178
- let queryInputRaw$OrQueryInputRaw: TQueryInputRaw | Thunk<TQueryInputRaw, ReactivityGraphContext, RefreshReason>
145
+ let queryInputRaw$OrQueryInputRaw: TQueryInputRaw | Thunk<TQueryInputRaw, QueryContext, RefreshReason>
179
146
 
180
147
  const fromQueryBuilder = (qb: QueryBuilder.Any, otelContext: otel.Context | undefined) => {
181
148
  try {
@@ -189,7 +156,7 @@ export class LiveStoreDbQuery<
189
156
  schema,
190
157
  bindValues: qbRes.bindValues,
191
158
  queriedTables: new Set([ast.tableDef.sqliteDef.name]),
192
- queryInfo: queryInfoFromQueryBuilder(qb),
159
+ queryInfo: ast._tag === 'RowQuery' ? { _tag: 'Row', table: ast.tableDef, id: ast.id } : { _tag: 'None' },
193
160
  } satisfies TQueryInputRaw,
194
161
  label: ast._tag === 'RowQuery' ? rowQueryLabel(ast.tableDef, ast.id) : qb.toString(),
195
162
  execBeforeFirstRun:
@@ -211,10 +178,7 @@ export class LiveStoreDbQuery<
211
178
  queryInputRaw$OrQueryInputRaw = this.reactivityGraph.makeThunk(
212
179
  (get, setDebugInfo, ctx, otelContext) => {
213
180
  const startMs = performance.now()
214
- const queryInputResult = queryInput(
215
- makeGetAtomResult(get, ctx, otelContext ?? ctx.rootOtelContext, this.dependencyQueriesRef),
216
- ctx,
217
- )
181
+ const queryInputResult = queryInput(makeGetAtomResult(get, otelContext ?? ctx.rootOtelContext), ctx)
218
182
  const durationMs = performance.now() - startMs
219
183
 
220
184
  let queryInputRaw: TQueryInputRaw
@@ -246,8 +210,6 @@ export class LiveStoreDbQuery<
246
210
  equal: (a, b) => a.query === b.query && deepEqual(a.bindValues, b.bindValues),
247
211
  },
248
212
  )
249
-
250
- this.queryInput$ = queryInputRaw$OrQueryInputRaw
251
213
  } else {
252
214
  let queryInputRaw: TQueryInputRaw
253
215
  if (isQueryBuilder(queryInput)) {
@@ -291,16 +253,10 @@ export class LiveStoreDbQuery<
291
253
  : undefined
292
254
 
293
255
  const results$ = this.reactivityGraph.makeThunk<TResult>(
294
- (get, setDebugInfo, queryContext, otelContext, debugRefreshReason) =>
256
+ (get, setDebugInfo, queryContext, otelContext) =>
295
257
  queryContext.otelTracer.startActiveSpan(
296
258
  'db:...', // NOTE span name will be overridden further down
297
- {
298
- attributes: {
299
- 'livestore.debugRefreshReason': Predicate.hasProperty(debugRefreshReason, 'label')
300
- ? (debugRefreshReason.label as string)
301
- : debugRefreshReason?._tag,
302
- },
303
- },
259
+ {},
304
260
  otelContext ?? queryContext.rootOtelContext,
305
261
  (span) => {
306
262
  const otelContext = otel.trace.setSpan(otel.context.active(), span)
@@ -312,14 +268,14 @@ export class LiveStoreDbQuery<
312
268
  }
313
269
 
314
270
  const queryInputResult = isThunk(queryInputRaw$OrQueryInputRaw)
315
- ? (get(queryInputRaw$OrQueryInputRaw, otelContext, debugRefreshReason) as TQueryInputRaw)
271
+ ? (get(queryInputRaw$OrQueryInputRaw, otelContext) as TQueryInputRaw)
316
272
  : (queryInputRaw$OrQueryInputRaw as TQueryInputRaw)
317
273
 
318
274
  const sqlString = queryInputResult.query
319
275
  const bindValues = queryInputResult.bindValues
320
276
 
321
277
  if (queriedTablesRef.current === undefined) {
322
- queriedTablesRef.current = store.sqliteDbWrapper.getTablesUsed(sqlString)
278
+ queriedTablesRef.current = store.syncDbWrapper.getTablesUsed(sqlString)
323
279
  }
324
280
 
325
281
  if (bindValues !== undefined) {
@@ -329,20 +285,17 @@ export class LiveStoreDbQuery<
329
285
  // Establish a reactive dependency on the tables used in the query
330
286
  for (const tableName of queriedTablesRef.current) {
331
287
  const tableRef = store.tableRefs[tableName] ?? shouldNeverHappen(`No table ref found for ${tableName}`)
332
- get(tableRef, otelContext, debugRefreshReason)
288
+ get(tableRef, otelContext)
333
289
  }
334
290
 
335
291
  span.setAttribute('sql.query', sqlString)
336
292
  span.updateName(`db:${sqlString.slice(0, 50)}`)
337
293
 
338
- const rawDbResults = store.sqliteDbWrapper.select<any>(
339
- sqlString,
340
- bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
341
- {
342
- queriedTables: queriedTablesRef.current,
343
- otelContext,
344
- },
345
- )
294
+ const rawDbResults = store.syncDbWrapper.select<any>(sqlString, {
295
+ queriedTables: queriedTablesRef.current,
296
+ bindValues: bindValues ? prepareBindValues(bindValues, sqlString) : undefined,
297
+ otelContext,
298
+ })
346
299
 
347
300
  span.setAttribute('sql.rowsCount', rawDbResults.length)
348
301
 
@@ -393,21 +346,10 @@ Result:`,
393
346
  }
394
347
 
395
348
  destroy = () => {
396
- this.isDestroyed = true
397
-
398
349
  if (this.queryInput$ !== undefined) {
399
350
  this.reactivityGraph.destroyNode(this.queryInput$)
400
351
  }
401
352
 
402
353
  this.reactivityGraph.destroyNode(this.results$)
403
-
404
- for (const query of this.dependencyQueriesRef) {
405
- query.deref()
406
- }
407
354
  }
408
355
  }
409
-
410
- const queryInfoFromQueryBuilder = (qb: QueryBuilder.Any): QueryInfo.Row | QueryInfo.None => {
411
- const ast = qb[QueryBuilderAstSymbol]
412
- return ast._tag === 'RowQuery' ? { _tag: 'Row', table: ast.tableDef, id: ast.id } : { _tag: 'None' }
413
- }
@@ -5,12 +5,13 @@ import { Schema, TreeFormatter } from '@livestore/utils/effect'
5
5
  import * as otel from '@opentelemetry/api'
6
6
  import * as graphql from 'graphql'
7
7
 
8
+ import { globalReactivityGraph } from '../global-state.js'
8
9
  import { isThunk, type Thunk } from '../reactive.js'
9
10
  import type { Store } from '../store/store.js'
10
11
  import type { BaseGraphQLContext, RefreshReason } from '../store/store-types.js'
11
12
  import { getDurationMsFromSpan } from '../utils/otel.js'
12
- import type { DepKey, GetAtomResult, LiveQueryDef, ReactivityGraph, ReactivityGraphContext } from './base-class.js'
13
- import { defCounterRef, depsToString, LiveStoreQueryBase, makeGetAtomResult, withRCMap } from './base-class.js'
13
+ import type { GetAtomResult, LiveQuery, QueryContext, ReactivityGraph } from './base-class.js'
14
+ import { LiveStoreQueryBase, makeGetAtomResult } from './base-class.js'
14
15
 
15
16
  export type MapResult<To, From> = ((res: From, get: GetAtomResult) => To) | Schema.Schema<To, From>
16
17
 
@@ -21,37 +22,17 @@ export const queryGraphQL = <
21
22
  >(
22
23
  document: DocumentNode<TResult, TVariableValues>,
23
24
  genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues),
24
- options: {
25
+ {
26
+ label,
27
+ reactivityGraph,
28
+ map,
29
+ }: {
25
30
  label?: string
26
- // reactivityGraph?: ReactivityGraph
31
+ reactivityGraph?: ReactivityGraph
27
32
  map?: MapResult<TResultMapped, TResult>
28
- deps?: DepKey
29
33
  } = {},
30
- ): LiveQueryDef<TResultMapped, QueryInfo.None> => {
31
- const documentName = graphql.getOperationAST(document)?.name?.value
32
- const hash = options.deps
33
- ? depsToString(options.deps)
34
- : (documentName ?? shouldNeverHappen('No document name found and no deps provided'))
35
- const label = options.label ?? documentName ?? 'graphql'
36
- const map = options.map
37
-
38
- return {
39
- _tag: 'def',
40
- id: ++defCounterRef.current,
41
- make: withRCMap(hash, (ctx, _otelContext) => {
42
- return new LiveStoreGraphQLQuery({
43
- document,
44
- genVariableValues,
45
- label,
46
- map,
47
- reactivityGraph: ctx.reactivityGraph.deref()!,
48
- })
49
- }),
50
- label,
51
- hash,
52
- queryInfo: { _tag: 'None' },
53
- }
54
- }
34
+ ): LiveQuery<TResultMapped, QueryInfo.None> =>
35
+ new LiveStoreGraphQLQuery({ document, genVariableValues, label, reactivityGraph, map })
55
36
 
56
37
  export class LiveStoreGraphQLQuery<
57
38
  TResult extends Record<string, any>,
@@ -65,13 +46,13 @@ export class LiveStoreGraphQLQuery<
65
46
  document: DocumentNode<TResult, TVariableValues>
66
47
 
67
48
  /** A reactive thunk representing the query results */
68
- results$: Thunk<TResultMapped, ReactivityGraphContext, RefreshReason>
49
+ results$: Thunk<TResultMapped, QueryContext, RefreshReason>
69
50
 
70
- variableValues$: Thunk<TVariableValues, ReactivityGraphContext, RefreshReason> | undefined
51
+ variableValues$: Thunk<TVariableValues, QueryContext, RefreshReason> | undefined
71
52
 
72
53
  label: string
73
54
 
74
- reactivityGraph: ReactivityGraph
55
+ protected reactivityGraph: ReactivityGraph
75
56
 
76
57
  queryInfo: QueryInfo.None = { _tag: 'None' }
77
58
 
@@ -87,7 +68,7 @@ export class LiveStoreGraphQLQuery<
87
68
  document: DocumentNode<TResult, TVariableValues>
88
69
  genVariableValues: TVariableValues | ((get: GetAtomResult) => TVariableValues)
89
70
  label?: string
90
- reactivityGraph: ReactivityGraph
71
+ reactivityGraph?: ReactivityGraph
91
72
  map?: MapResult<TResultMapped, TResult>
92
73
  }) {
93
74
  super()
@@ -97,7 +78,7 @@ export class LiveStoreGraphQLQuery<
97
78
  this.label = labelWithDefault
98
79
  this.document = document
99
80
 
100
- this.reactivityGraph = reactivityGraph
81
+ this.reactivityGraph = reactivityGraph ?? globalReactivityGraph
101
82
 
102
83
  this.mapResult =
103
84
  map === undefined
@@ -121,10 +102,8 @@ export class LiveStoreGraphQLQuery<
121
102
 
122
103
  if (typeof genVariableValues === 'function') {
123
104
  variableValues$OrvariableValues = this.reactivityGraph.makeThunk(
124
- (get, _setDebugInfo, ctx, otelContext) => {
125
- return genVariableValues(
126
- makeGetAtomResult(get, ctx, otelContext ?? ctx.rootOtelContext, this.dependencyQueriesRef),
127
- )
105
+ (get, _setDebugInfo, { rootOtelContext }, otelContext) => {
106
+ return genVariableValues(makeGetAtomResult(get, otelContext ?? rootOtelContext))
128
107
  },
129
108
  { label: `${labelWithDefault}:variableValues`, meta: { liveStoreThunkType: 'graphql.variables' } },
130
109
  )
@@ -135,10 +114,9 @@ export class LiveStoreGraphQLQuery<
135
114
 
136
115
  const resultsLabel = `${labelWithDefault}:results`
137
116
  this.results$ = this.reactivityGraph.makeThunk<TResultMapped>(
138
- (get, setDebugInfo, ctx, otelContext, debugRefreshReason) => {
139
- const { store, otelTracer, rootOtelContext } = ctx
117
+ (get, setDebugInfo, { store, otelTracer, rootOtelContext }, otelContext) => {
140
118
  const variableValues = isThunk(variableValues$OrvariableValues)
141
- ? (get(variableValues$OrvariableValues, otelContext, debugRefreshReason) as TVariableValues)
119
+ ? (get(variableValues$OrvariableValues) as TVariableValues)
142
120
  : (variableValues$OrvariableValues as TVariableValues)
143
121
  const { result, queriedTables, durationMs } = this.queryOnce({
144
122
  document,
@@ -146,7 +124,7 @@ export class LiveStoreGraphQLQuery<
146
124
  otelContext: otelContext ?? rootOtelContext,
147
125
  otelTracer,
148
126
  store: store as Store<TContext>,
149
- get: makeGetAtomResult(get, ctx, otelContext ?? rootOtelContext, this.dependencyQueriesRef),
127
+ get: makeGetAtomResult(get, otelContext ?? rootOtelContext),
150
128
  })
151
129
 
152
130
  // Add dependencies on any tables that were used
@@ -237,9 +215,5 @@ export class LiveStoreGraphQLQuery<
237
215
  }
238
216
 
239
217
  this.reactivityGraph.destroyNode(this.results$)
240
-
241
- for (const query of this.dependencyQueriesRef) {
242
- query.deref()
243
- }
244
218
  }
245
219
  }
package/src/reactive.ts CHANGED
@@ -32,11 +32,7 @@ import type * as otel from '@opentelemetry/api'
32
32
  export const NOT_REFRESHED_YET = Symbol.for('NOT_REFRESHED_YET')
33
33
  export type NOT_REFRESHED_YET = typeof NOT_REFRESHED_YET
34
34
 
35
- export type GetAtom = <T>(
36
- atom: Atom<T, any, any>,
37
- otelContext?: otel.Context | undefined,
38
- debugRefreshReason?: TODO | undefined,
39
- ) => T
35
+ export type GetAtom = <T>(atom: Atom<T, any, any>, otelContext?: otel.Context) => T
40
36
 
41
37
  export type Ref<T, TContext, TDebugRefreshReason extends DebugRefreshReason> = {
42
38
  _tag: 'ref'
@@ -46,7 +42,7 @@ export type Ref<T, TContext, TDebugRefreshReason extends DebugRefreshReason> = {
46
42
  previousResult: T
47
43
  computeResult: () => T
48
44
  sub: Set<Atom<any, TContext, TDebugRefreshReason>> // always empty
49
- super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>>
45
+ super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect>
50
46
  label?: string
51
47
  /** Container for meta information (e.g. the LiveStore Store) */
52
48
  meta?: any
@@ -62,7 +58,7 @@ export type Thunk<TResult, TContext, TDebugRefreshReason extends DebugRefreshRea
62
58
  computeResult: (otelContext?: otel.Context, debugRefreshReason?: TDebugRefreshReason) => TResult
63
59
  previousResult: TResult | NOT_REFRESHED_YET
64
60
  sub: Set<Atom<any, TContext, TDebugRefreshReason>>
65
- super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>>
61
+ super: Set<Thunk<any, TContext, TDebugRefreshReason> | Effect>
66
62
  label?: string
67
63
  /** Container for meta information (e.g. the LiveStore Store) */
68
64
  meta?: any
@@ -76,11 +72,11 @@ export type Atom<T, TContext, TDebugRefreshReason extends DebugRefreshReason> =
76
72
  | Ref<T, TContext, TDebugRefreshReason>
77
73
  | Thunk<T, TContext, TDebugRefreshReason>
78
74
 
79
- export type Effect<TDebugRefreshReason extends DebugRefreshReason> = {
75
+ export type Effect = {
80
76
  _tag: 'effect'
81
77
  id: string
82
78
  isDestroyed: boolean
83
- doEffect: (otelContext?: otel.Context | undefined, debugRefreshReason?: TDebugRefreshReason | undefined) => void
79
+ doEffect: (otelContext?: otel.Context) => void
84
80
  sub: Set<Atom<any, TODO, TODO>>
85
81
  label?: string
86
82
  invocations: number
@@ -88,7 +84,7 @@ export type Effect<TDebugRefreshReason extends DebugRefreshReason> = {
88
84
 
89
85
  export type Node<T, TContext, TDebugRefreshReason extends DebugRefreshReason> =
90
86
  | Atom<T, TContext, TDebugRefreshReason>
91
- | Effect<TDebugRefreshReason>
87
+ | Effect
92
88
 
93
89
  export const isThunk = <T, TContext, TDebugRefreshReason extends DebugRefreshReason>(
94
90
  obj: unknown,
@@ -170,7 +166,7 @@ export type SerializedThunk = Readonly<
170
166
 
171
167
  export type SerializedEffect = Readonly<
172
168
  PrettifyFlat<
173
- Pick<Effect<any>, '_tag' | 'id' | 'label' | 'invocations' | 'isDestroyed'> & {
169
+ Pick<Effect, '_tag' | 'id' | 'label' | 'invocations' | 'isDestroyed'> & {
174
170
  sub: ReadonlyArray<string>
175
171
  }
176
172
  >
@@ -191,13 +187,6 @@ const uniqueRefreshInfoId = () => `refresh-info-${++refreshInfoIdCounter}`
191
187
  let globalGraphIdCounter = 0
192
188
  const uniqueGraphId = () => `graph-${++globalGraphIdCounter}`
193
189
 
194
- /** Used for testing */
195
- export const __resetIds = () => {
196
- nodeIdCounter = 0
197
- refreshInfoIdCounter = 0
198
- globalGraphIdCounter = 0
199
- }
200
-
201
190
  export class ReactiveGraph<
202
191
  TDebugRefreshReason extends DebugRefreshReason,
203
192
  TDebugThunkInfo extends DebugThunkInfo,
@@ -206,7 +195,7 @@ export class ReactiveGraph<
206
195
  id = uniqueGraphId()
207
196
 
208
197
  readonly atoms: Set<Atom<any, TContext, TDebugRefreshReason>> = new Set()
209
- readonly effects: Set<Effect<TDebugRefreshReason>> = new Set()
198
+ readonly effects: Set<Effect> = new Set()
210
199
 
211
200
  context: TContext | undefined
212
201
 
@@ -216,7 +205,7 @@ export class ReactiveGraph<
216
205
  | { refreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[]; startMs: DOMHighResTimeStamp }
217
206
  | undefined
218
207
 
219
- private deferredEffects: Map<Effect<TDebugRefreshReason>, Set<TDebugRefreshReason>> = new Map()
208
+ private deferredEffects: Map<Effect, Set<TDebugRefreshReason>> = new Map()
220
209
 
221
210
  private refreshCallbacks: Set<() => void> = new Set()
222
211
 
@@ -250,7 +239,6 @@ export class ReactiveGraph<
250
239
  setDebugInfo: (debugInfo: TDebugThunkInfo) => void,
251
240
  ctx: TContext,
252
241
  otelContext: otel.Context | undefined,
253
- debugRefreshReason: TDebugRefreshReason | undefined,
254
242
  ) => T,
255
243
  options?:
256
244
  | {
@@ -278,7 +266,7 @@ export class ReactiveGraph<
278
266
 
279
267
  const getAtom = (atom: Atom<T, TContext, TDebugRefreshReason>, otelContext: otel.Context) => {
280
268
  this.addEdge(thunk, atom)
281
- return compute(atom, otelContext, debugRefreshReason)
269
+ return compute(atom, otelContext)
282
270
  }
283
271
 
284
272
  let debugInfo: TDebugThunkInfo | undefined = undefined
@@ -291,7 +279,6 @@ export class ReactiveGraph<
291
279
  setDebugInfo,
292
280
  this.context ?? throwContextNotSetError(this),
293
281
  otelContext,
294
- debugRefreshReason,
295
282
  )
296
283
 
297
284
  const resultChanged = thunk.equal(thunk.previousResult as T, result) === false
@@ -378,18 +365,14 @@ export class ReactiveGraph<
378
365
  }
379
366
 
380
367
  makeEffect(
381
- doEffect: (
382
- get: GetAtom,
383
- otelContext: otel.Context | undefined,
384
- debugRefreshReason: DebugRefreshReason | undefined,
385
- ) => void,
368
+ doEffect: (get: GetAtom, otelContext?: otel.Context) => void,
386
369
  options?: { label?: string } | undefined,
387
- ): Effect<TDebugRefreshReason> {
388
- const effect: Effect<TDebugRefreshReason> = {
370
+ ): Effect {
371
+ const effect: Effect = {
389
372
  _tag: 'effect',
390
373
  id: uniqueNodeId(),
391
374
  isDestroyed: false,
392
- doEffect: (otelContext, debugRefreshReason) => {
375
+ doEffect: (otelContext) => {
393
376
  effect.invocations++
394
377
 
395
378
  // NOTE we're not tracking any debug refresh info for effects as they're tracked by the thunks they depend on
@@ -397,16 +380,12 @@ export class ReactiveGraph<
397
380
  // Reset previous subcomputations as we're about to re-add them as part of the `doEffect` call below
398
381
  effect.sub = new Set()
399
382
 
400
- const getAtom = (
401
- atom: Atom<any, TContext, TDebugRefreshReason>,
402
- otelContext: otel.Context,
403
- debugRefreshReason: DebugRefreshReason | undefined,
404
- ) => {
383
+ const getAtom = (atom: Atom<any, TContext, TDebugRefreshReason>, otelContext: otel.Context) => {
405
384
  this.addEdge(effect, atom)
406
- return compute(atom, otelContext, debugRefreshReason)
385
+ return compute(atom, otelContext)
407
386
  }
408
387
 
409
- doEffect(getAtom as GetAtom, otelContext, debugRefreshReason)
388
+ doEffect(getAtom as GetAtom, otelContext)
410
389
  },
411
390
  sub: new Set(),
412
391
  label: options?.label,
@@ -442,7 +421,7 @@ export class ReactiveGraph<
442
421
  }
443
422
  | undefined,
444
423
  ) {
445
- const effectsToRefresh = new Set<Effect<TDebugRefreshReason>>()
424
+ const effectsToRefresh = new Set<Effect>()
446
425
  for (const [ref, val] of refs) {
447
426
  ref.previousResult = val
448
427
  ref.refreshes++
@@ -469,7 +448,7 @@ export class ReactiveGraph<
469
448
  }
470
449
 
471
450
  private runEffects = (
472
- effectsToRefresh: Set<Effect<TDebugRefreshReason>>,
451
+ effectsToRefresh: Set<Effect>,
473
452
  options: {
474
453
  debugRefreshReason: TDebugRefreshReason
475
454
  otelContext?: otel.Context
@@ -480,7 +459,7 @@ export class ReactiveGraph<
480
459
  this.currentDebugRefresh = { refreshedAtoms: [], startMs: performance.now() }
481
460
 
482
461
  for (const effect of effectsToRefresh) {
483
- effect.doEffect(options?.otelContext, options.debugRefreshReason)
462
+ effect.doEffect(options?.otelContext)
484
463
  }
485
464
 
486
465
  const refreshedAtoms = this.currentDebugRefresh.refreshedAtoms
@@ -525,7 +504,7 @@ export class ReactiveGraph<
525
504
  }
526
505
 
527
506
  addEdge(
528
- superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>,
507
+ superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect,
529
508
  subComp: Atom<any, TContext, TDebugRefreshReason>,
530
509
  ) {
531
510
  superComp.sub.add(subComp)
@@ -537,11 +516,11 @@ export class ReactiveGraph<
537
516
  }
538
517
 
539
518
  removeEdge(
540
- superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect<TDebugRefreshReason>,
519
+ superComp: Thunk<any, TContext, TDebugRefreshReason> | Effect,
541
520
  subComp: Atom<any, TContext, TDebugRefreshReason>,
542
521
  ) {
543
522
  superComp.sub.delete(subComp)
544
- const effectsToRefresh = new Set<Effect<TDebugRefreshReason>>()
523
+ const effectsToRefresh = new Set<Effect>()
545
524
  markSuperCompDirtyRec(subComp, effectsToRefresh)
546
525
 
547
526
  for (const effect of effectsToRefresh) {
@@ -584,11 +563,7 @@ export class ReactiveGraph<
584
563
  }
585
564
  }
586
565
 
587
- const compute = <T>(
588
- atom: Atom<T, unknown, any>,
589
- otelContext: otel.Context,
590
- debugRefreshReason: DebugRefreshReason | undefined,
591
- ): T => {
566
+ const compute = <T>(atom: Atom<T, unknown, any>, otelContext: otel.Context): T => {
592
567
  // const __getResult = atom._tag === 'thunk' ? atom.__getResult.toString() : ''
593
568
  if (atom.isDestroyed) {
594
569
  shouldNeverHappen(`LiveStore Error: Attempted to compute destroyed ${atom._tag} (${atom.id}): ${atom.label ?? ''}`)
@@ -596,7 +571,7 @@ const compute = <T>(
596
571
 
597
572
  if (atom.isDirty) {
598
573
  // console.log('atom is dirty', atom.id, atom.label ?? '', atom._tag, __getResult)
599
- const result = atom.computeResult(otelContext, debugRefreshReason)
574
+ const result = atom.computeResult(otelContext)
600
575
  atom.isDirty = false
601
576
  atom.previousResult = result
602
577
  return result
@@ -606,7 +581,7 @@ const compute = <T>(
606
581
  }
607
582
  }
608
583
 
609
- const markSuperCompDirtyRec = <T>(atom: Atom<T, unknown, any>, effectsToRefresh: Set<Effect<any>>) => {
584
+ const markSuperCompDirtyRec = <T>(atom: Atom<T, unknown, any>, effectsToRefresh: Set<Effect>) => {
610
585
  for (const superComp of atom.super) {
611
586
  if (superComp._tag === 'thunk') {
612
587
  superComp.isDirty = true
@@ -669,7 +644,7 @@ const serializeAtom = (atom: Atom<any, unknown, any>, includeResult: boolean): S
669
644
  }
670
645
 
671
646
  // NOTE This function is performance-optimized (i.e. not using `pick` and `Array.from`)
672
- const serializeEffect = (effect: Effect<any>): SerializedEffect => {
647
+ const serializeEffect = (effect: Effect): SerializedEffect => {
673
648
  const sub: string[] = []
674
649
  for (const a of effect.sub) {
675
650
  sub.push(a.id)