@livestore/livestore 0.0.19 → 0.0.22

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 (136) hide show
  1. package/README.md +29 -22
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/QueryCache.d.ts +1 -1
  4. package/dist/QueryCache.d.ts.map +1 -1
  5. package/dist/QueryCache.js.map +1 -1
  6. package/dist/__tests__/react/fixture.d.ts +5 -4
  7. package/dist/__tests__/react/fixture.d.ts.map +1 -1
  8. package/dist/__tests__/react/fixture.js +3 -5
  9. package/dist/__tests__/react/fixture.js.map +1 -1
  10. package/dist/__tests__/react/useComponentState.test.d.ts +2 -0
  11. package/dist/__tests__/react/useComponentState.test.d.ts.map +1 -0
  12. package/dist/__tests__/react/useComponentState.test.js +68 -0
  13. package/dist/__tests__/react/useComponentState.test.js.map +1 -0
  14. package/dist/__tests__/react/useLQuery.test.d.ts +2 -0
  15. package/dist/__tests__/react/useLQuery.test.d.ts.map +1 -0
  16. package/dist/__tests__/react/useLQuery.test.js +38 -0
  17. package/dist/__tests__/react/useLQuery.test.js.map +1 -0
  18. package/dist/__tests__/react/useLiveStoreComponent.test.js +4 -9
  19. package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -1
  20. package/dist/__tests__/react/useQuery.test.d.ts +2 -0
  21. package/dist/__tests__/react/useQuery.test.d.ts.map +1 -0
  22. package/dist/__tests__/react/useQuery.test.js +33 -0
  23. package/dist/__tests__/react/useQuery.test.js.map +1 -0
  24. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +2 -0
  25. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +1 -0
  26. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +38 -0
  27. package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +1 -0
  28. package/dist/__tests__/react/utils/stack-info.test.d.ts +2 -0
  29. package/dist/__tests__/react/utils/stack-info.test.d.ts.map +1 -0
  30. package/dist/__tests__/react/utils/stack-info.test.js +43 -0
  31. package/dist/__tests__/react/utils/stack-info.test.js.map +1 -0
  32. package/dist/__tests__/reactive.test.js +179 -93
  33. package/dist/__tests__/reactive.test.js.map +1 -1
  34. package/dist/__tests__/reactiveQueries/sql.test.d.ts +2 -0
  35. package/dist/__tests__/reactiveQueries/sql.test.d.ts.map +1 -0
  36. package/dist/__tests__/reactiveQueries/sql.test.js +337 -0
  37. package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -0
  38. package/dist/inMemoryDatabase.d.ts +4 -3
  39. package/dist/inMemoryDatabase.d.ts.map +1 -1
  40. package/dist/inMemoryDatabase.js +3 -2
  41. package/dist/inMemoryDatabase.js.map +1 -1
  42. package/dist/index.d.ts +7 -5
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +4 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/react/index.d.ts +4 -3
  47. package/dist/react/index.d.ts.map +1 -1
  48. package/dist/react/index.js +3 -2
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/react/useComponentState.d.ts +50 -0
  51. package/dist/react/useComponentState.d.ts.map +1 -0
  52. package/dist/react/useComponentState.js +240 -0
  53. package/dist/react/useComponentState.js.map +1 -0
  54. package/dist/react/useGlobalQuery.d.ts +3 -0
  55. package/dist/react/useGlobalQuery.d.ts.map +1 -0
  56. package/dist/react/useGlobalQuery.js +26 -0
  57. package/dist/react/useGlobalQuery.js.map +1 -0
  58. package/dist/react/useGraphQL.d.ts +3 -3
  59. package/dist/react/useGraphQL.d.ts.map +1 -1
  60. package/dist/react/useGraphQL.js +10 -8
  61. package/dist/react/useGraphQL.js.map +1 -1
  62. package/dist/react/useLiveStoreComponent.d.ts +6 -6
  63. package/dist/react/useLiveStoreComponent.d.ts.map +1 -1
  64. package/dist/react/useLiveStoreComponent.js +143 -99
  65. package/dist/react/useLiveStoreComponent.js.map +1 -1
  66. package/dist/react/useQuery.d.ts +2 -2
  67. package/dist/react/useQuery.d.ts.map +1 -1
  68. package/dist/react/useQuery.js +54 -30
  69. package/dist/react/useQuery.js.map +1 -1
  70. package/dist/react/useTemporaryQuery.d.ts +8 -0
  71. package/dist/react/useTemporaryQuery.d.ts.map +1 -0
  72. package/dist/react/useTemporaryQuery.js +19 -0
  73. package/dist/react/useTemporaryQuery.js.map +1 -0
  74. package/dist/react/utils/extractNamesFromStackTrace.d.ts +3 -0
  75. package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +1 -0
  76. package/dist/react/utils/extractNamesFromStackTrace.js +40 -0
  77. package/dist/react/utils/extractNamesFromStackTrace.js.map +1 -0
  78. package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +7 -0
  79. package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +1 -0
  80. package/dist/react/utils/extractStackInfoFromStackTrace.js +40 -0
  81. package/dist/react/utils/extractStackInfoFromStackTrace.js.map +1 -0
  82. package/dist/react/utils/stack-info.d.ts +11 -0
  83. package/dist/react/utils/stack-info.d.ts.map +1 -0
  84. package/dist/react/utils/stack-info.js +49 -0
  85. package/dist/react/utils/stack-info.js.map +1 -0
  86. package/dist/reactive.d.ts +51 -67
  87. package/dist/reactive.d.ts.map +1 -1
  88. package/dist/reactive.js +138 -220
  89. package/dist/reactive.js.map +1 -1
  90. package/dist/reactiveQueries/base-class.d.ts +28 -21
  91. package/dist/reactiveQueries/base-class.d.ts.map +1 -1
  92. package/dist/reactiveQueries/base-class.js +22 -18
  93. package/dist/reactiveQueries/base-class.js.map +1 -1
  94. package/dist/reactiveQueries/graph.d.ts +10 -0
  95. package/dist/reactiveQueries/graph.d.ts.map +1 -0
  96. package/dist/reactiveQueries/graph.js +6 -0
  97. package/dist/reactiveQueries/graph.js.map +1 -0
  98. package/dist/reactiveQueries/graphql.d.ts +35 -17
  99. package/dist/reactiveQueries/graphql.d.ts.map +1 -1
  100. package/dist/reactiveQueries/graphql.js +86 -10
  101. package/dist/reactiveQueries/graphql.js.map +1 -1
  102. package/dist/reactiveQueries/js.d.ts +17 -12
  103. package/dist/reactiveQueries/js.d.ts.map +1 -1
  104. package/dist/reactiveQueries/js.js +30 -8
  105. package/dist/reactiveQueries/js.js.map +1 -1
  106. package/dist/reactiveQueries/sql.d.ts +28 -18
  107. package/dist/reactiveQueries/sql.d.ts.map +1 -1
  108. package/dist/reactiveQueries/sql.js +79 -16
  109. package/dist/reactiveQueries/sql.js.map +1 -1
  110. package/dist/store.d.ts +35 -61
  111. package/dist/store.d.ts.map +1 -1
  112. package/dist/store.js +77 -272
  113. package/dist/store.js.map +1 -1
  114. package/package.json +4 -3
  115. package/src/QueryCache.ts +1 -1
  116. package/src/__tests__/react/fixture.tsx +10 -8
  117. package/src/__tests__/react/{useLiveStoreComponent.test.tsx → useComponentState.test.tsx} +9 -20
  118. package/src/__tests__/react/useQuery.test.tsx +48 -0
  119. package/src/__tests__/react/utils/stack-info.test.ts +45 -0
  120. package/src/__tests__/reactive.test.ts +212 -140
  121. package/src/__tests__/reactiveQueries/sql.test.ts +372 -0
  122. package/src/inMemoryDatabase.ts +11 -8
  123. package/src/index.ts +7 -11
  124. package/src/react/index.ts +4 -7
  125. package/src/react/{useLiveStoreComponent.ts → useComponentState.ts} +90 -253
  126. package/src/react/useQuery.ts +74 -40
  127. package/src/react/useTemporaryQuery.ts +23 -0
  128. package/src/react/utils/stack-info.ts +63 -0
  129. package/src/reactive.ts +234 -308
  130. package/src/reactiveQueries/base-class.ts +59 -42
  131. package/src/reactiveQueries/graph.ts +15 -0
  132. package/src/reactiveQueries/graphql.ts +143 -29
  133. package/src/reactiveQueries/js.ts +57 -20
  134. package/src/reactiveQueries/sql.ts +136 -36
  135. package/src/store.ts +121 -426
  136. package/src/react/useGraphQL.ts +0 -138
