atom.io 0.33.20 → 0.34.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/internal/index.d.ts +5 -6
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1257 -1249
- package/dist/internal/index.js.map +1 -1
- package/dist/react/index.js +22 -11
- package/dist/react/index.js.map +1 -1
- package/dist/realtime-client/index.js +2 -2
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js +0 -1
- package/dist/realtime-testing/index.js.map +1 -1
- package/package.json +2 -2
- package/src/internal/atom/create-regular-atom.ts +6 -7
- package/src/internal/caching.ts +10 -9
- package/src/internal/future.ts +3 -0
- package/src/internal/get-state/read-or-compute-value.ts +12 -7
- package/src/internal/mutable/create-mutable-atom.ts +2 -4
- package/src/internal/selector/create-readonly-held-selector.ts +10 -0
- package/src/internal/selector/create-readonly-pure-selector.ts +13 -4
- package/src/internal/selector/create-writable-held-selector.ts +11 -2
- package/src/internal/selector/create-writable-pure-selector.ts +11 -1
- package/src/internal/selector/trace-selector-atoms.ts +18 -40
- package/src/internal/selector/update-selector-atoms.ts +5 -5
- package/src/internal/set-state/evict-downstream.ts +2 -2
- package/src/internal/set-state/reset-atom-or-selector.ts +3 -3
- package/src/internal/store/store.ts +0 -1
- package/src/internal/subscribe/subscribe-to-root-atoms.ts +42 -38
- package/src/internal/subscribe/subscribe-to-state.ts +23 -11
- package/src/react/use-loadable.ts +18 -13
- package/src/realtime-client/realtime-client-stores/client-sync-store.ts +2 -2
- package/src/realtime-testing/setup-realtime-test.tsx +0 -5
|
@@ -29,8 +29,18 @@ export const createWritableHeldSelector = <T extends object>(
|
|
|
29
29
|
const getterToolkit = { find, get, json }
|
|
30
30
|
|
|
31
31
|
const getSelf = (getFn = options.get, innerTarget = newest(store)): T => {
|
|
32
|
+
const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
|
|
33
|
+
downstreamSelectorKey: key,
|
|
34
|
+
})
|
|
35
|
+
for (const [downstreamSelectorKey, { source }] of upstreamStates) {
|
|
36
|
+
if (source !== key) {
|
|
37
|
+
innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
innerTarget.selectorAtoms.delete(key)
|
|
32
41
|
getFn(getterToolkit, constant)
|
|
33
42
|
cacheValue(innerTarget, key, constant, subject)
|
|
43
|
+
store.logger.info(`✨`, type, key, `=`, constant)
|
|
34
44
|
covered.clear()
|
|
35
45
|
return constant
|
|
36
46
|
}
|
|
@@ -57,8 +67,7 @@ export const createWritableHeldSelector = <T extends object>(
|
|
|
57
67
|
...(family && { family }),
|
|
58
68
|
}
|
|
59
69
|
target.writableSelectors.set(key, mySelector)
|
|
60
|
-
const initialValue = getSelf()
|
|
61
|
-
store.logger.info(`✨`, type, key, `=`, initialValue)
|
|
70
|
+
// const initialValue = getSelf()
|
|
62
71
|
|
|
63
72
|
const token: WritableHeldSelectorToken<T> = { key, type }
|
|
64
73
|
if (family) {
|
|
@@ -29,8 +29,18 @@ export const createWritablePureSelector = <T>(
|
|
|
29
29
|
const getterToolkit = { find, get, json }
|
|
30
30
|
|
|
31
31
|
const getSelf = (getFn = options.get, innerTarget = newest(store)): T => {
|
|
32
|
+
const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
|
|
33
|
+
downstreamSelectorKey: key,
|
|
34
|
+
})
|
|
35
|
+
for (const [downstreamSelectorKey, { source }] of upstreamStates) {
|
|
36
|
+
if (source !== key) {
|
|
37
|
+
innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
innerTarget.selectorAtoms.delete(key)
|
|
32
41
|
const value = getFn(getterToolkit)
|
|
33
|
-
cacheValue(innerTarget, key, value, subject)
|
|
42
|
+
const cached = cacheValue(innerTarget, key, value, subject)
|
|
43
|
+
store.logger.info(`✨`, type, key, `=`, cached)
|
|
34
44
|
covered.clear()
|
|
35
45
|
return value
|
|
36
46
|
}
|
|
@@ -1,51 +1,29 @@
|
|
|
1
|
-
import type { Atom,
|
|
2
|
-
import type { AtomKey, StateKey } from "../keys"
|
|
1
|
+
import type { Atom, Store } from ".."
|
|
3
2
|
import { isAtomKey } from "../keys"
|
|
4
3
|
import { getSelectorDependencyKeys } from "./get-selector-dependency-keys"
|
|
5
4
|
|
|
6
|
-
export const
|
|
5
|
+
export const traceRootSelectorAtoms = (
|
|
7
6
|
store: Store,
|
|
8
|
-
|
|
9
|
-
covered: Set<string
|
|
10
|
-
):
|
|
11
|
-
const
|
|
7
|
+
selectorKey: string,
|
|
8
|
+
covered: Set<string> = new Set<string>(),
|
|
9
|
+
): Map<string, Atom<unknown>> => {
|
|
10
|
+
const dependencies = getSelectorDependencyKeys(store, selectorKey)
|
|
12
11
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
)
|
|
17
|
-
while (indirectDependencyKeys.length > 0) {
|
|
12
|
+
const roots = new Map<string, Atom<unknown>>()
|
|
13
|
+
|
|
14
|
+
while (dependencies.length > 0) {
|
|
18
15
|
// biome-ignore lint/style/noNonNullAssertion: just checked length ^^^
|
|
19
|
-
const
|
|
20
|
-
if (covered.has(
|
|
16
|
+
const dependencyKey = dependencies.pop()!
|
|
17
|
+
if (covered.has(dependencyKey)) {
|
|
21
18
|
continue
|
|
22
19
|
}
|
|
23
|
-
covered.add(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
29
|
-
} else if (!rootKeys.includes(indirectDependencyKey)) {
|
|
30
|
-
rootKeys.push(indirectDependencyKey)
|
|
20
|
+
covered.add(dependencyKey)
|
|
21
|
+
if (isAtomKey(store, dependencyKey)) {
|
|
22
|
+
const atom = store.atoms.get(dependencyKey) as Atom<unknown>
|
|
23
|
+
roots.set(atom.key, atom)
|
|
24
|
+
} else {
|
|
25
|
+
dependencies.push(...getSelectorDependencyKeys(store, dependencyKey))
|
|
31
26
|
}
|
|
32
27
|
}
|
|
33
|
-
|
|
34
|
-
return rootKeys
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const traceAllSelectorAtoms = (
|
|
38
|
-
selector: Selector<any>,
|
|
39
|
-
store: Store,
|
|
40
|
-
): Atom<unknown>[] => {
|
|
41
|
-
const selectorKey = selector.key
|
|
42
|
-
const directDependencyKeys = getSelectorDependencyKeys(store, selectorKey)
|
|
43
|
-
const covered = new Set<string>()
|
|
44
|
-
return directDependencyKeys
|
|
45
|
-
.flatMap((depKey) =>
|
|
46
|
-
isAtomKey(store, depKey)
|
|
47
|
-
? depKey
|
|
48
|
-
: traceSelectorAtoms(store, depKey, covered),
|
|
49
|
-
)
|
|
50
|
-
.map((atomKey) => store.atoms.get(atomKey) as Atom<unknown>)
|
|
28
|
+
return roots
|
|
51
29
|
}
|
|
@@ -2,7 +2,7 @@ import type { ReadonlyPureSelectorToken, WritableToken } from "atom.io"
|
|
|
2
2
|
|
|
3
3
|
import { newest } from "../lineage"
|
|
4
4
|
import type { Store } from "../store"
|
|
5
|
-
import {
|
|
5
|
+
import { traceRootSelectorAtoms } from "./trace-selector-atoms"
|
|
6
6
|
|
|
7
7
|
export const updateSelectorAtoms = (
|
|
8
8
|
store: Store,
|
|
@@ -29,16 +29,16 @@ export const updateSelectorAtoms = (
|
|
|
29
29
|
`discovers root atom "${dependencyKey}"`,
|
|
30
30
|
)
|
|
31
31
|
} else {
|
|
32
|
-
const rootKeys =
|
|
32
|
+
const rootKeys = traceRootSelectorAtoms(store, dependencyKey, covered)
|
|
33
33
|
store.logger.info(
|
|
34
34
|
`🔍`,
|
|
35
35
|
selectorType,
|
|
36
36
|
selectorKey,
|
|
37
|
-
`discovers root atoms: [ ${rootKeys
|
|
38
|
-
.map((
|
|
37
|
+
`discovers root atoms: [ ${[...rootKeys.values()]
|
|
38
|
+
.map((root) => `"${root.key}"`)
|
|
39
39
|
.join(`, `)} ]`,
|
|
40
40
|
)
|
|
41
|
-
for (const atomKey of rootKeys) {
|
|
41
|
+
for (const { key: atomKey } of rootKeys.values()) {
|
|
42
42
|
target.selectorAtoms = target.selectorAtoms.set({
|
|
43
43
|
selectorKey,
|
|
44
44
|
atomKey,
|
|
@@ -29,7 +29,7 @@ export function evictDownStream(store: Store, atom: Atom<any>): void {
|
|
|
29
29
|
if (isDone(target, key)) {
|
|
30
30
|
continue
|
|
31
31
|
}
|
|
32
|
-
evictCachedValue(
|
|
32
|
+
evictCachedValue(target, key)
|
|
33
33
|
markDone(target, key)
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -49,7 +49,7 @@ export function evictDownStreamFromSelector(
|
|
|
49
49
|
if (isDone(target, downstreamSelectorKey)) {
|
|
50
50
|
continue
|
|
51
51
|
}
|
|
52
|
-
evictCachedValue(
|
|
52
|
+
evictCachedValue(target, downstreamSelectorKey)
|
|
53
53
|
markDone(target, downstreamSelectorKey)
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Atom, WritableState } from ".."
|
|
2
|
-
import {
|
|
2
|
+
import { traceRootSelectorAtoms } from ".."
|
|
3
3
|
import type { Store } from "../store"
|
|
4
4
|
import { setAtom } from "./set-atom"
|
|
5
5
|
|
|
@@ -23,8 +23,8 @@ export function resetAtomOrSelector(
|
|
|
23
23
|
case `writable_pure_selector`:
|
|
24
24
|
case `writable_held_selector`:
|
|
25
25
|
{
|
|
26
|
-
const atoms =
|
|
27
|
-
for (const atom of atoms) {
|
|
26
|
+
const atoms = traceRootSelectorAtoms(store, state.key)
|
|
27
|
+
for (const atom of atoms.values()) {
|
|
28
28
|
resetAtom(store, atom)
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,46 +1,50 @@
|
|
|
1
|
-
import type { Selector } from ".."
|
|
1
|
+
import type { Atom, Selector } from ".."
|
|
2
2
|
import { readOrComputeValue } from "../get-state/read-or-compute-value"
|
|
3
|
-
import { newest } from "../lineage"
|
|
4
|
-
import { traceAllSelectorAtoms } from "../selector"
|
|
5
3
|
import type { Store } from "../store"
|
|
6
4
|
import { recallState } from "./recall-state"
|
|
7
5
|
|
|
8
|
-
export const
|
|
9
|
-
|
|
10
|
-
selector: Selector<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`->`,
|
|
27
|
-
atomChange.newValue,
|
|
28
|
-
)
|
|
29
|
-
const oldValue = recallState(target, selector)
|
|
30
|
-
const newValue = readOrComputeValue(target, selector)
|
|
31
|
-
store.logger.info(
|
|
32
|
-
`✨`,
|
|
33
|
-
selector.type,
|
|
34
|
-
selector.key,
|
|
35
|
-
`went`,
|
|
36
|
-
oldValue,
|
|
37
|
-
`->`,
|
|
38
|
-
newValue,
|
|
39
|
-
)
|
|
40
|
-
selector.subject.next({ newValue, oldValue })
|
|
41
|
-
},
|
|
6
|
+
export const subscribeToRootDependency = (
|
|
7
|
+
target: Store,
|
|
8
|
+
selector: Selector<any>,
|
|
9
|
+
atom: Atom<any>,
|
|
10
|
+
): (() => void) => {
|
|
11
|
+
return atom.subject.subscribe(
|
|
12
|
+
`${selector.type}:${selector.key}`,
|
|
13
|
+
(atomChange) => {
|
|
14
|
+
target.logger.info(
|
|
15
|
+
`📢`,
|
|
16
|
+
selector.type,
|
|
17
|
+
selector.key,
|
|
18
|
+
`root`,
|
|
19
|
+
atom.key,
|
|
20
|
+
`went`,
|
|
21
|
+
atomChange.oldValue,
|
|
22
|
+
`->`,
|
|
23
|
+
atomChange.newValue,
|
|
42
24
|
)
|
|
25
|
+
const oldValue = recallState(target, selector)
|
|
26
|
+
const newValue = readOrComputeValue(target, selector)
|
|
27
|
+
target.logger.info(
|
|
28
|
+
`✨`,
|
|
29
|
+
selector.type,
|
|
30
|
+
selector.key,
|
|
31
|
+
`went`,
|
|
32
|
+
oldValue,
|
|
33
|
+
`->`,
|
|
34
|
+
newValue,
|
|
35
|
+
)
|
|
36
|
+
selector.subject.next({ newValue, oldValue })
|
|
43
37
|
},
|
|
44
38
|
)
|
|
45
|
-
return dependencySubscriptions
|
|
46
39
|
}
|
|
40
|
+
|
|
41
|
+
// export const subscribeToRootAtoms = (
|
|
42
|
+
// store: Store,
|
|
43
|
+
// selector: Selector<any>,
|
|
44
|
+
// ): (() => void)[] => {
|
|
45
|
+
// const target = newest(store)
|
|
46
|
+
// const dependencySubscriptions = traceAllSelectorAtoms(selector, store).map(
|
|
47
|
+
// (atom) => subscribeToRootDependency(target, selector, atom),
|
|
48
|
+
// )
|
|
49
|
+
// return dependencySubscriptions
|
|
50
|
+
// }
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ReadableToken, StateUpdate, UpdateHandler } from "atom.io"
|
|
2
2
|
|
|
3
|
+
import { readOrComputeValue } from "../get-state"
|
|
4
|
+
import { traceRootSelectorAtoms } from "../selector"
|
|
3
5
|
import type { Store } from "../store"
|
|
4
6
|
import { withdraw } from "../store"
|
|
5
|
-
import {
|
|
7
|
+
import { subscribeToRootDependency } from "./subscribe-to-root-atoms"
|
|
6
8
|
|
|
7
9
|
export function subscribeToState<T>(
|
|
8
10
|
store: Store,
|
|
@@ -28,14 +30,26 @@ export function subscribeToState<T>(
|
|
|
28
30
|
const isSelector =
|
|
29
31
|
state.type === `writable_pure_selector` ||
|
|
30
32
|
state.type === `readonly_pure_selector`
|
|
31
|
-
|
|
33
|
+
const rootSubs = new Map<string, () => void>()
|
|
32
34
|
let updateHandler: UpdateHandler<T> = safelyHandleUpdate
|
|
33
35
|
if (isSelector) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
readOrComputeValue(store, state)
|
|
37
|
+
for (const [atomKey, atom] of traceRootSelectorAtoms(store, state.key)) {
|
|
38
|
+
rootSubs.set(atomKey, subscribeToRootDependency(store, state, atom))
|
|
39
|
+
}
|
|
40
|
+
updateHandler = function updateRootsBeforeHandlingUpdate(update) {
|
|
41
|
+
const dependencies = traceRootSelectorAtoms(store, state.key)
|
|
42
|
+
for (const [previousRootKey, unsub] of rootSubs) {
|
|
43
|
+
const currentRoot = dependencies.get(previousRootKey)
|
|
44
|
+
if (currentRoot) {
|
|
45
|
+
dependencies.delete(previousRootKey)
|
|
46
|
+
} else {
|
|
47
|
+
unsub()
|
|
48
|
+
rootSubs.delete(previousRootKey)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const [atomKey, atom] of dependencies) {
|
|
52
|
+
rootSubs.set(atomKey, subscribeToRootDependency(store, state, atom))
|
|
39
53
|
}
|
|
40
54
|
safelyHandleUpdate(update)
|
|
41
55
|
}
|
|
@@ -49,10 +63,8 @@ export function subscribeToState<T>(
|
|
|
49
63
|
`Removing subscription "${key}"`,
|
|
50
64
|
)
|
|
51
65
|
mainUnsubFunction()
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
unsubFromDependency()
|
|
55
|
-
}
|
|
66
|
+
for (const unsubFromDependency of rootSubs.values()) {
|
|
67
|
+
unsubFromDependency()
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
70
|
|
|
@@ -31,7 +31,7 @@ export function useLoadable(
|
|
|
31
31
|
| readonly [ReadableToken<any>, unknown]
|
|
32
32
|
| readonly [ReadableToken<any>]
|
|
33
33
|
): `LOADING` | { loading: boolean; value: unknown } {
|
|
34
|
-
let
|
|
34
|
+
let state: unknown
|
|
35
35
|
let fallback: unknown
|
|
36
36
|
|
|
37
37
|
const [token] = params
|
|
@@ -43,7 +43,7 @@ export function useLoadable(
|
|
|
43
43
|
case `readonly_pure_selector`:
|
|
44
44
|
case `writable_held_selector`:
|
|
45
45
|
case `writable_pure_selector`:
|
|
46
|
-
|
|
46
|
+
state = useO(token)
|
|
47
47
|
fallback = params[1]
|
|
48
48
|
break
|
|
49
49
|
case `atom_family`:
|
|
@@ -53,27 +53,32 @@ export function useLoadable(
|
|
|
53
53
|
case `writable_held_selector_family`:
|
|
54
54
|
case `writable_pure_selector_family`:
|
|
55
55
|
key = params[1] as Canonical
|
|
56
|
-
|
|
56
|
+
state = useO(token, key)
|
|
57
57
|
fallback = params[2]
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
const wrapperRef = React.useRef({ loading: false, value: null as unknown })
|
|
60
61
|
const lastLoadedRef = React.useRef(
|
|
61
|
-
fallback ?? (
|
|
62
|
+
fallback ?? (state instanceof Promise ? `LOADING` : state),
|
|
62
63
|
)
|
|
64
|
+
|
|
63
65
|
const { current: lastLoaded } = lastLoadedRef
|
|
64
|
-
|
|
66
|
+
let { current: wrapper } = wrapperRef
|
|
67
|
+
|
|
68
|
+
if (state instanceof Promise) {
|
|
65
69
|
if (lastLoaded === `LOADING`) {
|
|
66
70
|
return `LOADING`
|
|
67
71
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
wrapper = wrapperRef.current = { loading: true, value: lastLoaded }
|
|
73
|
+
} else {
|
|
74
|
+
lastLoadedRef.current = state
|
|
75
|
+
if (wrapper.loading === true) {
|
|
76
|
+
wrapper = wrapperRef.current = { loading: false, value: state }
|
|
77
|
+
} else {
|
|
78
|
+
wrapper.loading = false
|
|
79
|
+
wrapper.value = state
|
|
71
80
|
}
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
loading: false,
|
|
77
|
-
value: loadable,
|
|
78
|
-
}
|
|
83
|
+
return wrapper
|
|
79
84
|
}
|
|
@@ -4,12 +4,12 @@ export const optimisticUpdateQueue: AtomIO.RegularAtomToken<
|
|
|
4
4
|
AtomIO.TransactionUpdate<any>[]
|
|
5
5
|
> = AtomIO.atom<AtomIO.TransactionUpdate<any>[]>({
|
|
6
6
|
key: `updateQueue`,
|
|
7
|
-
default: [],
|
|
7
|
+
default: () => [],
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
export const confirmedUpdateQueue: AtomIO.RegularAtomToken<
|
|
11
11
|
AtomIO.TransactionUpdate<any>[]
|
|
12
12
|
> = AtomIO.atom<AtomIO.TransactionUpdate<any>[]>({
|
|
13
13
|
key: `serverConfirmedUpdateQueue`,
|
|
14
|
-
default: [],
|
|
14
|
+
default: () => [],
|
|
15
15
|
})
|
|
@@ -192,11 +192,6 @@ export const setupRealtimeTestClient = (
|
|
|
192
192
|
auth: { token: `test`, username: `${name}-${testNumber}` },
|
|
193
193
|
})
|
|
194
194
|
const silo = new AtomIO.Silo({ name, lifespan: `ephemeral` }, IMPLICIT.STORE)
|
|
195
|
-
for (const [key, value] of silo.store.valueMap.entries()) {
|
|
196
|
-
if (Array.isArray(value)) {
|
|
197
|
-
silo.store.valueMap.set(key, [...value])
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
195
|
silo.setState(RTC.myUsernameState, `${name}-${testNumber}`)
|
|
201
196
|
|
|
202
197
|
const { document } = new Happy.Window()
|