@livestore/livestore 0.0.12 → 0.0.14
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 +25 -28
- package/dist/.tsbuildinfo +1 -0
- package/dist/QueryCache.d.ts +20 -0
- package/dist/QueryCache.d.ts.map +1 -0
- package/dist/QueryCache.js +71 -0
- package/dist/QueryCache.js.map +1 -0
- package/dist/__tests__/react/fixture.d.ts +26 -0
- package/dist/__tests__/react/fixture.d.ts.map +1 -0
- package/dist/__tests__/react/fixture.js +60 -0
- package/dist/__tests__/react/fixture.js.map +1 -0
- 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.d.ts +2 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.d.ts.map +1 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js +73 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -0
- 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__/reactive.test.d.ts +2 -0
- package/dist/__tests__/reactive.test.d.ts.map +1 -0
- package/dist/__tests__/reactive.test.js +271 -0
- package/dist/__tests__/reactive.test.js.map +1 -0
- 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/bounded-collections.d.ts +34 -0
- package/dist/bounded-collections.d.ts.map +1 -0
- package/dist/bounded-collections.js +103 -0
- package/dist/bounded-collections.js.map +1 -0
- package/dist/componentKey.d.ts +20 -0
- package/dist/componentKey.d.ts.map +1 -0
- package/dist/componentKey.js +3 -0
- package/dist/componentKey.js.map +1 -0
- package/dist/effect/LiveStore.d.ts +36 -0
- package/dist/effect/LiveStore.d.ts.map +1 -0
- package/dist/effect/LiveStore.js +41 -0
- package/dist/effect/LiveStore.js.map +1 -0
- package/dist/effect/index.d.ts +2 -0
- package/dist/effect/index.d.ts.map +1 -0
- package/dist/effect/index.js +2 -0
- package/dist/effect/index.js.map +1 -0
- package/dist/events.d.ts +7 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/inMemoryDatabase.d.ts +56 -0
- package/dist/inMemoryDatabase.d.ts.map +1 -0
- package/dist/inMemoryDatabase.js +223 -0
- package/dist/inMemoryDatabase.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations.d.ts +16 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +67 -0
- package/dist/migrations.js.map +1 -0
- package/dist/otel.d.ts +4 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +6 -0
- package/dist/otel.js.map +1 -0
- package/dist/react/LiveStoreContext.d.ts +11 -0
- package/dist/react/LiveStoreContext.d.ts.map +1 -0
- package/dist/react/LiveStoreContext.js +10 -0
- package/dist/react/LiveStoreContext.js.map +1 -0
- package/dist/react/LiveStoreProvider.d.ts +20 -0
- package/dist/react/LiveStoreProvider.d.ts.map +1 -0
- package/dist/react/LiveStoreProvider.js +52 -0
- package/dist/react/LiveStoreProvider.js.map +1 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/useComponentState.d.ts +50 -0
- package/dist/react/useComponentState.d.ts.map +1 -0
- package/dist/react/useComponentState.js +248 -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 +13 -0
- package/dist/react/useGraphQL.d.ts.map +1 -0
- package/dist/react/useGraphQL.js +87 -0
- package/dist/react/useGraphQL.js.map +1 -0
- package/dist/react/useLiveStoreComponent.d.ts +75 -0
- package/dist/react/useLiveStoreComponent.d.ts.map +1 -0
- package/dist/react/useLiveStoreComponent.js +361 -0
- package/dist/react/useLiveStoreComponent.js.map +1 -0
- package/dist/react/useQuery.d.ts +3 -0
- package/dist/react/useQuery.d.ts.map +1 -0
- package/dist/react/useQuery.js +42 -0
- package/dist/react/useQuery.js.map +1 -0
- package/dist/react/useTemporaryQuery.d.ts +8 -0
- package/dist/react/useTemporaryQuery.d.ts.map +1 -0
- package/dist/react/useTemporaryQuery.js +17 -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/useStateRefWithReactiveInput.d.ts +13 -0
- package/dist/react/utils/useStateRefWithReactiveInput.d.ts.map +1 -0
- package/dist/react/utils/useStateRefWithReactiveInput.js +38 -0
- package/dist/react/utils/useStateRefWithReactiveInput.js.map +1 -0
- package/dist/reactive.d.ts +134 -0
- package/dist/reactive.d.ts.map +1 -0
- package/dist/reactive.js +409 -0
- package/dist/reactive.js.map +1 -0
- package/dist/reactiveQueries/base-class.d.ts +32 -0
- package/dist/reactiveQueries/base-class.d.ts.map +1 -0
- package/dist/reactiveQueries/base-class.js +30 -0
- package/dist/reactiveQueries/base-class.js.map +1 -0
- 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 +42 -0
- package/dist/reactiveQueries/graphql.d.ts.map +1 -0
- package/dist/reactiveQueries/graphql.js +99 -0
- package/dist/reactiveQueries/graphql.js.map +1 -0
- package/dist/reactiveQueries/js.d.ts +23 -0
- package/dist/reactiveQueries/js.d.ts.map +1 -0
- package/dist/reactiveQueries/js.js +36 -0
- package/dist/reactiveQueries/js.js.map +1 -0
- package/dist/reactiveQueries/sql.d.ts +35 -0
- package/dist/reactiveQueries/sql.d.ts.map +1 -0
- package/dist/reactiveQueries/sql.js +97 -0
- package/dist/reactiveQueries/sql.js.map +1 -0
- package/dist/schema.d.ts +81 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +46 -0
- package/dist/schema.js.map +1 -0
- package/dist/storage/in-memory/index.d.ts +15 -0
- package/dist/storage/in-memory/index.d.ts.map +1 -0
- package/dist/storage/in-memory/index.js +14 -0
- package/dist/storage/in-memory/index.js.map +1 -0
- package/dist/storage/index.d.ts +14 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +9 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/tauri/index.d.ts +19 -0
- package/dist/storage/tauri/index.d.ts.map +1 -0
- package/dist/storage/tauri/index.js +38 -0
- package/dist/storage/tauri/index.js.map +1 -0
- package/dist/storage/utils/idb.d.ts +10 -0
- package/dist/storage/utils/idb.d.ts.map +1 -0
- package/dist/storage/utils/idb.js +58 -0
- package/dist/storage/utils/idb.js.map +1 -0
- package/dist/storage/web-worker/index.d.ts +27 -0
- package/dist/storage/web-worker/index.d.ts.map +1 -0
- package/dist/storage/web-worker/index.js +74 -0
- package/dist/storage/web-worker/index.js.map +1 -0
- package/dist/storage/web-worker/worker.d.ts +13 -0
- package/dist/storage/web-worker/worker.d.ts.map +1 -0
- package/dist/storage/web-worker/worker.js +110 -0
- package/dist/storage/web-worker/worker.js.map +1 -0
- package/dist/store.d.ts +159 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +626 -0
- package/dist/store.js.map +1 -0
- package/dist/util.d.ts +28 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +55 -0
- package/dist/util.js.map +1 -0
- package/package.json +47 -19
- package/src/QueryCache.ts +1 -1
- package/src/__tests__/react/fixture.tsx +35 -39
- 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/extractStackInfoFromStackTrace.test.ts +40 -0
- package/src/__tests__/reactive.test.ts +194 -142
- package/src/__tests__/reactiveQueries/sql.test.ts +372 -0
- package/src/effect/LiveStore.ts +22 -31
- package/src/events.ts +1 -1
- package/src/inMemoryDatabase.ts +117 -142
- package/src/index.ts +18 -22
- package/src/migrations.ts +119 -0
- package/src/otel.ts +0 -11
- package/src/react/LiveStoreProvider.tsx +24 -23
- package/src/react/index.ts +12 -7
- package/src/react/useComponentState.ts +409 -0
- package/src/react/useQuery.ts +58 -0
- package/src/react/useTemporaryQuery.ts +21 -0
- package/src/react/utils/extractStackInfoFromStackTrace.ts +47 -0
- package/src/reactive.ts +386 -267
- package/src/reactiveQueries/base-class.ts +61 -39
- package/src/reactiveQueries/graph.ts +15 -0
- package/src/reactiveQueries/graphql.ts +147 -31
- package/src/reactiveQueries/js.ts +54 -21
- package/src/reactiveQueries/sql.ts +128 -37
- package/src/schema.ts +69 -145
- package/src/storage/in-memory/index.ts +21 -0
- package/src/storage/index.ts +27 -0
- package/src/{backends/tauri.ts → storage/tauri/index.ts} +14 -28
- package/src/storage/web-worker/index.ts +116 -0
- package/src/{backends/web-worker.ts → storage/web-worker/worker.ts} +17 -52
- package/src/store.ts +466 -457
- package/src/util.ts +13 -3
- package/tsconfig.json +1 -3
- package/src/backends/base.ts +0 -67
- package/src/backends/index.ts +0 -98
- package/src/backends/noop.ts +0 -32
- package/src/backends/web-in-memory.ts +0 -65
- package/src/backends/web.ts +0 -97
- package/src/react/useGlobalQuery.ts +0 -40
- package/src/react/useGraphQL.ts +0 -112
- package/src/react/useLiveStoreComponent.ts +0 -483
- /package/src/{backends → storage}/utils/idb.ts +0 -0
package/src/reactive.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
// Effect: a side effect that runs when a value changes; return value is ignored
|
|
7
7
|
// Atom: a node returning a value that can be depended on: Ref | Thunk
|
|
8
8
|
|
|
9
|
-
// Super computation: Nodes that depend on a given node
|
|
10
|
-
// Sub computation: Nodes that a given node depends on
|
|
9
|
+
// Super computation: Nodes that depend on a given node ("downstream")
|
|
10
|
+
// Sub computation: Nodes that a given node depends on ("upstream")
|
|
11
11
|
|
|
12
12
|
// This vocabulary comes from the MiniAdapton paper linked below, although
|
|
13
13
|
// we don't actually implement the MiniAdapton algorithm because we don't need lazy recomputation.
|
|
@@ -24,65 +24,69 @@
|
|
|
24
24
|
/* eslint-disable prefer-arrow/prefer-arrow-functions */
|
|
25
25
|
|
|
26
26
|
import type { PrettifyFlat } from '@livestore/utils'
|
|
27
|
-
import { pick } from '@livestore/utils'
|
|
27
|
+
import { pick, shouldNeverHappen } from '@livestore/utils'
|
|
28
28
|
import type * as otel from '@opentelemetry/api'
|
|
29
29
|
import { isEqual, max, uniqueId } from 'lodash-es'
|
|
30
30
|
|
|
31
31
|
import { BoundArray } from './bounded-collections.js'
|
|
32
|
+
// import { getDurationMsFromSpan } from './otel.js'
|
|
32
33
|
|
|
33
|
-
export
|
|
34
|
+
export const NOT_REFRESHED_YET = Symbol.for('NOT_REFRESHED_YET')
|
|
35
|
+
export type NOT_REFRESHED_YET = typeof NOT_REFRESHED_YET
|
|
36
|
+
|
|
37
|
+
export type GetAtom = <T>(atom: Atom<T, any>, otelContext?: otel.Context) => T
|
|
34
38
|
|
|
35
39
|
export type Ref<T> = {
|
|
36
40
|
_tag: 'ref'
|
|
37
41
|
id: string
|
|
38
|
-
|
|
42
|
+
isDirty: false
|
|
43
|
+
previousResult: T
|
|
39
44
|
height: 0
|
|
40
|
-
|
|
41
|
-
sub: Set<Atom<any>> // always empty
|
|
42
|
-
super: Set<Atom<any> | Effect>
|
|
45
|
+
computeResult: () => T
|
|
46
|
+
sub: Set<Atom<any, TODO>> // always empty
|
|
47
|
+
super: Set<Atom<any, TODO> | Effect>
|
|
43
48
|
label?: string
|
|
44
49
|
/** Container for meta information (e.g. the LiveStore Store) */
|
|
45
50
|
meta?: any
|
|
46
51
|
equal: (a: T, b: T) => boolean
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
type BaseThunk<
|
|
54
|
+
type BaseThunk<TResult, TContext> = {
|
|
50
55
|
_tag: 'thunk'
|
|
51
56
|
id: string
|
|
57
|
+
isDirty: boolean
|
|
52
58
|
height: number
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
computeResult: (otelContext?: otel.Context) => TResult
|
|
60
|
+
previousResult: TResult | NOT_REFRESHED_YET
|
|
61
|
+
sub: Set<Atom<any, TContext>>
|
|
62
|
+
super: Set<Atom<any, TContext> | Effect>
|
|
56
63
|
label?: string
|
|
57
64
|
/** Container for meta information (e.g. the LiveStore Store) */
|
|
58
65
|
meta?: any
|
|
59
|
-
equal: (a:
|
|
66
|
+
equal: (a: TResult, b: TResult) => boolean
|
|
67
|
+
recomputations: number
|
|
68
|
+
|
|
69
|
+
__getResult: any
|
|
60
70
|
}
|
|
61
71
|
|
|
62
|
-
type UnevaluatedThunk<T> = BaseThunk<T>
|
|
63
|
-
|
|
72
|
+
type UnevaluatedThunk<T, TContext> = BaseThunk<T, TContext>
|
|
73
|
+
// & { result: NOT_REFRESHED_YET }
|
|
74
|
+
export type Thunk<T, TContext> = BaseThunk<T, TContext>
|
|
75
|
+
// & { result: T }
|
|
64
76
|
|
|
65
|
-
export type Atom<T> = Ref<T> | Thunk<T>
|
|
77
|
+
export type Atom<T, TContext> = Ref<T> | Thunk<T, TContext>
|
|
66
78
|
|
|
67
79
|
export type Effect = {
|
|
68
80
|
_tag: 'effect'
|
|
69
81
|
id: string
|
|
70
|
-
doEffect: (
|
|
71
|
-
sub: Set<Atom<any>>
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
class DependencyNotReadyError extends Error {
|
|
75
|
-
constructor(message: string) {
|
|
76
|
-
super(message)
|
|
77
|
-
this.name = 'DependencyNotReadyError'
|
|
78
|
-
}
|
|
82
|
+
doEffect: (otelContext?: otel.Context) => void
|
|
83
|
+
sub: Set<Atom<any, TODO>>
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
export type Taggable<T extends string = string> = { _tag: T }
|
|
82
87
|
|
|
83
88
|
export type ReactiveGraphOptions = {
|
|
84
89
|
effectsWrapper?: (runEffects: () => void) => void
|
|
85
|
-
otelTracer: otel.Tracer
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
export type AtomDebugInfo<TDebugThunkInfo extends Taggable> = {
|
|
@@ -117,13 +121,13 @@ export type RefreshReasonWithGenericReasons<T extends Taggable> =
|
|
|
117
121
|
| { _tag: 'unknown' }
|
|
118
122
|
|
|
119
123
|
export const unknownRefreshReason = () => {
|
|
120
|
-
debugger
|
|
124
|
+
// debugger
|
|
121
125
|
return { _tag: 'unknown' as const }
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
export type SerializedAtom = Readonly<
|
|
125
129
|
PrettifyFlat<
|
|
126
|
-
Pick<Atom<unknown>, '_tag' | 'height' | 'id' | 'label' | 'meta'
|
|
130
|
+
Pick<Atom<unknown, TODO>, '_tag' | 'height' | 'id' | 'label' | 'meta'> & {
|
|
127
131
|
sub: string[]
|
|
128
132
|
super: string[]
|
|
129
133
|
}
|
|
@@ -134,45 +138,44 @@ export type SerializedEffect = Readonly<PrettifyFlat<Pick<Effect, '_tag' | 'id'>
|
|
|
134
138
|
|
|
135
139
|
type ReactiveGraphSnapshot = {
|
|
136
140
|
readonly atoms: SerializedAtom[]
|
|
137
|
-
readonly effects: SerializedEffect[]
|
|
141
|
+
// readonly effects: SerializedEffect[]
|
|
138
142
|
/** IDs of atoms and effects that are dirty */
|
|
139
|
-
readonly dirtyNodes: string[]
|
|
143
|
+
// readonly dirtyNodes: string[]
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
const uniqueNodeId = () => uniqueId('node-')
|
|
143
147
|
const uniqueRefreshInfoId = () => uniqueId('refresh-info-')
|
|
144
148
|
|
|
145
|
-
const serializeAtom = (atom: Atom<any>): SerializedAtom => ({
|
|
146
|
-
...pick(atom, ['_tag', 'height', 'id', 'label', 'meta'
|
|
149
|
+
const serializeAtom = (atom: Atom<any, TODO>): SerializedAtom => ({
|
|
150
|
+
...pick(atom, ['_tag', 'height', 'id', 'label', 'meta']),
|
|
147
151
|
sub: Array.from(atom.sub).map((a) => a.id),
|
|
148
152
|
super: Array.from(atom.super).map((a) => a.id),
|
|
149
153
|
})
|
|
150
154
|
|
|
151
|
-
const serializeEffect = (effect: Effect): SerializedEffect => pick(effect, ['_tag', 'id'])
|
|
155
|
+
// const serializeEffect = (effect: Effect): SerializedEffect => pick(effect, ['_tag', 'id'])
|
|
152
156
|
|
|
153
|
-
export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo extends Taggable> {
|
|
154
|
-
|
|
155
|
-
private effects: Set<Effect> = new Set()
|
|
156
|
-
private otelTracer: otel.Tracer
|
|
157
|
-
readonly dirtyNodes: Set<Atom<any> | Effect> = new Set()
|
|
157
|
+
export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo extends Taggable, TContext = {}> {
|
|
158
|
+
readonly atoms: Set<Atom<any, TContext>> = new Set()
|
|
158
159
|
effectsWrapper: (runEffects: () => void) => void
|
|
159
160
|
|
|
161
|
+
context: TContext | undefined
|
|
162
|
+
|
|
160
163
|
debugRefreshInfos: BoundArray<
|
|
161
164
|
RefreshDebugInfo<RefreshReasonWithGenericReasons<TDebugRefreshReason>, TDebugThunkInfo>
|
|
162
165
|
> = new BoundArray(5000)
|
|
163
166
|
|
|
164
167
|
constructor(options: ReactiveGraphOptions) {
|
|
165
168
|
this.effectsWrapper = options?.effectsWrapper ?? ((runEffects: () => void) => runEffects())
|
|
166
|
-
this.otelTracer = options.otelTracer
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
makeRef<T>(val: T, options?: { label?: string; meta?: unknown; equal?: (a: T, b: T) => boolean }): Ref<T> {
|
|
170
172
|
const ref: Ref<T> = {
|
|
171
173
|
_tag: 'ref',
|
|
172
174
|
id: uniqueNodeId(),
|
|
173
|
-
|
|
175
|
+
isDirty: false,
|
|
176
|
+
previousResult: val,
|
|
174
177
|
height: 0,
|
|
175
|
-
|
|
178
|
+
computeResult: () => ref.previousResult,
|
|
176
179
|
sub: new Set(),
|
|
177
180
|
super: new Set(),
|
|
178
181
|
label: options?.label,
|
|
@@ -186,8 +189,13 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
makeThunk<T>(
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
getResult_: (
|
|
193
|
+
get: GetAtom,
|
|
194
|
+
addDebugInfo: (debugInfo: TDebugThunkInfo) => void,
|
|
195
|
+
ctx: TContext,
|
|
196
|
+
otelContext: otel.Context | undefined,
|
|
197
|
+
) => T,
|
|
198
|
+
options?:
|
|
191
199
|
| {
|
|
192
200
|
label?: string
|
|
193
201
|
meta?: any
|
|
@@ -196,36 +204,98 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
196
204
|
debugRefreshReason?: RefreshReasonWithGenericReasons<TDebugRefreshReason>
|
|
197
205
|
}
|
|
198
206
|
| undefined,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
207
|
+
): Thunk<T, TContext> {
|
|
208
|
+
// const computeResult = (): T => {
|
|
209
|
+
// const getAtom = (atom: Atom<T, any>): T => {
|
|
210
|
+
// const __getResult = atom._tag === 'thunk' ? atom.__getResult.toString() : ''
|
|
211
|
+
// if (atom.isDirty) {
|
|
212
|
+
// console.log('atom is dirty', atom.id, atom.label ?? '', atom._tag, __getResult)
|
|
213
|
+
// const result = atom.computeResult()
|
|
214
|
+
// atom.isDirty = false
|
|
215
|
+
// atom.previousResult = result
|
|
216
|
+
// return result
|
|
217
|
+
// } else {
|
|
218
|
+
// console.log('atom is clean', atom.id, atom.label ?? '', atom._tag, __getResult)
|
|
219
|
+
// return atom.previousResult as T
|
|
220
|
+
// }
|
|
221
|
+
// }
|
|
222
|
+
|
|
223
|
+
// let resultChanged = false
|
|
224
|
+
// const debugInfoForAtom = {
|
|
225
|
+
// atom: serializeAtom(null as TODO),
|
|
226
|
+
// resultChanged,
|
|
227
|
+
// // debugInfo: unknownRefreshReason() as TDebugThunkInfo,
|
|
228
|
+
// debugInfo: { _tag: 'unknown' } as TDebugThunkInfo,
|
|
229
|
+
// durationMs: 0,
|
|
230
|
+
// } satisfies AtomDebugInfo<TDebugThunkInfo>
|
|
231
|
+
|
|
232
|
+
const addDebugInfo = (_debugInfo: TDebugThunkInfo) => {
|
|
233
|
+
// debugInfoForAtom.debugInfo = debugInfo
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// debugInfoForRefreshedAtoms.push(debugInfoForAtom)
|
|
237
|
+
|
|
238
|
+
// return getResult_(getAtom as GetAtom, addDebugInfo, this.context!)
|
|
239
|
+
// }
|
|
240
|
+
|
|
241
|
+
const thunk: UnevaluatedThunk<T, TContext> = {
|
|
202
242
|
_tag: 'thunk',
|
|
203
243
|
id: uniqueNodeId(),
|
|
204
|
-
|
|
244
|
+
previousResult: NOT_REFRESHED_YET,
|
|
245
|
+
isDirty: true,
|
|
205
246
|
height: 0,
|
|
206
|
-
|
|
247
|
+
computeResult: (otelContext) => {
|
|
248
|
+
if (thunk.isDirty) {
|
|
249
|
+
// Reset previous subcomputations as we're about to re-add them as part of the `doEffect` call below
|
|
250
|
+
thunk.sub = new Set()
|
|
251
|
+
|
|
252
|
+
const compute_ = (atom: Atom<T, unknown>, otelContext: otel.Context) => {
|
|
253
|
+
this.addEdge(thunk, atom)
|
|
254
|
+
return compute(atom, otelContext)
|
|
255
|
+
}
|
|
256
|
+
const result = getResult_(
|
|
257
|
+
compute_ as GetAtom,
|
|
258
|
+
addDebugInfo,
|
|
259
|
+
this.context ?? shouldNeverHappen('No store context set yet'),
|
|
260
|
+
otelContext,
|
|
261
|
+
)
|
|
262
|
+
thunk.isDirty = false
|
|
263
|
+
thunk.previousResult = result
|
|
264
|
+
thunk.recomputations++
|
|
265
|
+
return result
|
|
266
|
+
} else {
|
|
267
|
+
return thunk.previousResult as T
|
|
268
|
+
}
|
|
269
|
+
},
|
|
207
270
|
sub: new Set(),
|
|
208
271
|
super: new Set(),
|
|
272
|
+
recomputations: 0,
|
|
209
273
|
label: options?.label,
|
|
210
274
|
meta: options?.meta,
|
|
211
275
|
equal: options?.equal ?? isEqual,
|
|
276
|
+
__getResult: getResult_,
|
|
212
277
|
}
|
|
213
278
|
|
|
214
279
|
this.atoms.add(thunk)
|
|
215
|
-
this.dirtyNodes.add(thunk)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
280
|
+
// this.dirtyNodes.add(thunk)
|
|
281
|
+
|
|
282
|
+
const debugRefreshReason = options?.debugRefreshReason ?? { _tag: 'makeThunk', label: options?.label }
|
|
283
|
+
|
|
284
|
+
const refreshDebugInfo = {
|
|
285
|
+
id: uniqueRefreshInfoId(),
|
|
286
|
+
reason: debugRefreshReason,
|
|
287
|
+
skippedRefresh: true,
|
|
288
|
+
refreshedAtoms: [],
|
|
289
|
+
durationMs: 0,
|
|
290
|
+
completedTimestamp: Date.now(),
|
|
291
|
+
graphSnapshot: this.getSnapshot(),
|
|
292
|
+
}
|
|
293
|
+
this.debugRefreshInfos.push(refreshDebugInfo)
|
|
223
294
|
|
|
224
|
-
|
|
225
|
-
return thunk as unknown as Thunk<T>
|
|
295
|
+
return thunk as unknown as Thunk<T, TContext>
|
|
226
296
|
}
|
|
227
297
|
|
|
228
|
-
destroy(node: Atom<any> | Effect) {
|
|
298
|
+
destroy(node: Atom<any, TContext> | Effect) {
|
|
229
299
|
// Recursively destroy any supercomputations
|
|
230
300
|
if (node._tag === 'ref' || node._tag === 'thunk') {
|
|
231
301
|
for (const superComp of node.super) {
|
|
@@ -238,31 +308,51 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
238
308
|
this.removeEdge(node, subComp)
|
|
239
309
|
}
|
|
240
310
|
|
|
241
|
-
if (node._tag
|
|
242
|
-
this.effects.delete(node)
|
|
243
|
-
} else {
|
|
311
|
+
if (node._tag !== 'effect') {
|
|
244
312
|
this.atoms.delete(node)
|
|
245
313
|
}
|
|
246
314
|
}
|
|
247
315
|
|
|
248
316
|
makeEffect(
|
|
249
|
-
doEffect: (get: GetAtom) => void,
|
|
250
|
-
options
|
|
251
|
-
|
|
317
|
+
doEffect: (get: GetAtom, otelContext?: otel.Context) => void,
|
|
318
|
+
options?:
|
|
319
|
+
| {
|
|
320
|
+
label?: string
|
|
321
|
+
debugRefreshReason?: RefreshReasonWithGenericReasons<TDebugRefreshReason>
|
|
322
|
+
}
|
|
323
|
+
| undefined,
|
|
252
324
|
): Effect {
|
|
253
325
|
const effect: Effect = {
|
|
254
326
|
_tag: 'effect',
|
|
255
327
|
id: uniqueNodeId(),
|
|
256
|
-
doEffect
|
|
328
|
+
doEffect: (otelContext) => {
|
|
329
|
+
// Reset previous subcomputations as we're about to re-add them as part of the `doEffect` call below
|
|
330
|
+
effect.sub = new Set()
|
|
331
|
+
|
|
332
|
+
const getAtom = (atom: Atom<any, unknown>, otelContext: otel.Context) => {
|
|
333
|
+
this.addEdge(effect, atom)
|
|
334
|
+
return compute(atom, otelContext)
|
|
335
|
+
}
|
|
336
|
+
doEffect(getAtom as GetAtom, otelContext)
|
|
337
|
+
},
|
|
257
338
|
sub: new Set(),
|
|
258
339
|
}
|
|
259
340
|
|
|
260
|
-
this.effects.add(effect)
|
|
261
|
-
this.dirtyNodes.add(effect)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
341
|
+
// this.effects.add(effect)
|
|
342
|
+
// this.dirtyNodes.add(effect)
|
|
343
|
+
|
|
344
|
+
const debugRefreshReason = options?.debugRefreshReason ?? { _tag: 'makeEffect', label: options?.label }
|
|
345
|
+
|
|
346
|
+
const refreshDebugInfo = {
|
|
347
|
+
id: uniqueRefreshInfoId(),
|
|
348
|
+
reason: debugRefreshReason ?? (unknownRefreshReason() as TDebugRefreshReason),
|
|
349
|
+
skippedRefresh: true,
|
|
350
|
+
refreshedAtoms: [],
|
|
351
|
+
durationMs: 0,
|
|
352
|
+
completedTimestamp: Date.now(),
|
|
353
|
+
graphSnapshot: this.getSnapshot(),
|
|
354
|
+
}
|
|
355
|
+
this.debugRefreshInfos.push(refreshDebugInfo)
|
|
266
356
|
|
|
267
357
|
return effect
|
|
268
358
|
}
|
|
@@ -270,89 +360,88 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
270
360
|
setRef<T>(
|
|
271
361
|
ref: Ref<T>,
|
|
272
362
|
val: T,
|
|
273
|
-
options
|
|
363
|
+
options?:
|
|
274
364
|
| {
|
|
275
|
-
otelHint?: string
|
|
276
|
-
skipRefresh?: boolean
|
|
277
365
|
debugRefreshReason?: TDebugRefreshReason
|
|
366
|
+
otelContext?: otel.Context
|
|
278
367
|
}
|
|
279
368
|
| undefined,
|
|
280
|
-
otelContext: otel.Context,
|
|
281
369
|
) {
|
|
282
|
-
const {
|
|
283
|
-
ref.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
refreshedAtoms: [],
|
|
292
|
-
durationMs: 0,
|
|
293
|
-
completedTimestamp: Date.now(),
|
|
294
|
-
graphSnapshot: this.getSnapshot(),
|
|
370
|
+
const { debugRefreshReason } = options ?? {}
|
|
371
|
+
ref.previousResult = val
|
|
372
|
+
|
|
373
|
+
const effectsToRefresh = new Set<Effect>()
|
|
374
|
+
markSuperCompDirtyRec(ref, effectsToRefresh)
|
|
375
|
+
|
|
376
|
+
this.effectsWrapper(() => {
|
|
377
|
+
for (const effect of effectsToRefresh) {
|
|
378
|
+
effect.doEffect(options?.otelContext)
|
|
295
379
|
}
|
|
296
|
-
|
|
297
|
-
return
|
|
298
|
-
}
|
|
380
|
+
})
|
|
299
381
|
|
|
300
|
-
|
|
382
|
+
const refreshDebugInfo: RefreshDebugInfo<TDebugRefreshReason, TDebugThunkInfo> = {
|
|
383
|
+
id: uniqueRefreshInfoId(),
|
|
384
|
+
reason: debugRefreshReason ?? (unknownRefreshReason() as TDebugRefreshReason),
|
|
385
|
+
skippedRefresh: true,
|
|
386
|
+
refreshedAtoms: [],
|
|
387
|
+
durationMs: 0,
|
|
388
|
+
completedTimestamp: Date.now(),
|
|
389
|
+
graphSnapshot: this.getSnapshot(),
|
|
390
|
+
}
|
|
391
|
+
this.debugRefreshInfos.push(refreshDebugInfo)
|
|
301
392
|
}
|
|
302
393
|
|
|
303
394
|
setRefs<T>(
|
|
304
395
|
refs: [Ref<T>, T][],
|
|
305
|
-
options
|
|
396
|
+
options?:
|
|
306
397
|
| {
|
|
307
|
-
otelHint?: string
|
|
308
|
-
skipRefresh?: boolean
|
|
309
398
|
debugRefreshReason?: TDebugRefreshReason
|
|
399
|
+
otelContext?: otel.Context
|
|
310
400
|
}
|
|
311
401
|
| undefined,
|
|
312
|
-
otelContext: otel.Context,
|
|
313
402
|
) {
|
|
314
|
-
const otelHint = options?.otelHint ?? ''
|
|
315
|
-
const skipRefresh = options?.skipRefresh ?? false
|
|
316
403
|
const debugRefreshReason = options?.debugRefreshReason
|
|
404
|
+
const effectsToRefresh = new Set<Effect>()
|
|
317
405
|
for (const [ref, val] of refs) {
|
|
318
|
-
ref.
|
|
319
|
-
|
|
406
|
+
ref.previousResult = val
|
|
407
|
+
|
|
408
|
+
markSuperCompDirtyRec(ref, effectsToRefresh)
|
|
320
409
|
}
|
|
321
410
|
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
reason: debugRefreshReason ?? (unknownRefreshReason() as TDebugRefreshReason),
|
|
326
|
-
skippedRefresh: true,
|
|
327
|
-
refreshedAtoms: [],
|
|
328
|
-
durationMs: 0,
|
|
329
|
-
completedTimestamp: Date.now(),
|
|
330
|
-
graphSnapshot: this.getSnapshot(),
|
|
411
|
+
this.effectsWrapper(() => {
|
|
412
|
+
for (const effect of effectsToRefresh) {
|
|
413
|
+
effect.doEffect(options?.otelContext)
|
|
331
414
|
}
|
|
332
|
-
|
|
333
|
-
return
|
|
334
|
-
}
|
|
415
|
+
})
|
|
335
416
|
|
|
336
|
-
|
|
417
|
+
const refreshDebugInfo: RefreshDebugInfo<TDebugRefreshReason, TDebugThunkInfo> = {
|
|
418
|
+
id: uniqueRefreshInfoId(),
|
|
419
|
+
reason: debugRefreshReason ?? (unknownRefreshReason() as TDebugRefreshReason),
|
|
420
|
+
skippedRefresh: true,
|
|
421
|
+
refreshedAtoms: [],
|
|
422
|
+
durationMs: 0,
|
|
423
|
+
completedTimestamp: Date.now(),
|
|
424
|
+
graphSnapshot: this.getSnapshot(),
|
|
425
|
+
}
|
|
426
|
+
this.debugRefreshInfos.push(refreshDebugInfo)
|
|
337
427
|
}
|
|
338
428
|
|
|
339
|
-
get<T>(atom: Atom<T>, context: Atom<any> | Effect): T {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
429
|
+
// get<T>(atom: Atom<T, TContext>, context: Atom<any, TContext> | Effect): T {
|
|
430
|
+
// // Autotracking: if we're getting the value of an atom,
|
|
431
|
+
// // that means it's a subcomputation for the currently refreshing atom.
|
|
432
|
+
// this.addEdge(context, atom)
|
|
343
433
|
|
|
344
|
-
|
|
345
|
-
|
|
434
|
+
// const dependencyMightBeStale = context._tag !== 'effect' && context.height <= atom.height
|
|
435
|
+
// const dependencyNotRefreshedYet = atom.result === NOT_REFRESHED_YET
|
|
346
436
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
437
|
+
// if (dependencyMightBeStale || dependencyNotRefreshedYet) {
|
|
438
|
+
// throw new DependencyNotReadyError(
|
|
439
|
+
// `${this.label(context)} referenced dependency ${this.label(atom)} which isn't ready`,
|
|
440
|
+
// )
|
|
441
|
+
// }
|
|
352
442
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
443
|
+
// return atom.result
|
|
444
|
+
// }
|
|
356
445
|
|
|
357
446
|
/**
|
|
358
447
|
* Update the graph to be consistent with the current values of the root atoms.
|
|
@@ -361,138 +450,138 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
361
450
|
*
|
|
362
451
|
* @param roots Root atoms to start the refresh from
|
|
363
452
|
*/
|
|
364
|
-
refresh(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
): void {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
label(atom: Atom<any> | Effect) {
|
|
453
|
+
// refresh(
|
|
454
|
+
// options?:
|
|
455
|
+
// | {
|
|
456
|
+
// otelHint?: string
|
|
457
|
+
// debugRefreshReason?: RefreshReasonWithGenericReasons<TDebugRefreshReason>
|
|
458
|
+
// }
|
|
459
|
+
// | undefined,
|
|
460
|
+
// otelContext: otel.Context = otel.context.active(),
|
|
461
|
+
// ): void {
|
|
462
|
+
// const otelHint = options?.otelHint ?? ''
|
|
463
|
+
// const debugRefreshReason = options?.debugRefreshReason
|
|
464
|
+
|
|
465
|
+
// const roots = [...this.dirtyNodes]
|
|
466
|
+
|
|
467
|
+
// const debugInfoForRefreshedAtoms: AtomDebugInfo<TDebugThunkInfo>[] = []
|
|
468
|
+
|
|
469
|
+
// // if (otelHint.includes('tableName')) {
|
|
470
|
+
// // console.log('refresh', otelHint, { shouldTrace })
|
|
471
|
+
// // }
|
|
472
|
+
|
|
473
|
+
// this.otelTracer.startActiveSpan(`LiveStore.refresh:${otelHint}`, {}, otelContext, (span) => {
|
|
474
|
+
// const atomsToRefresh = roots.filter(isAtom)
|
|
475
|
+
// const effectsToRun = new Set(roots.filter(isEffect))
|
|
476
|
+
|
|
477
|
+
// span.setAttribute('livestore.hint', otelHint)
|
|
478
|
+
// span.setAttribute('livestore.rootsCount', roots.length)
|
|
479
|
+
// // span.setAttribute('sstack', new Error().stack!)
|
|
480
|
+
|
|
481
|
+
// // Sort in topological order, starting with minimum height
|
|
482
|
+
// while (atomsToRefresh.length > 0) {
|
|
483
|
+
// atomsToRefresh.sort((a, b) => a.height - b.height)
|
|
484
|
+
// const atomToRefresh = atomsToRefresh.shift()!
|
|
485
|
+
|
|
486
|
+
// // Recompute the value
|
|
487
|
+
// let resultChanged = false
|
|
488
|
+
// const debugInfoForAtom = {
|
|
489
|
+
// atom: serializeAtom(atomToRefresh),
|
|
490
|
+
// resultChanged,
|
|
491
|
+
// // debugInfo: unknownRefreshReason() as TDebugThunkInfo,
|
|
492
|
+
// debugInfo: { _tag: 'unknown' } as TDebugThunkInfo,
|
|
493
|
+
// durationMs: 0,
|
|
494
|
+
// } satisfies AtomDebugInfo<TDebugThunkInfo>
|
|
495
|
+
// try {
|
|
496
|
+
// atomToRefresh.sub = new Set()
|
|
497
|
+
// const beforeTimestamp = performance.now()
|
|
498
|
+
// const newResult = atomToRefresh.getResult(
|
|
499
|
+
// (atom) => this.get(atom, atomToRefresh),
|
|
500
|
+
// (debugInfo) => {
|
|
501
|
+
// debugInfoForAtom.debugInfo = debugInfo
|
|
502
|
+
// },
|
|
503
|
+
// this.context ?? shouldNeverHappen(`No context provided yet for ReactiveGraph`),
|
|
504
|
+
// )
|
|
505
|
+
// const afterTimestamp = performance.now()
|
|
506
|
+
// debugInfoForAtom.durationMs = afterTimestamp - beforeTimestamp
|
|
507
|
+
|
|
508
|
+
// // Determine if the result changed to do early cutoff and avoid further unnecessary updates.
|
|
509
|
+
// // Refs never depend on anything, so if a ref is being refreshed it definitely changed.
|
|
510
|
+
// // For thunks, we use a deep equality check.
|
|
511
|
+
// resultChanged =
|
|
512
|
+
// atomToRefresh._tag === 'ref' ||
|
|
513
|
+
// (atomToRefresh._tag === 'thunk' && !atomToRefresh.equal(atomToRefresh.result, newResult))
|
|
514
|
+
|
|
515
|
+
// if (resultChanged) {
|
|
516
|
+
// atomToRefresh.result = newResult
|
|
517
|
+
// }
|
|
518
|
+
|
|
519
|
+
// this.dirtyNodes.delete(atomToRefresh)
|
|
520
|
+
// } catch (e) {
|
|
521
|
+
// if (e instanceof DependencyNotReadyError) {
|
|
522
|
+
// // If we hit a dependency that wasn't ready yet,
|
|
523
|
+
// // abort this recomputation and try again later.
|
|
524
|
+
// if (!atomsToRefresh.includes(atomToRefresh)) {
|
|
525
|
+
// atomsToRefresh.push(atomToRefresh)
|
|
526
|
+
// }
|
|
527
|
+
// } else {
|
|
528
|
+
// throw e
|
|
529
|
+
// }
|
|
530
|
+
// }
|
|
531
|
+
|
|
532
|
+
// debugInfoForRefreshedAtoms.push(debugInfoForAtom)
|
|
533
|
+
|
|
534
|
+
// if (!resultChanged) {
|
|
535
|
+
// continue
|
|
536
|
+
// }
|
|
537
|
+
|
|
538
|
+
// // Schedule supercomputations
|
|
539
|
+
// for (const superComp of atomToRefresh.super) {
|
|
540
|
+
// switch (superComp._tag) {
|
|
541
|
+
// case 'ref':
|
|
542
|
+
// case 'thunk': {
|
|
543
|
+
// if (!atomsToRefresh.includes(superComp)) {
|
|
544
|
+
// atomsToRefresh.push(superComp)
|
|
545
|
+
// }
|
|
546
|
+
// break
|
|
547
|
+
// }
|
|
548
|
+
// case 'effect': {
|
|
549
|
+
// effectsToRun.add(superComp)
|
|
550
|
+
// break
|
|
551
|
+
// }
|
|
552
|
+
// }
|
|
553
|
+
// }
|
|
554
|
+
// }
|
|
555
|
+
|
|
556
|
+
// this.effectsWrapper(() => {
|
|
557
|
+
// for (const effect of effectsToRun) {
|
|
558
|
+
// effect.doEffect((atom: Atom<any, TContext>) => this.get(atom, effect))
|
|
559
|
+
// this.dirtyNodes.delete(effect)
|
|
560
|
+
// }
|
|
561
|
+
// })
|
|
562
|
+
|
|
563
|
+
// span.end()
|
|
564
|
+
|
|
565
|
+
// const spanDurationMs = getDurationMsFromSpan(span)
|
|
566
|
+
|
|
567
|
+
// const refreshDebugInfo: RefreshDebugInfo<
|
|
568
|
+
// RefreshReasonWithGenericReasons<TDebugRefreshReason>,
|
|
569
|
+
// TDebugThunkInfo
|
|
570
|
+
// > = {
|
|
571
|
+
// id: uniqueRefreshInfoId(),
|
|
572
|
+
// reason: debugRefreshReason ?? unknownRefreshReason(),
|
|
573
|
+
// refreshedAtoms: debugInfoForRefreshedAtoms,
|
|
574
|
+
// skippedRefresh: false,
|
|
575
|
+
// durationMs: spanDurationMs,
|
|
576
|
+
// completedTimestamp: Date.now(),
|
|
577
|
+
// graphSnapshot: this.getSnapshot(),
|
|
578
|
+
// }
|
|
579
|
+
|
|
580
|
+
// this.debugRefreshInfos.push(refreshDebugInfo)
|
|
581
|
+
// })
|
|
582
|
+
// }
|
|
583
|
+
|
|
584
|
+
label(atom: Atom<any, TContext> | Effect) {
|
|
496
585
|
if (atom._tag === 'effect') {
|
|
497
586
|
return `unknown effect`
|
|
498
587
|
} else {
|
|
@@ -500,19 +589,19 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
500
589
|
}
|
|
501
590
|
}
|
|
502
591
|
|
|
503
|
-
addEdge(superComp: Atom<any> | Effect, subComp: Atom<any>) {
|
|
592
|
+
addEdge(superComp: Atom<any, TContext> | Effect, subComp: Atom<any, TContext>) {
|
|
504
593
|
superComp.sub.add(subComp)
|
|
505
594
|
subComp.super.add(superComp)
|
|
506
595
|
this.updateAtomHeight(superComp)
|
|
507
596
|
}
|
|
508
597
|
|
|
509
|
-
removeEdge(superComp: Atom<any> | Effect, subComp: Atom<any>) {
|
|
598
|
+
removeEdge(superComp: Atom<any, TContext> | Effect, subComp: Atom<any, TContext>) {
|
|
510
599
|
superComp.sub.delete(subComp)
|
|
511
600
|
subComp.super.delete(superComp)
|
|
512
601
|
this.updateAtomHeight(superComp)
|
|
513
602
|
}
|
|
514
603
|
|
|
515
|
-
updateAtomHeight(atom: Atom<any> | Effect) {
|
|
604
|
+
updateAtomHeight(atom: Atom<any, TContext> | Effect) {
|
|
516
605
|
switch (atom._tag) {
|
|
517
606
|
case 'ref': {
|
|
518
607
|
atom.height = 0
|
|
@@ -530,10 +619,40 @@ export class ReactiveGraph<TDebugRefreshReason extends Taggable, TDebugThunkInfo
|
|
|
530
619
|
|
|
531
620
|
private getSnapshot = (): ReactiveGraphSnapshot => ({
|
|
532
621
|
atoms: Array.from(this.atoms).map(serializeAtom),
|
|
533
|
-
effects: Array.from(this.effects).map(serializeEffect),
|
|
534
|
-
dirtyNodes: Array.from(this.dirtyNodes).map((a) => a.id),
|
|
622
|
+
// effects: Array.from(this.effects).map(serializeEffect),
|
|
623
|
+
// dirtyNodes: Array.from(this.dirtyNodes).map((a) => a.id),
|
|
535
624
|
})
|
|
625
|
+
|
|
626
|
+
get atomsCount() {
|
|
627
|
+
return this.atoms.size
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// const isAtom = <T, TContext>(a: Atom<T, TContext> | Effect): a is Atom<T, TContext> =>
|
|
632
|
+
// a._tag === 'ref' || a._tag === 'thunk'
|
|
633
|
+
// const isEffect = <T, TContext>(a: Atom<T, TContext> | Effect): a is Effect => a._tag === 'effect'
|
|
634
|
+
|
|
635
|
+
const compute = <T>(atom: Atom<T, any>, otelContext: otel.Context): T => {
|
|
636
|
+
// const __getResult = atom._tag === 'thunk' ? atom.__getResult.toString() : ''
|
|
637
|
+
if (atom.isDirty) {
|
|
638
|
+
// console.log('atom is dirty', atom.id, atom.label ?? '', atom._tag, __getResult)
|
|
639
|
+
const result = atom.computeResult(otelContext)
|
|
640
|
+
atom.isDirty = false
|
|
641
|
+
atom.previousResult = result
|
|
642
|
+
return result
|
|
643
|
+
} else {
|
|
644
|
+
// console.log('atom is clean', atom.id, atom.label ?? '', atom._tag, __getResult)
|
|
645
|
+
return atom.previousResult as T
|
|
646
|
+
}
|
|
536
647
|
}
|
|
537
648
|
|
|
538
|
-
const
|
|
539
|
-
|
|
649
|
+
const markSuperCompDirtyRec = <T>(atom: Atom<T, any>, effectsToRefresh: Set<Effect>) => {
|
|
650
|
+
for (const superComp of atom.super) {
|
|
651
|
+
if (superComp._tag === 'thunk' || superComp._tag === 'ref') {
|
|
652
|
+
superComp.isDirty = true
|
|
653
|
+
markSuperCompDirtyRec(superComp, effectsToRefresh)
|
|
654
|
+
} else {
|
|
655
|
+
effectsToRefresh.add(superComp)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|