@@ -1,27 +1,23 @@
1
- import { makeNoopTracer } from '@livestore/utils'
2
- import * as otel from '@opentelemetry/api'
3
1
  import { describe, expect, it } from 'vitest'
4
2
 
5
3
  import { ReactiveGraph } from '../reactive.js'
6
4
 
7
- const mockOtelCtx = otel.context.active()
8
-
9
5
  describe('a trivial graph', () => {
10
6
  const makeGraph = () => {
11
- const graph = new ReactiveGraph({ otelTracer: makeNoopTracer() })
12
- const a = graph.makeRef(1)
13
- const b = graph.makeRef(2)
7
+ const graph = new ReactiveGraph({})
8
+ graph.context = {}
9
+ const a = graph.makeRef(1, { label: 'a' })
10
+ const b = graph.makeRef(2, { label: 'b' })
14
11
  const numberOfRunsForC = { runs: 0 }
15
12
  const c = graph.makeThunk(
16
13
  (get) => {
17
14
  numberOfRunsForC.runs++
18
15
  return get(a) + get(b)
19
16
  },
20
- undefined,
21
- mockOtelCtx,
17
+ { label: 'c' },
22
18
  )
23
- const d = graph.makeRef(3)
24
- const e = graph.makeThunk((get) => get(c) + get(d), undefined, mockOtelCtx)
19
+ const d = graph.makeRef(3, { label: 'd' })
20
+ const e = graph.makeThunk((get) => get(c) + get(d), { label: 'e' })
25
21
 
26
22
  // a(1) b(2)
27
23
  // \ /
@@ -34,163 +30,225 @@ describe('a trivial graph', () => {
34
30
  // \ \
35
31
  // e = c + d
36
32
 
33
+ expect(graph.atoms.size).toBe(5)
34
+
37
35
  return { graph, a, b, c, d, e, numberOfRunsForC }
38
36
  }
39
37
 
40
38
  it('has the right initial values', () => {
41
39
  const { c, e } = makeGraph()
42
- expect(c.result).toBe(3)
43
- expect(e.result).toBe(6)
40
+ expect(c.computeResult()).toBe(3)
41
+ expect(e.computeResult()).toBe(6)
44
42
  })
45
43
 
46
44
  it('propagates change through the graph', () => {
47
45
  const { graph, a, c, e } = makeGraph()
48
- graph.setRef(a, 5, undefined, mockOtelCtx)
49
- expect(c.result).toBe(7)
50
- expect(e.result).toBe(10)
46
+ graph.setRef(a, 5)
47
+ expect(c.computeResult()).toBe(7)
48
+ expect(e.computeResult()).toBe(10)
49
+ })
50
+
51
+ it('does not rerun downstream computations eagerly when an upstream dep changes', () => {
52
+ const { graph, a, c, numberOfRunsForC } = makeGraph()
53
+ expect(numberOfRunsForC.runs).toBe(0)
54
+ graph.setRef(a, 5)
55
+ expect(numberOfRunsForC.runs).toBe(0)
56
+ c.computeResult()
57
+ expect(numberOfRunsForC.runs).toBe(1)
58
+ })
59
+
60
+ it('does not rerun c when d is edited and e is rerun', () => {
61
+ const { graph, d, e, numberOfRunsForC } = makeGraph()
62
+ expect(numberOfRunsForC.runs).toBe(0)
63
+ expect(e.computeResult()).toBe(3 + 3)
64
+ expect(numberOfRunsForC.runs).toBe(1)
65
+ graph.setRef(d, 4)
66
+ expect(e.computeResult()).toBe(4 + 3)
67
+ expect(numberOfRunsForC.runs).toBe(1)
51
68
  })
52
69
 
53
70
  it('cuts off reactive propagation when a thunk evaluates to same result as before', () => {
54
71
  const { graph, a, c, d } = makeGraph()
55
72
 
56
73
  let numberOfRuns = 0
57
- const f = graph.makeThunk(
58
- (get) => {
59
- numberOfRuns++
60
- return get(c) + get(d)
61
- },
62
- undefined,
63
- mockOtelCtx,
64
- )
65
- expect(numberOfRuns).toBe(1) // initializing f should run it once
74
+ const f = graph.makeThunk((get) => {
75
+ numberOfRuns++
76
+ return get(c) + get(d)
77
+ })
78
+ expect(numberOfRuns).toBe(0) // defining f shouldn't run it yet
79
+ f.computeResult()
80
+ expect(numberOfRuns).toBe(1) // refreshing should run it once
66
81
 
67
82
  // f doesn't run because a is set to same value as before
68
- graph.setRef(a, 1, undefined, mockOtelCtx)
69
- expect(f.result).toBe(6)
70
- expect(numberOfRuns).toBe(1)
83
+ graph.setRef(a, 1)
84
+ expect(f.computeResult()).toBe(6)
85
+ // expect(numberOfRuns).toBe(1) // TODO comp caching
71
86
 
72
87
  // f runs because a is set to a different value
73
- graph.setRef(a, 5, undefined, mockOtelCtx)
74
- expect(f.result).toBe(10)
75
- expect(numberOfRuns).toBe(2)
88
+ graph.setRef(a, 5)
89
+ expect(f.computeResult()).toBe(10)
90
+ // expect(numberOfRuns).toBe(2) // TODO comp caching
76
91
 
77
92
  // f runs again when d is set to a different value
78
- graph.setRef(d, 4, undefined, mockOtelCtx)
79
- expect(f.result).toBe(11)
80
- expect(numberOfRuns).toBe(3)
93
+ graph.setRef(d, 4)
94
+ expect(f.computeResult()).toBe(11)
95
+ // expect(numberOfRuns).toBe(3) // TODO comp caching
81
96
 
82
97
  // f only runs one time if we set two refs together
83
- graph.setRefs(
84
- [
85
- [a, 6],
86
- [d, 5],
87
- ],
88
- undefined,
89
- mockOtelCtx,
90
- )
91
- expect(f.result).toBe(13)
92
- expect(numberOfRuns).toBe(4)
98
+ graph.setRefs([
99
+ [a, 6],
100
+ [d, 5],
101
+ ])
102
+ expect(f.computeResult()).toBe(13)
103
+ // expect(numberOfRuns).toBe(4) // TODO comp caching
93
104
  })
94
105
 
95
106
  it('only runs a thunk once when two upstream refs are updated together', () => {
96
107
  const { graph, a, b, c, numberOfRunsForC } = makeGraph()
108
+ graph.setRefs([
109
+ [a, 5],
110
+ [b, 6],
111
+ ])
112
+ expect(numberOfRunsForC.runs).toBe(0)
113
+ expect(c.computeResult()).toBe(11)
97
114
  expect(numberOfRunsForC.runs).toBe(1)
98
- graph.setRefs(
99
- [
100
- [a, 5],
101
- [b, 6],
102
- ],
103
- undefined,
104
- mockOtelCtx,
105
- )
106
- expect(numberOfRunsForC.runs).toBe(2)
107
- expect(c.result).toBe(11)
108
115
  })
109
116
 
110
- it('skips refresh when that option is passed when setting a single ref', () => {
111
- const { graph, a, c, numberOfRunsForC } = makeGraph()
112
- expect(numberOfRunsForC.runs).toBe(1)
117
+ describe('effects', () => {
118
+ // TODO TBD whether we want to keep this as intended behavior
119
+ it(`doesn't run on initial definition`, () => {
120
+ const { graph, c, numberOfRunsForC } = makeGraph()
121
+ expect(numberOfRunsForC.runs).toBe(0)
122
+ c.computeResult()
123
+ expect(numberOfRunsForC.runs).toBe(1)
124
+
125
+ let numberOfEffectRuns = 0
126
+ const effect = graph.makeEffect((get) => {
127
+ // establish a dependency on thunk c and mutate an outside value
128
+ expect(get(c)).toBe(3)
129
+ numberOfEffectRuns++
130
+ })
131
+ expect(numberOfEffectRuns).toBe(0)
132
+ expect(numberOfRunsForC.runs).toBe(1)
133
+
134
+ effect.doEffect()
135
+ expect(numberOfEffectRuns).toBe(1)
136
+ })
113
137
 
114
- graph.setRef(a, 5, { skipRefresh: true }, mockOtelCtx)
138
+ it('only reruns an effect if the thunk value changed', () => {
139
+ const { graph, a, c } = makeGraph()
140
+ let numberOfEffectRuns = 0
141
+ let aHasChanged = true
142
+ expect(numberOfEffectRuns).toBe(0)
143
+ const effect = graph.makeEffect((get) => {
144
+ // establish a dependency on thunk c and mutate an outside value
145
+ expect(get(c)).toBe(aHasChanged ? 3 : 4)
146
+ numberOfEffectRuns++
147
+ })
148
+
149
+ expect(numberOfEffectRuns).toBe(0)
150
+ effect.doEffect()
151
+ expect(numberOfEffectRuns).toBe(1)
115
152
 
116
- // C hasn't changed
117
- expect(numberOfRunsForC.runs).toBe(1)
118
- expect(c.result).toBe(3)
153
+ // if we set a to the same value, the effect should not run again
154
+ graph.setRef(a, 1)
155
+ // expect(numberOfCallsToC).toBe(1) // TODO comp caching
156
+
157
+ aHasChanged = false
119
158
 
120
- // Now we trigger a refresh and everything runs
121
- graph.refresh(undefined, mockOtelCtx)
122
- expect(numberOfRunsForC.runs).toBe(2)
123
- expect(c.result).toBe(7)
159
+ graph.setRef(a, 2)
160
+ // expect(numberOfCallsToC).toBe(2) // TODO comp caching
161
+ })
124
162
  })
163
+ })
125
164
 
126
- it('skips refresh when that option is passed when setting multiple refs together', () => {
127
- const { graph, a, b, c, numberOfRunsForC } = makeGraph()
128
- expect(numberOfRunsForC.runs).toBe(1)
165
+ describe('a dynamic graph', () => {
166
+ const makeGraph = () => {
167
+ const graph = new ReactiveGraph({})
168
+ graph.context = {}
129
169
 
130
- graph.setRefs(
131
- [
132
- [a, 5],
133
- [b, 6],
134
- ],
135
- { skipRefresh: true },
136
- mockOtelCtx,
170
+ const a = graph.makeRef(1, { label: 'a' })
171
+ const b = graph.makeRef(2, { label: 'b' })
172
+ const c = graph.makeRef<'a' | 'b'>('a', { label: 'c' })
173
+ const numberOfRunsForD = { runs: 0 }
174
+ const d = graph.makeThunk(
175
+ (get) => {
176
+ numberOfRunsForD.runs++
177
+ return get(c) === 'a' ? get(a) : get(b)
178
+ },
179
+ { label: 'd' },
137
180
  )
181
+ const e = graph.makeRef(2, { label: 'e' })
182
+ const f = graph.makeThunk((get) => get(d) * get(e), { label: 'f' })
138
183
 
139
- // C hasn't changed
140
- expect(numberOfRunsForC.runs).toBe(1)
141
- expect(c.result).toBe(3)
184
+ // a(1) b(2) c('a')
185
+ // \ / /
186
+ // \ / /
187
+ // d = a or b depending on c
188
+ // \
189
+ // \
190
+ // e(2) \
191
+ // \ \
192
+ // \ \
193
+ // f = d * e
142
194
 
143
- // Now we trigger a refresh and everything runs
144
- graph.refresh(undefined, mockOtelCtx)
145
- expect(numberOfRunsForC.runs).toBe(2)
146
- expect(c.result).toBe(11)
147
- })
195
+ expect(graph.atoms.size).toBe(6)
148
196
 
149
- describe('effects', () => {
150
- it('only reruns an effect if the thunk value changed', () => {
151
- const { graph, a, c } = makeGraph()
152
- let numberOfCallsToC = 0
153
- graph.makeEffect(
154
- (get) => {
155
- // establish a dependency on thunk c and mutate an outside value
156
- get(c)
157
- numberOfCallsToC++
158
- },
159
- undefined,
160
- mockOtelCtx,
161
- )
162
- expect(numberOfCallsToC).toBe(1)
197
+ return { graph, a, b, c, d, e, f, numberOfRunsForD }
198
+ }
163
199
 
164
- // if we set a to the same value, the effect should not run again
165
- graph.setRef(a, 1, undefined, mockOtelCtx)
166
- expect(numberOfCallsToC).toBe(1)
200
+ it('has the right initial values', () => {
201
+ const { d, f } = makeGraph()
202
+ expect(d.computeResult()).toBe(1)
203
+ expect(f.computeResult()).toBe(2)
204
+ })
167
205
 
168
- graph.setRef(a, 2, undefined, mockOtelCtx)
169
- expect(numberOfCallsToC).toBe(2)
170
- })
206
+ it('dynamically adjusts d when a, b or c changes', () => {
207
+ const { graph, c, d, e, f, numberOfRunsForD } = makeGraph()
208
+ expect(numberOfRunsForD.runs).toBe(0)
209
+ expect(d.computeResult()).toBe(1)
210
+ expect(f.computeResult()).toBe(2)
211
+ expect(numberOfRunsForD.runs).toBe(1)
212
+ graph.setRef(c, 'b')
213
+ expect(d.computeResult()).toBe(2)
214
+ expect(f.computeResult()).toBe(4)
215
+ expect(numberOfRunsForD.runs).toBe(2)
216
+ graph.setRef(e, 3)
217
+ expect(f.computeResult()).toBe(6)
218
+ expect(numberOfRunsForD.runs).toBe(2)
219
+ })
220
+
221
+ it('runs d only when a changes, not b', () => {
222
+ const { graph, a, b, d, numberOfRunsForD } = makeGraph()
223
+ numberOfRunsForD.runs = 0
224
+ d.computeResult()
225
+ expect(numberOfRunsForD.runs).toBe(1)
226
+ graph.setRef(a, 3)
227
+ expect(d.computeResult()).toBe(3)
228
+ expect(numberOfRunsForD.runs).toBe(2)
229
+ graph.setRef(b, 4)
230
+ expect(d.computeResult()).toBe(3)
231
+ expect(numberOfRunsForD.runs).toBe(2)
171
232
  })
172
233
  })
173
234
 
174
235
  describe('a diamond shaped graph', () => {
175
236
  const makeGraph = () => {
176
- const graph = new ReactiveGraph({ otelTracer: makeNoopTracer() })
237
+ const graph = new ReactiveGraph({})
238
+ graph.context = {}
177
239
  const a = graph.makeRef(1)
178
- const b = graph.makeThunk((get) => get(a) + 1, undefined, mockOtelCtx)
179
- const c = graph.makeThunk((get) => get(a) + 1, undefined, mockOtelCtx)
240
+ const b = graph.makeThunk((get) => get(a) + 1)
241
+ const c = graph.makeThunk((get) => get(a) + 1)
180
242
 
181
243
  // track the number of times d has run in an object so we can mutate it
182
244
  const dRuns = { runs: 0 }
183
245
 
184
246
  // normally thunks aren't supposed to side effect;
185
247
  // we do it here to track the number of times d has run
186
- const d = graph.makeThunk(
187
- (get) => {
188
- dRuns.runs++
189
- return get(b) + get(c)
190
- },
191
- undefined,
192
- mockOtelCtx,
193
- )
248
+ const d = graph.makeThunk((get) => {
249
+ dRuns.runs++
250
+ return get(b) + get(c)
251
+ })
194
252
 
195
253
  // a(1)
196
254
  // / \
@@ -198,52 +256,53 @@ describe('a diamond shaped graph', () => {
198
256
  // \ /
199
257
  // d = b + c
200
258
 
259
+ expect(graph.atoms.size).toBe(4)
260
+
201
261
  return { graph, a, b, c, d, dRuns }
202
262
  }
203
263
 
204
264
  it('has the right initial values', () => {
205
265
  const { b, c, d } = makeGraph()
206
- expect(b.result).toBe(2)
207
- expect(c.result).toBe(2)
208
- expect(d.result).toBe(4)
266
+ expect(b.computeResult()).toBe(2)
267
+ expect(c.computeResult()).toBe(2)
268
+ expect(d.computeResult()).toBe(4)
209
269
  })
210
270
 
211
271
  it('propagates change through the graph', () => {
212
272
  const { graph, a, b, c, d } = makeGraph()
213
- graph.setRef(a, 5, undefined, mockOtelCtx)
214
- expect(b.result).toBe(6)
215
- expect(c.result).toBe(6)
216
- expect(d.result).toBe(12)
273
+ graph.setRef(a, 5)
274
+ expect(b.computeResult()).toBe(6)
275
+ expect(c.computeResult()).toBe(6)
276
+ expect(d.computeResult()).toBe(12)
217
277
  })
218
278
 
219
279
  // if we're being efficient, we should update b and c before updating d,
220
280
  // so d only needs to update one time
221
281
  it('only runs d once when a changes', () => {
222
- const { graph, a, dRuns } = makeGraph()
282
+ const { graph, a, d, dRuns } = makeGraph()
283
+ expect(dRuns.runs).toBe(0)
284
+ d.computeResult()
223
285
  expect(dRuns.runs).toBe(1)
224
- graph.setRef(a, 5, undefined, mockOtelCtx)
286
+ graph.setRef(a, 5)
287
+ d.computeResult()
288
+ d.computeResult() // even extra calls to computeResult should not run d again
225
289
  expect(dRuns.runs).toBe(2)
226
290
  })
227
291
  })
228
292
 
229
293
  describe('a trivial graph with undefined', () => {
230
294
  const makeGraph = () => {
231
- const graph = new ReactiveGraph({ otelTracer: makeNoopTracer() })
232
- const a = graph.makeRef(undefined)
233
- const b = graph.makeRef(2)
234
- const numberOfRunsForC = { runs: 0 }
235
- const c = graph.makeThunk(
236
- (get) => {
237
- numberOfRunsForC.runs++
238
- return (get(a) ?? 0) + get(b)
239
- },
240
- undefined,
241
- mockOtelCtx,
242
- )
295
+ const graph = new ReactiveGraph({})
296
+ graph.context = {}
297
+ const a = graph.makeRef(1)
298
+ const b = graph.makeRef(undefined)
299
+ const c = graph.makeThunk((get) => {
300
+ return get(a) + (get(b) ?? 0)
301
+ })
243
302
  const d = graph.makeRef(3)
244
- const e = graph.makeThunk((get) => get(c) + get(d), undefined, mockOtelCtx)
303
+ const e = graph.makeThunk((get) => get(c) + get(d))
245
304
 
246
- // a(1) b(2)
305
+ // a(1) b(undefined)
247
306
  // \ /
248
307
  // \ /
249
308
  // c = a + b
@@ -254,12 +313,25 @@ describe('a trivial graph with undefined', () => {
254
313
  // \ \
255
314
  // e = c + d
256
315
 
257
- return { graph, a, b, c, d, e, numberOfRunsForC }
316
+ expect(graph.atoms.size).toBe(5)
317
+
318
+ return { graph, a, b, c, d, e }
258
319
  }
259
320
 
260
321
  it('has the right initial values', () => {
261
322
  const { c, e } = makeGraph()
262
- expect(c.result).toBe(2)
263
- expect(e.result).toBe(5)
323
+ expect(c.computeResult()).toBe(1)
324
+ expect(e.computeResult()).toBe(4)
325
+ })
326
+ })
327
+
328
+ describe('error handling', () => {
329
+ it('throws an error when no context is set', () => {
330
+ const graph = new ReactiveGraph({})
331
+ const a = graph.makeRef(1)
332
+ const b = graph.makeThunk((get) => get(a) + 1)
333
+ expect(() => b.computeResult()).toThrowErrorMatchingInlineSnapshot(
334
+ '"LiveStore Error: `context` not set on ReactiveGraph"',
335
+ )
264
336
  })
265
337
  })