@tanstack/store 0.8.0 → 0.9.0
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/dist/cjs/alien.cjs +345 -0
- package/dist/cjs/alien.cjs.map +1 -0
- package/dist/cjs/alien.d.cts +57 -0
- package/dist/cjs/atom.cjs +222 -0
- package/dist/cjs/atom.cjs.map +1 -0
- package/dist/cjs/atom.d.cts +16 -0
- package/dist/cjs/batch.cjs +15 -0
- package/dist/cjs/batch.cjs.map +1 -0
- package/dist/cjs/batch.d.cts +1 -0
- package/dist/cjs/index.cjs +8 -12
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +3 -4
- package/dist/cjs/store.cjs +22 -29
- package/dist/cjs/store.cjs.map +1 -1
- package/dist/cjs/store.d.cts +11 -30
- package/dist/cjs/types.d.cts +47 -20
- package/dist/esm/alien.d.ts +57 -0
- package/dist/esm/alien.js +345 -0
- package/dist/esm/alien.js.map +1 -0
- package/dist/esm/atom.d.ts +16 -0
- package/dist/esm/atom.js +222 -0
- package/dist/esm/atom.js.map +1 -0
- package/dist/esm/batch.d.ts +1 -0
- package/dist/esm/batch.js +15 -0
- package/dist/esm/batch.js.map +1 -0
- package/dist/esm/index.d.ts +3 -4
- package/dist/esm/index.js +8 -12
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/store.d.ts +11 -30
- package/dist/esm/store.js +23 -30
- package/dist/esm/store.js.map +1 -1
- package/dist/esm/types.d.ts +47 -20
- package/package.json +6 -7
- package/src/alien.ts +654 -0
- package/src/atom.ts +298 -0
- package/src/batch.ts +12 -0
- package/src/index.ts +3 -4
- package/src/store.ts +33 -72
- package/src/types.ts +60 -24
- package/dist/cjs/derived.cjs +0 -130
- package/dist/cjs/derived.cjs.map +0 -1
- package/dist/cjs/derived.d.cts +0 -50
- package/dist/cjs/effect.cjs +0 -24
- package/dist/cjs/effect.cjs.map +0 -1
- package/dist/cjs/effect.d.cts +0 -18
- package/dist/cjs/scheduler.cjs +0 -103
- package/dist/cjs/scheduler.cjs.map +0 -1
- package/dist/cjs/scheduler.d.cts +0 -27
- package/dist/cjs/types.cjs +0 -7
- package/dist/cjs/types.cjs.map +0 -1
- package/dist/esm/derived.d.ts +0 -50
- package/dist/esm/derived.js +0 -130
- package/dist/esm/derived.js.map +0 -1
- package/dist/esm/effect.d.ts +0 -18
- package/dist/esm/effect.js +0 -24
- package/dist/esm/effect.js.map +0 -1
- package/dist/esm/scheduler.d.ts +0 -27
- package/dist/esm/scheduler.js +0 -103
- package/dist/esm/scheduler.js.map +0 -1
- package/dist/esm/types.js +0 -7
- package/dist/esm/types.js.map +0 -1
- package/src/derived.ts +0 -219
- package/src/effect.ts +0 -42
- package/src/scheduler.ts +0 -146
package/src/atom.ts
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { ReactiveFlags, createReactiveSystem, getBatchDepth } from './alien'
|
|
2
|
+
|
|
3
|
+
import type { ReactiveNode } from './alien'
|
|
4
|
+
import type {
|
|
5
|
+
Atom,
|
|
6
|
+
AtomOptions,
|
|
7
|
+
Observer,
|
|
8
|
+
ReadonlyAtom,
|
|
9
|
+
Subscription,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
export function toObserver<T>(
|
|
13
|
+
nextHandler?: Observer<T> | ((value: T) => void),
|
|
14
|
+
errorHandler?: (error: any) => void,
|
|
15
|
+
completionHandler?: () => void,
|
|
16
|
+
): Observer<T> {
|
|
17
|
+
const isObserver = typeof nextHandler === 'object'
|
|
18
|
+
const self = isObserver ? nextHandler : undefined
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
22
|
+
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
23
|
+
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(
|
|
24
|
+
self,
|
|
25
|
+
),
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface InternalAtom<T> extends ReactiveNode {
|
|
30
|
+
_snapshot: T
|
|
31
|
+
_update: (getValue?: T | ((snapshot: T) => T)) => boolean
|
|
32
|
+
get: () => T
|
|
33
|
+
subscribe: (observerOrFn: Observer<T> | ((value: T) => void)) => Subscription
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const queuedEffects: Array<Effect | undefined> = []
|
|
37
|
+
let cycle = 0
|
|
38
|
+
const { link, unlink, propagate, checkDirty, shallowPropagate } =
|
|
39
|
+
createReactiveSystem({
|
|
40
|
+
update(atom: InternalAtom<any>): boolean {
|
|
41
|
+
return atom._update()
|
|
42
|
+
},
|
|
43
|
+
// eslint-disable-next-line no-shadow
|
|
44
|
+
notify(effect: Effect): void {
|
|
45
|
+
queuedEffects[queuedEffectsLength++] = effect
|
|
46
|
+
effect.flags &= ~ReactiveFlags.Watching
|
|
47
|
+
},
|
|
48
|
+
unwatched(atom: InternalAtom<any>): void {
|
|
49
|
+
if (atom.depsTail !== undefined) {
|
|
50
|
+
atom.depsTail = undefined
|
|
51
|
+
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty
|
|
52
|
+
purgeDeps(atom)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
let notifyIndex = 0
|
|
58
|
+
let queuedEffectsLength = 0
|
|
59
|
+
let activeSub: ReactiveNode | undefined
|
|
60
|
+
|
|
61
|
+
function purgeDeps(sub: ReactiveNode) {
|
|
62
|
+
const depsTail = sub.depsTail
|
|
63
|
+
let dep = depsTail !== undefined ? depsTail.nextDep : sub.deps
|
|
64
|
+
while (dep !== undefined) {
|
|
65
|
+
dep = unlink(dep, sub)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function flush(): void {
|
|
70
|
+
if (getBatchDepth() > 0) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
while (notifyIndex < queuedEffectsLength) {
|
|
74
|
+
// eslint-disable-next-line no-shadow
|
|
75
|
+
const effect = queuedEffects[notifyIndex]!
|
|
76
|
+
queuedEffects[notifyIndex++] = undefined
|
|
77
|
+
effect.notify()
|
|
78
|
+
}
|
|
79
|
+
notifyIndex = 0
|
|
80
|
+
queuedEffectsLength = 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
type AsyncAtomState<TData, TError = unknown> =
|
|
84
|
+
| { status: 'pending' }
|
|
85
|
+
| { status: 'done'; data: TData }
|
|
86
|
+
| { status: 'error'; error: TError }
|
|
87
|
+
|
|
88
|
+
export function createAsyncAtom<T>(
|
|
89
|
+
getValue: () => Promise<T>,
|
|
90
|
+
options?: AtomOptions<AsyncAtomState<T>>,
|
|
91
|
+
): ReadonlyAtom<AsyncAtomState<T>> {
|
|
92
|
+
const ref: { current?: InternalAtom<AsyncAtomState<T>> } = {}
|
|
93
|
+
const atom = createAtom<AsyncAtomState<T>>(() => {
|
|
94
|
+
getValue().then(
|
|
95
|
+
(data) => {
|
|
96
|
+
const internalAtom = ref.current!
|
|
97
|
+
if (internalAtom._update({ status: 'done', data })) {
|
|
98
|
+
const subs = internalAtom.subs
|
|
99
|
+
if (subs !== undefined) {
|
|
100
|
+
propagate(subs)
|
|
101
|
+
shallowPropagate(subs)
|
|
102
|
+
flush()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
(error) => {
|
|
107
|
+
const internalAtom = ref.current!
|
|
108
|
+
if (internalAtom._update({ status: 'error', error })) {
|
|
109
|
+
const subs = internalAtom.subs
|
|
110
|
+
if (subs !== undefined) {
|
|
111
|
+
propagate(subs)
|
|
112
|
+
shallowPropagate(subs)
|
|
113
|
+
flush()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return { status: 'pending' }
|
|
120
|
+
}, options)
|
|
121
|
+
ref.current = atom as unknown as InternalAtom<AsyncAtomState<T>>
|
|
122
|
+
|
|
123
|
+
return atom
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createAtom<T>(
|
|
127
|
+
getValue: (prev?: NoInfer<T>) => T,
|
|
128
|
+
options?: AtomOptions<T>,
|
|
129
|
+
): ReadonlyAtom<T>
|
|
130
|
+
export function createAtom<T>(
|
|
131
|
+
initialValue: T,
|
|
132
|
+
options?: AtomOptions<T>,
|
|
133
|
+
): Atom<T>
|
|
134
|
+
export function createAtom<T>(
|
|
135
|
+
valueOrFn: T | ((prev?: T) => T),
|
|
136
|
+
options?: AtomOptions<T>,
|
|
137
|
+
): Atom<T> | ReadonlyAtom<T> {
|
|
138
|
+
const isComputed = typeof valueOrFn === 'function'
|
|
139
|
+
const getter = valueOrFn as (prev?: T) => T
|
|
140
|
+
|
|
141
|
+
// Create plain object atom
|
|
142
|
+
const atom: InternalAtom<T> = {
|
|
143
|
+
_snapshot: isComputed ? undefined! : valueOrFn,
|
|
144
|
+
|
|
145
|
+
subs: undefined,
|
|
146
|
+
subsTail: undefined,
|
|
147
|
+
deps: undefined,
|
|
148
|
+
depsTail: undefined,
|
|
149
|
+
flags: isComputed ? ReactiveFlags.None : ReactiveFlags.Mutable,
|
|
150
|
+
|
|
151
|
+
get(): T {
|
|
152
|
+
if (activeSub !== undefined) {
|
|
153
|
+
link(atom, activeSub, cycle)
|
|
154
|
+
}
|
|
155
|
+
return atom._snapshot
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
subscribe(observerOrFn: Observer<T> | ((value: T) => void)) {
|
|
159
|
+
const obs = toObserver(observerOrFn)
|
|
160
|
+
const observed = { current: false }
|
|
161
|
+
const e = effect(() => {
|
|
162
|
+
atom.get()
|
|
163
|
+
if (!observed.current) {
|
|
164
|
+
observed.current = true
|
|
165
|
+
} else {
|
|
166
|
+
obs.next?.(atom._snapshot)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
unsubscribe: () => {
|
|
172
|
+
e.stop()
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
_update(getValue?: T | ((snapshot: T) => T)): boolean {
|
|
177
|
+
const prevSub = activeSub
|
|
178
|
+
const compare = options?.compare ?? Object.is
|
|
179
|
+
activeSub = atom
|
|
180
|
+
++cycle
|
|
181
|
+
atom.depsTail = undefined
|
|
182
|
+
if (isComputed) {
|
|
183
|
+
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.RecursedCheck
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const oldValue = atom._snapshot
|
|
187
|
+
const newValue =
|
|
188
|
+
typeof getValue === 'function'
|
|
189
|
+
? (getValue as (snapshot: T) => T)(oldValue)
|
|
190
|
+
: getValue === undefined && isComputed
|
|
191
|
+
? getter(oldValue)
|
|
192
|
+
: getValue!
|
|
193
|
+
if (oldValue === undefined || !compare(oldValue, newValue)) {
|
|
194
|
+
atom._snapshot = newValue
|
|
195
|
+
return true
|
|
196
|
+
}
|
|
197
|
+
return false
|
|
198
|
+
} finally {
|
|
199
|
+
activeSub = prevSub
|
|
200
|
+
if (isComputed) {
|
|
201
|
+
atom.flags &= ~ReactiveFlags.RecursedCheck
|
|
202
|
+
}
|
|
203
|
+
purgeDeps(atom)
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (isComputed) {
|
|
209
|
+
atom.flags = ReactiveFlags.Mutable | ReactiveFlags.Dirty
|
|
210
|
+
atom.get = function (): T {
|
|
211
|
+
const flags = atom.flags
|
|
212
|
+
if (
|
|
213
|
+
flags & ReactiveFlags.Dirty ||
|
|
214
|
+
(flags & ReactiveFlags.Pending && checkDirty(atom.deps!, atom))
|
|
215
|
+
) {
|
|
216
|
+
if (atom._update()) {
|
|
217
|
+
const subs = atom.subs
|
|
218
|
+
if (subs !== undefined) {
|
|
219
|
+
shallowPropagate(subs)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} else if (flags & ReactiveFlags.Pending) {
|
|
223
|
+
atom.flags = flags & ~ReactiveFlags.Pending
|
|
224
|
+
}
|
|
225
|
+
if (activeSub !== undefined) {
|
|
226
|
+
link(atom, activeSub, cycle)
|
|
227
|
+
}
|
|
228
|
+
return atom._snapshot
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
;(atom as unknown as Atom<T>).set = function (
|
|
232
|
+
// eslint-disable-next-line no-shadow
|
|
233
|
+
valueOrFn: T | ((prev: T) => T),
|
|
234
|
+
): void {
|
|
235
|
+
if (atom._update(valueOrFn)) {
|
|
236
|
+
const subs = atom.subs
|
|
237
|
+
if (subs !== undefined) {
|
|
238
|
+
propagate(subs)
|
|
239
|
+
shallowPropagate(subs)
|
|
240
|
+
flush()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return atom as unknown as Atom<T> | ReadonlyAtom<T>
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
interface Effect extends ReactiveNode {
|
|
250
|
+
notify: () => void
|
|
251
|
+
stop: () => void
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function effect<T>(fn: () => T): Effect {
|
|
255
|
+
const run = (): T => {
|
|
256
|
+
const prevSub = activeSub
|
|
257
|
+
activeSub = effectObj
|
|
258
|
+
++cycle
|
|
259
|
+
effectObj.depsTail = undefined
|
|
260
|
+
effectObj.flags = ReactiveFlags.Watching | ReactiveFlags.RecursedCheck
|
|
261
|
+
try {
|
|
262
|
+
return fn()
|
|
263
|
+
} finally {
|
|
264
|
+
activeSub = prevSub
|
|
265
|
+
effectObj.flags &= ~ReactiveFlags.RecursedCheck
|
|
266
|
+
purgeDeps(effectObj)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const effectObj: Effect = {
|
|
270
|
+
deps: undefined,
|
|
271
|
+
depsTail: undefined,
|
|
272
|
+
subs: undefined,
|
|
273
|
+
subsTail: undefined,
|
|
274
|
+
flags: ReactiveFlags.Watching | ReactiveFlags.RecursedCheck,
|
|
275
|
+
|
|
276
|
+
notify(): void {
|
|
277
|
+
const flags = this.flags
|
|
278
|
+
if (
|
|
279
|
+
flags & ReactiveFlags.Dirty ||
|
|
280
|
+
(flags & ReactiveFlags.Pending && checkDirty(this.deps!, this))
|
|
281
|
+
) {
|
|
282
|
+
run()
|
|
283
|
+
} else {
|
|
284
|
+
this.flags = ReactiveFlags.Watching
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
stop(): void {
|
|
289
|
+
this.flags = ReactiveFlags.None
|
|
290
|
+
this.depsTail = undefined
|
|
291
|
+
purgeDeps(this)
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
run()
|
|
296
|
+
|
|
297
|
+
return effectObj
|
|
298
|
+
}
|
package/src/batch.ts
ADDED
package/src/index.ts
CHANGED
package/src/store.ts
CHANGED
|
@@ -1,77 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* Called when a listener subscribes to the store.
|
|
15
|
-
*
|
|
16
|
-
* @return a function to unsubscribe the listener
|
|
17
|
-
*/
|
|
18
|
-
onSubscribe?: (
|
|
19
|
-
listener: Listener<TState>,
|
|
20
|
-
store: Store<TState, TUpdater>,
|
|
21
|
-
) => () => void
|
|
22
|
-
/**
|
|
23
|
-
* Called after the state has been updated, used to derive other state.
|
|
24
|
-
*/
|
|
25
|
-
onUpdate?: () => void
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class Store<
|
|
29
|
-
TState,
|
|
30
|
-
TUpdater extends AnyUpdater = (cb: TState) => TState,
|
|
31
|
-
> {
|
|
32
|
-
listeners = new Set<Listener<TState>>()
|
|
33
|
-
state: TState
|
|
34
|
-
prevState: TState
|
|
35
|
-
options?: StoreOptions<TState, TUpdater>
|
|
36
|
-
|
|
37
|
-
constructor(initialState: TState, options?: StoreOptions<TState, TUpdater>) {
|
|
38
|
-
this.prevState = initialState
|
|
39
|
-
this.state = initialState
|
|
40
|
-
this.options = options
|
|
1
|
+
import { createAtom, toObserver } from './atom'
|
|
2
|
+
import type { Atom, Observer, Subscription } from './types'
|
|
3
|
+
|
|
4
|
+
export class Store<T> {
|
|
5
|
+
private atom: Atom<T>
|
|
6
|
+
constructor(getValue: (prev?: NoInfer<T>) => T)
|
|
7
|
+
constructor(initialValue: T)
|
|
8
|
+
constructor(valueOrFn: T | ((prev?: T) => T)) {
|
|
9
|
+
// createAtom has overloads that return ReadonlyAtom<T> for functions and Atom<T> for values
|
|
10
|
+
// Store always needs Atom<T> for setState, so we assert the return type
|
|
11
|
+
this.atom = createAtom(
|
|
12
|
+
valueOrFn as T | ((prev?: NoInfer<T>) => T),
|
|
13
|
+
) as Atom<T>
|
|
41
14
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.listeners.add(listener)
|
|
45
|
-
const unsub = this.options?.onSubscribe?.(listener, this)
|
|
46
|
-
return () => {
|
|
47
|
-
this.listeners.delete(listener)
|
|
48
|
-
unsub?.()
|
|
49
|
-
}
|
|
15
|
+
public setState(updater: (prev: T) => T) {
|
|
16
|
+
this.atom.set(updater)
|
|
50
17
|
}
|
|
18
|
+
public get state() {
|
|
19
|
+
return this.atom.get()
|
|
20
|
+
}
|
|
21
|
+
public get() {
|
|
22
|
+
return this.state
|
|
23
|
+
}
|
|
24
|
+
public subscribe(
|
|
25
|
+
observerOrFn: Observer<T> | ((value: T) => void),
|
|
26
|
+
): Subscription {
|
|
27
|
+
return this.atom.subscribe(toObserver(observerOrFn))
|
|
28
|
+
}
|
|
29
|
+
}
|
|
51
30
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
setState(updater: TUpdater): void
|
|
58
|
-
setState(updater: Updater<TState> | TUpdater): void {
|
|
59
|
-
this.prevState = this.state
|
|
60
|
-
|
|
61
|
-
if (this.options?.updateFn) {
|
|
62
|
-
this.state = this.options.updateFn(this.prevState)(updater as TUpdater)
|
|
63
|
-
} else {
|
|
64
|
-
if (isUpdaterFunction(updater)) {
|
|
65
|
-
this.state = updater(this.prevState)
|
|
66
|
-
} else {
|
|
67
|
-
this.state = updater as TState
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Always run onUpdate, regardless of batching
|
|
72
|
-
this.options?.onUpdate?.()
|
|
73
|
-
|
|
74
|
-
// Attempt to flush
|
|
75
|
-
__flush(this as never)
|
|
31
|
+
export function createStore<T>(getValue: (prev?: NoInfer<T>) => T): Store<T>
|
|
32
|
+
export function createStore<T>(initialValue: T): Store<T>
|
|
33
|
+
export function createStore<T>(valueOrFn: T | ((prev?: T) => T)): Store<T> {
|
|
34
|
+
if (typeof valueOrFn === 'function') {
|
|
35
|
+
return new Store(valueOrFn as (prev?: NoInfer<T>) => T)
|
|
76
36
|
}
|
|
37
|
+
return new Store(valueOrFn)
|
|
77
38
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,31 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
* @private
|
|
3
|
-
*/
|
|
4
|
-
export type AnyUpdater = (prev: any) => any
|
|
1
|
+
import type { ReactiveNode } from './alien'
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
* Type-safe updater that can be either a function or direct value
|
|
8
|
-
*/
|
|
9
|
-
export type Updater<T> = ((prev: T) => T) | T
|
|
3
|
+
export type Selection<TSelected> = Readable<TSelected>
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
export interface ListenerValue<T> {
|
|
15
|
-
readonly prevVal: T
|
|
16
|
-
readonly currentVal: T
|
|
5
|
+
export interface InteropSubscribable<T> {
|
|
6
|
+
subscribe: (observer: Observer<T>) => Subscription
|
|
17
7
|
}
|
|
18
8
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
// Based on RxJS types
|
|
10
|
+
export type Observer<T> = {
|
|
11
|
+
next?: (value: T) => void
|
|
12
|
+
error?: (err: unknown) => void
|
|
13
|
+
complete?: () => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Subscription {
|
|
17
|
+
unsubscribe: () => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Subscribable<T> extends InteropSubscribable<T> {
|
|
21
|
+
subscribe: ((observer: Observer<T>) => Subscription) &
|
|
22
|
+
((
|
|
23
|
+
next: (value: T) => void,
|
|
24
|
+
error?: (error: any) => void,
|
|
25
|
+
complete?: () => void,
|
|
26
|
+
) => Subscription)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Readable<T> extends Subscribable<T> {
|
|
30
|
+
get: () => T
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface BaseAtom<T> extends Subscribable<T>, Readable<T> {}
|
|
34
|
+
|
|
35
|
+
export interface InternalBaseAtom<T> extends Subscribable<T>, Readable<T> {
|
|
36
|
+
/** @internal */
|
|
37
|
+
_snapshot: T
|
|
38
|
+
/** @internal */
|
|
39
|
+
_update: (getValue?: T | ((snapshot: T) => T)) => boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Atom<T> extends BaseAtom<T> {
|
|
43
|
+
/** Sets the value of the atom using a function. */
|
|
44
|
+
set: ((fn: (prevVal: T) => T) => void) & ((value: T) => void)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AtomOptions<T> {
|
|
48
|
+
compare?: (prev: T, next: T) => boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type AnyAtom = BaseAtom<any>
|
|
52
|
+
|
|
53
|
+
export interface InternalReadonlyAtom<T>
|
|
54
|
+
extends InternalBaseAtom<T>, ReactiveNode {}
|
|
23
55
|
|
|
24
56
|
/**
|
|
25
|
-
*
|
|
57
|
+
* An atom that is read-only and cannot be set.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const atom = createAtom(() => 42);
|
|
63
|
+
* // @ts-expect-error - Cannot set a readonly atom
|
|
64
|
+
* atom.set(43);
|
|
65
|
+
* ```
|
|
26
66
|
*/
|
|
27
|
-
export
|
|
28
|
-
updater: Updater<T>,
|
|
29
|
-
): updater is (prev: T) => T {
|
|
30
|
-
return typeof updater === 'function'
|
|
31
|
-
}
|
|
67
|
+
export interface ReadonlyAtom<T> extends BaseAtom<T> {}
|
package/dist/cjs/derived.cjs
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const store = require("./store.cjs");
|
|
4
|
-
const scheduler = require("./scheduler.cjs");
|
|
5
|
-
class Derived {
|
|
6
|
-
constructor(options) {
|
|
7
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
8
|
-
this._subscriptions = [];
|
|
9
|
-
this.lastSeenDepValues = [];
|
|
10
|
-
this.getDepVals = () => {
|
|
11
|
-
const l = this.options.deps.length;
|
|
12
|
-
const prevDepVals = new Array(l);
|
|
13
|
-
const currDepVals = new Array(l);
|
|
14
|
-
for (let i = 0; i < l; i++) {
|
|
15
|
-
const dep = this.options.deps[i];
|
|
16
|
-
prevDepVals[i] = dep.prevState;
|
|
17
|
-
currDepVals[i] = dep.state;
|
|
18
|
-
}
|
|
19
|
-
this.lastSeenDepValues = currDepVals;
|
|
20
|
-
return {
|
|
21
|
-
prevDepVals,
|
|
22
|
-
currDepVals,
|
|
23
|
-
prevVal: this.prevState ?? void 0
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
this.recompute = () => {
|
|
27
|
-
var _a, _b;
|
|
28
|
-
this.prevState = this.state;
|
|
29
|
-
const depVals = this.getDepVals();
|
|
30
|
-
this.state = this.options.fn(depVals);
|
|
31
|
-
(_b = (_a = this.options).onUpdate) == null ? void 0 : _b.call(_a);
|
|
32
|
-
};
|
|
33
|
-
this.checkIfRecalculationNeededDeeply = () => {
|
|
34
|
-
for (const dep of this.options.deps) {
|
|
35
|
-
if (dep instanceof Derived) {
|
|
36
|
-
dep.checkIfRecalculationNeededDeeply();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
let shouldRecompute = false;
|
|
40
|
-
const lastSeenDepValues = this.lastSeenDepValues;
|
|
41
|
-
const { currDepVals } = this.getDepVals();
|
|
42
|
-
for (let i = 0; i < currDepVals.length; i++) {
|
|
43
|
-
if (currDepVals[i] !== lastSeenDepValues[i]) {
|
|
44
|
-
shouldRecompute = true;
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (shouldRecompute) {
|
|
49
|
-
this.recompute();
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
this.mount = () => {
|
|
53
|
-
this.registerOnGraph();
|
|
54
|
-
this.checkIfRecalculationNeededDeeply();
|
|
55
|
-
return () => {
|
|
56
|
-
this.unregisterFromGraph();
|
|
57
|
-
for (const cleanup of this._subscriptions) {
|
|
58
|
-
cleanup();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
this.subscribe = (listener) => {
|
|
63
|
-
var _a, _b;
|
|
64
|
-
this.listeners.add(listener);
|
|
65
|
-
const unsub = (_b = (_a = this.options).onSubscribe) == null ? void 0 : _b.call(_a, listener, this);
|
|
66
|
-
return () => {
|
|
67
|
-
this.listeners.delete(listener);
|
|
68
|
-
unsub == null ? void 0 : unsub();
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
this.options = options;
|
|
72
|
-
this.state = options.fn({
|
|
73
|
-
prevDepVals: void 0,
|
|
74
|
-
prevVal: void 0,
|
|
75
|
-
currDepVals: this.getDepVals().currDepVals
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
registerOnGraph(deps = this.options.deps) {
|
|
79
|
-
const toSort = /* @__PURE__ */ new Set();
|
|
80
|
-
for (const dep of deps) {
|
|
81
|
-
if (dep instanceof Derived) {
|
|
82
|
-
dep.registerOnGraph();
|
|
83
|
-
this.registerOnGraph(dep.options.deps);
|
|
84
|
-
} else if (dep instanceof store.Store) {
|
|
85
|
-
let relatedLinkedDerivedVals = scheduler.__storeToDerived.get(dep);
|
|
86
|
-
if (!relatedLinkedDerivedVals) {
|
|
87
|
-
relatedLinkedDerivedVals = [this];
|
|
88
|
-
scheduler.__storeToDerived.set(dep, relatedLinkedDerivedVals);
|
|
89
|
-
} else if (!relatedLinkedDerivedVals.includes(this)) {
|
|
90
|
-
relatedLinkedDerivedVals.push(this);
|
|
91
|
-
toSort.add(relatedLinkedDerivedVals);
|
|
92
|
-
}
|
|
93
|
-
let relatedStores = scheduler.__derivedToStore.get(this);
|
|
94
|
-
if (!relatedStores) {
|
|
95
|
-
relatedStores = /* @__PURE__ */ new Set();
|
|
96
|
-
scheduler.__derivedToStore.set(this, relatedStores);
|
|
97
|
-
}
|
|
98
|
-
relatedStores.add(dep);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
for (const arr of toSort) {
|
|
102
|
-
arr.sort((a, b) => {
|
|
103
|
-
if (a instanceof Derived && a.options.deps.includes(b)) return 1;
|
|
104
|
-
if (b instanceof Derived && b.options.deps.includes(a)) return -1;
|
|
105
|
-
return 0;
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
unregisterFromGraph(deps = this.options.deps) {
|
|
110
|
-
for (const dep of deps) {
|
|
111
|
-
if (dep instanceof Derived) {
|
|
112
|
-
this.unregisterFromGraph(dep.options.deps);
|
|
113
|
-
} else if (dep instanceof store.Store) {
|
|
114
|
-
const relatedLinkedDerivedVals = scheduler.__storeToDerived.get(dep);
|
|
115
|
-
if (relatedLinkedDerivedVals) {
|
|
116
|
-
relatedLinkedDerivedVals.splice(
|
|
117
|
-
relatedLinkedDerivedVals.indexOf(this),
|
|
118
|
-
1
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
const relatedStores = scheduler.__derivedToStore.get(this);
|
|
122
|
-
if (relatedStores) {
|
|
123
|
-
relatedStores.delete(dep);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
exports.Derived = Derived;
|
|
130
|
-
//# sourceMappingURL=derived.cjs.map
|
package/dist/cjs/derived.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"derived.cjs","sources":["../../src/derived.ts"],"sourcesContent":["import { Store } from './store'\nimport { __derivedToStore, __storeToDerived } from './scheduler'\nimport type { Listener } from './types'\n\nexport type UnwrapDerivedOrStore<T> =\n T extends Derived<infer InnerD>\n ? InnerD\n : T extends Store<infer InnerS>\n ? InnerS\n : never\n\ntype UnwrapReadonlyDerivedOrStoreArray<\n TArr extends ReadonlyArray<Derived<any> | Store<any>>,\n> = TArr extends readonly []\n ? []\n : TArr extends readonly [infer Head, ...infer Tail]\n ? Head extends Derived<any> | Store<any>\n ? Tail extends ReadonlyArray<Derived<any> | Store<any>>\n ? [\n UnwrapDerivedOrStore<Head>,\n ...UnwrapReadonlyDerivedOrStoreArray<Tail>,\n ]\n : [UnwrapDerivedOrStore<Head>]\n : []\n : TArr extends ReadonlyArray<Derived<any> | Store<any>>\n ? Array<UnwrapDerivedOrStore<TArr[number]>>\n : []\n\n// Can't have currVal, as it's being evaluated from the current derived fn\nexport interface DerivedFnProps<\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n TUnwrappedArr extends\n UnwrapReadonlyDerivedOrStoreArray<TArr> = UnwrapReadonlyDerivedOrStoreArray<TArr>,\n> {\n // `undefined` if it's the first run\n /**\n * `undefined` if it's the first run\n * @privateRemarks this also cannot be typed as TState, as it breaks the inferencing of the function's return type when an argument is used - even with `NoInfer` usage\n */\n prevVal: unknown | undefined\n prevDepVals: TUnwrappedArr | undefined\n currDepVals: TUnwrappedArr\n}\n\nexport interface DerivedOptions<\n TState,\n TArr extends ReadonlyArray<Derived<any> | Store<any>> = ReadonlyArray<any>,\n> {\n onSubscribe?: (\n listener: Listener<TState>,\n derived: Derived<TState>,\n ) => () => void\n onUpdate?: () => void\n deps: TArr\n /**\n * Values of the `deps` from before and after the current invocation of `fn`\n */\n fn: (props: DerivedFnProps<TArr>) => TState\n}\n\nexport class Derived<\n TState,\n const TArr extends ReadonlyArray<\n Derived<any> | Store<any>\n > = ReadonlyArray<any>,\n> {\n listeners = new Set<Listener<TState>>()\n state: TState\n prevState: TState | undefined\n options: DerivedOptions<TState, TArr>\n\n /**\n * Functions representing the subscriptions. Call a function to cleanup\n * @private\n */\n _subscriptions: Array<() => void> = []\n\n lastSeenDepValues: Array<unknown> = []\n getDepVals = () => {\n const l = this.options.deps.length\n const prevDepVals = new Array<unknown>(l)\n const currDepVals = new Array<unknown>(l)\n for (let i = 0; i < l; i++) {\n const dep = this.options.deps[i]!\n prevDepVals[i] = dep.prevState\n currDepVals[i] = dep.state\n }\n this.lastSeenDepValues = currDepVals\n return {\n prevDepVals,\n currDepVals,\n prevVal: this.prevState ?? undefined,\n }\n }\n\n constructor(options: DerivedOptions<TState, TArr>) {\n this.options = options\n this.state = options.fn({\n prevDepVals: undefined,\n prevVal: undefined,\n currDepVals: this.getDepVals().currDepVals as never,\n })\n }\n\n registerOnGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n const toSort = new Set<Array<Derived<unknown>>>()\n for (const dep of deps) {\n if (dep instanceof Derived) {\n // First register the intermediate derived value if it's not already registered\n dep.registerOnGraph()\n // Then register this derived with the dep's underlying stores\n this.registerOnGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n // Register the derived as related derived to the store\n let relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (!relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals = [this as never]\n __storeToDerived.set(dep, relatedLinkedDerivedVals)\n } else if (!relatedLinkedDerivedVals.includes(this as never)) {\n relatedLinkedDerivedVals.push(this as never)\n toSort.add(relatedLinkedDerivedVals)\n }\n\n // Register the store as a related store to this derived\n let relatedStores = __derivedToStore.get(this as never)\n if (!relatedStores) {\n relatedStores = new Set()\n __derivedToStore.set(this as never, relatedStores)\n }\n relatedStores.add(dep)\n }\n }\n for (const arr of toSort) {\n // First sort deriveds by dependency order\n arr.sort((a, b) => {\n // If a depends on b, b should go first\n if (a instanceof Derived && a.options.deps.includes(b)) return 1\n // If b depends on a, a should go first\n if (b instanceof Derived && b.options.deps.includes(a)) return -1\n return 0\n })\n }\n }\n\n unregisterFromGraph(\n deps: ReadonlyArray<Derived<any> | Store<any>> = this.options.deps,\n ) {\n for (const dep of deps) {\n if (dep instanceof Derived) {\n this.unregisterFromGraph(dep.options.deps)\n } else if (dep instanceof Store) {\n const relatedLinkedDerivedVals = __storeToDerived.get(dep)\n if (relatedLinkedDerivedVals) {\n relatedLinkedDerivedVals.splice(\n relatedLinkedDerivedVals.indexOf(this as never),\n 1,\n )\n }\n\n const relatedStores = __derivedToStore.get(this as never)\n if (relatedStores) {\n relatedStores.delete(dep)\n }\n }\n }\n }\n\n recompute = () => {\n this.prevState = this.state\n const depVals = this.getDepVals()\n this.state = this.options.fn(depVals as never)\n\n this.options.onUpdate?.()\n }\n\n checkIfRecalculationNeededDeeply = () => {\n for (const dep of this.options.deps) {\n if (dep instanceof Derived) {\n dep.checkIfRecalculationNeededDeeply()\n }\n }\n let shouldRecompute = false\n const lastSeenDepValues = this.lastSeenDepValues\n const { currDepVals } = this.getDepVals()\n for (let i = 0; i < currDepVals.length; i++) {\n if (currDepVals[i] !== lastSeenDepValues[i]) {\n shouldRecompute = true\n break\n }\n }\n\n if (shouldRecompute) {\n this.recompute()\n }\n }\n\n mount = () => {\n this.registerOnGraph()\n this.checkIfRecalculationNeededDeeply()\n\n return () => {\n this.unregisterFromGraph()\n for (const cleanup of this._subscriptions) {\n cleanup()\n }\n }\n }\n\n subscribe = (listener: Listener<TState>) => {\n this.listeners.add(listener)\n const unsub = this.options.onSubscribe?.(listener, this)\n return () => {\n this.listeners.delete(listener)\n unsub?.()\n }\n }\n}\n"],"names":["Store","__storeToDerived","__derivedToStore"],"mappings":"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"}
|