@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.
- package/README.md +29 -22
- package/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.d.ts +1 -1
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +5 -4
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +3 -5
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/__tests__/react/useComponentState.test.d.ts +2 -0
- package/dist/__tests__/react/useComponentState.test.d.ts.map +1 -0
- package/dist/__tests__/react/useComponentState.test.js +68 -0
- package/dist/__tests__/react/useComponentState.test.js.map +1 -0
- package/dist/__tests__/react/useLQuery.test.d.ts +2 -0
- package/dist/__tests__/react/useLQuery.test.d.ts.map +1 -0
- package/dist/__tests__/react/useLQuery.test.js +38 -0
- package/dist/__tests__/react/useLQuery.test.js.map +1 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js +4 -9
- package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -1
- package/dist/__tests__/react/useQuery.test.d.ts +2 -0
- package/dist/__tests__/react/useQuery.test.d.ts.map +1 -0
- package/dist/__tests__/react/useQuery.test.js +33 -0
- package/dist/__tests__/react/useQuery.test.js.map +1 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +2 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +1 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +38 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +1 -0
- package/dist/__tests__/react/utils/stack-info.test.d.ts +2 -0
- package/dist/__tests__/react/utils/stack-info.test.d.ts.map +1 -0
- package/dist/__tests__/react/utils/stack-info.test.js +43 -0
- package/dist/__tests__/react/utils/stack-info.test.js.map +1 -0
- package/dist/__tests__/reactive.test.js +179 -93
- package/dist/__tests__/reactive.test.js.map +1 -1
- package/dist/__tests__/reactiveQueries/sql.test.d.ts +2 -0
- package/dist/__tests__/reactiveQueries/sql.test.d.ts.map +1 -0
- package/dist/__tests__/reactiveQueries/sql.test.js +337 -0
- package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -0
- package/dist/inMemoryDatabase.d.ts +4 -3
- package/dist/inMemoryDatabase.d.ts.map +1 -1
- package/dist/inMemoryDatabase.js +3 -2
- package/dist/inMemoryDatabase.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react/useComponentState.d.ts +50 -0
- package/dist/react/useComponentState.d.ts.map +1 -0
- package/dist/react/useComponentState.js +240 -0
- package/dist/react/useComponentState.js.map +1 -0
- package/dist/react/useGlobalQuery.d.ts +3 -0
- package/dist/react/useGlobalQuery.d.ts.map +1 -0
- package/dist/react/useGlobalQuery.js +26 -0
- package/dist/react/useGlobalQuery.js.map +1 -0
- package/dist/react/useGraphQL.d.ts +3 -3
- package/dist/react/useGraphQL.d.ts.map +1 -1
- package/dist/react/useGraphQL.js +10 -8
- package/dist/react/useGraphQL.js.map +1 -1
- package/dist/react/useLiveStoreComponent.d.ts +6 -6
- package/dist/react/useLiveStoreComponent.d.ts.map +1 -1
- package/dist/react/useLiveStoreComponent.js +143 -99
- package/dist/react/useLiveStoreComponent.js.map +1 -1
- package/dist/react/useQuery.d.ts +2 -2
- package/dist/react/useQuery.d.ts.map +1 -1
- package/dist/react/useQuery.js +54 -30
- package/dist/react/useQuery.js.map +1 -1
- package/dist/react/useTemporaryQuery.d.ts +8 -0
- package/dist/react/useTemporaryQuery.d.ts.map +1 -0
- package/dist/react/useTemporaryQuery.js +19 -0
- package/dist/react/useTemporaryQuery.js.map +1 -0
- package/dist/react/utils/extractNamesFromStackTrace.d.ts +3 -0
- package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +1 -0
- package/dist/react/utils/extractNamesFromStackTrace.js +40 -0
- package/dist/react/utils/extractNamesFromStackTrace.js.map +1 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +7 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +1 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.js +40 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.js.map +1 -0
- package/dist/react/utils/stack-info.d.ts +11 -0
- package/dist/react/utils/stack-info.d.ts.map +1 -0
- package/dist/react/utils/stack-info.js +49 -0
- package/dist/react/utils/stack-info.js.map +1 -0
- package/dist/reactive.d.ts +51 -67
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +138 -220
- package/dist/reactive.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +28 -21
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +22 -18
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/graph.d.ts +10 -0
- package/dist/reactiveQueries/graph.d.ts.map +1 -0
- package/dist/reactiveQueries/graph.js +6 -0
- package/dist/reactiveQueries/graph.js.map +1 -0
- package/dist/reactiveQueries/graphql.d.ts +35 -17
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +86 -10
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +17 -12
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js +30 -8
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +28 -18
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +79 -16
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/store.d.ts +35 -61
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +77 -272
- package/dist/store.js.map +1 -1
- package/package.json +4 -3
- package/src/QueryCache.ts +1 -1
- package/src/__tests__/react/fixture.tsx +10 -8
- package/src/__tests__/react/{useLiveStoreComponent.test.tsx → useComponentState.test.tsx} +9 -20
- package/src/__tests__/react/useQuery.test.tsx +48 -0
- package/src/__tests__/react/utils/stack-info.test.ts +45 -0
- package/src/__tests__/reactive.test.ts +212 -140
- package/src/__tests__/reactiveQueries/sql.test.ts +372 -0
- package/src/inMemoryDatabase.ts +11 -8
- package/src/index.ts +7 -11
- package/src/react/index.ts +4 -7
- package/src/react/{useLiveStoreComponent.ts → useComponentState.ts} +90 -253
- package/src/react/useQuery.ts +74 -40
- package/src/react/useTemporaryQuery.ts +23 -0
- package/src/react/utils/stack-info.ts +63 -0
- package/src/reactive.ts +234 -308
- package/src/reactiveQueries/base-class.ts +59 -42
- package/src/reactiveQueries/graph.ts +15 -0
- package/src/reactiveQueries/graphql.ts +143 -29
- package/src/reactiveQueries/js.ts +57 -20
- package/src/reactiveQueries/sql.ts +136 -36
- package/src/store.ts +121 -426
- 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({
|
|
12
|
-
|
|
13
|
-
const
|
|
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
|
-
|
|
21
|
-
mockOtelCtx,
|
|
17
|
+
{ label: 'c' },
|
|
22
18
|
)
|
|
23
|
-
const d = graph.makeRef(3)
|
|
24
|
-
const e = graph.makeThunk((get) => get(c) + get(d),
|
|
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.
|
|
43
|
-
expect(e.
|
|
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
|
|
49
|
-
expect(c.
|
|
50
|
-
expect(e.
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
69
|
-
expect(f.
|
|
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
|
|
74
|
-
expect(f.
|
|
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
|
|
79
|
-
expect(f.
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
165
|
+
describe('a dynamic graph', () => {
|
|
166
|
+
const makeGraph = () => {
|
|
167
|
+
const graph = new ReactiveGraph({})
|
|
168
|
+
graph.context = {}
|
|
129
169
|
|
|
130
|
-
graph.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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({
|
|
237
|
+
const graph = new ReactiveGraph({})
|
|
238
|
+
graph.context = {}
|
|
177
239
|
const a = graph.makeRef(1)
|
|
178
|
-
const b = graph.makeThunk((get) => get(a) + 1
|
|
179
|
-
const c = graph.makeThunk((get) => get(a) + 1
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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.
|
|
207
|
-
expect(c.
|
|
208
|
-
expect(d.
|
|
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
|
|
214
|
-
expect(b.
|
|
215
|
-
expect(c.
|
|
216
|
-
expect(d.
|
|
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
|
|
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({
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
const c = graph.makeThunk(
|
|
236
|
-
(get)
|
|
237
|
-
|
|
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)
|
|
303
|
+
const e = graph.makeThunk((get) => get(c) + get(d))
|
|
245
304
|
|
|
246
|
-
// a(1) b(
|
|
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
|
-
|
|
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.
|
|
263
|
-
expect(e.
|
|
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
|
})
|