atom.io 0.33.21 → 0.34.1
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/eslint-plugin/index.js +1 -2
- package/dist/eslint-plugin/index.js.map +1 -1
- package/dist/internal/index.d.ts +10 -6
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1262 -1253
- package/dist/internal/index.js.map +1 -1
- package/dist/json/index.d.ts +3 -0
- package/dist/json/index.d.ts.map +1 -1
- package/dist/json/index.js.map +1 -1
- package/dist/main/index.d.ts +405 -23
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js +107 -11
- package/dist/main/index.js.map +1 -1
- package/dist/realtime-client/index.js +6 -10
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-server/index.js +1 -1
- package/dist/realtime-server/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 +6 -6
- package/src/internal/atom/create-regular-atom.ts +6 -7
- package/src/internal/caching.ts +9 -9
- package/src/internal/future.ts +3 -0
- package/src/internal/get-state/read-or-compute-value.ts +12 -7
- package/src/internal/join/create-join.ts +27 -0
- package/src/internal/join/index.ts +1 -0
- package/src/internal/junction.ts +2 -0
- 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/json/index.ts +3 -0
- package/src/main/atom.ts +54 -13
- package/src/main/dispose-state.ts +8 -2
- package/src/main/find-state.ts +44 -14
- package/src/main/get-state.ts +2 -2
- package/src/main/index.ts +8 -0
- package/src/main/join.ts +79 -22
- package/src/main/realm.ts +50 -4
- package/src/main/selector.ts +116 -6
- package/src/main/subscribe.ts +31 -1
- package/src/main/timeline.ts +17 -0
- package/src/main/transaction.ts +39 -3
- package/src/realtime-client/realtime-client-stores/client-sync-store.ts +2 -2
- package/src/realtime-testing/setup-realtime-test.tsx +0 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReadableState } from ".."
|
|
2
|
-
import { readCachedValue } from "../caching"
|
|
2
|
+
import { cacheValue, readCachedValue } from "../caching"
|
|
3
3
|
import type { Store } from "../store"
|
|
4
4
|
|
|
5
5
|
export const readOrComputeValue = <T>(
|
|
@@ -7,7 +7,6 @@ export const readOrComputeValue = <T>(
|
|
|
7
7
|
state: ReadableState<T>,
|
|
8
8
|
): T => {
|
|
9
9
|
if (target.valueMap.has(state.key)) {
|
|
10
|
-
target.logger.info(`📖`, state.type, state.key, `reading cached value`)
|
|
11
10
|
return readCachedValue(state, target)
|
|
12
11
|
}
|
|
13
12
|
switch (state.type) {
|
|
@@ -20,20 +19,26 @@ export const readOrComputeValue = <T>(
|
|
|
20
19
|
case `atom`:
|
|
21
20
|
case `mutable_atom`: {
|
|
22
21
|
const def = state.default
|
|
23
|
-
let
|
|
22
|
+
let defaultValue: T
|
|
24
23
|
if (def instanceof Function) {
|
|
25
|
-
|
|
24
|
+
defaultValue = def()
|
|
26
25
|
} else {
|
|
27
|
-
|
|
26
|
+
defaultValue = def
|
|
28
27
|
}
|
|
28
|
+
const cachedValue = cacheValue(
|
|
29
|
+
target,
|
|
30
|
+
state.key,
|
|
31
|
+
defaultValue,
|
|
32
|
+
state.subject,
|
|
33
|
+
)
|
|
29
34
|
target.logger.info(
|
|
30
35
|
`💁`,
|
|
31
36
|
`atom`,
|
|
32
37
|
state.key,
|
|
33
38
|
`could not find cached value; using default`,
|
|
34
|
-
|
|
39
|
+
defaultValue,
|
|
35
40
|
)
|
|
36
|
-
return
|
|
41
|
+
return cachedValue
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { JoinOptions, JoinToken } from "atom.io"
|
|
2
|
+
import type { Store } from "atom.io/internal"
|
|
3
|
+
import { Join } from "atom.io/internal"
|
|
4
|
+
import type { Json } from "atom.io/json"
|
|
5
|
+
|
|
6
|
+
export function createJoin<
|
|
7
|
+
ASide extends string,
|
|
8
|
+
AType extends string,
|
|
9
|
+
BSide extends string,
|
|
10
|
+
BType extends string,
|
|
11
|
+
Cardinality extends `1:1` | `1:n` | `n:n`,
|
|
12
|
+
Content extends Json.Object,
|
|
13
|
+
>(
|
|
14
|
+
store: Store,
|
|
15
|
+
options: JoinOptions<ASide, AType, BSide, BType, Cardinality, Content>,
|
|
16
|
+
defaultContent: Content | undefined,
|
|
17
|
+
): JoinToken<ASide, AType, BSide, BType, Cardinality, Content> {
|
|
18
|
+
store.joins.set(options.key, new Join(options, defaultContent, store))
|
|
19
|
+
const token: JoinToken<ASide, AType, BSide, BType, Cardinality, Content> = {
|
|
20
|
+
key: options.key,
|
|
21
|
+
type: `join`,
|
|
22
|
+
a: options.between[0],
|
|
23
|
+
b: options.between[1],
|
|
24
|
+
cardinality: options.cardinality,
|
|
25
|
+
}
|
|
26
|
+
return token
|
|
27
|
+
}
|
package/src/internal/junction.ts
CHANGED
|
@@ -18,7 +18,9 @@ export interface JunctionEntries<
|
|
|
18
18
|
JunctionEntriesBase<AType, BType, Content> {}
|
|
19
19
|
|
|
20
20
|
export type JunctionSchemaBase<ASide extends string, BSide extends string> = {
|
|
21
|
+
/** Description of the relationship between the two sides */
|
|
21
22
|
readonly between: [a: ASide, b: BSide]
|
|
23
|
+
/** How many relations are allowed in each direction? */
|
|
22
24
|
readonly cardinality: `1:1` | `1:n` | `n:n`
|
|
23
25
|
}
|
|
24
26
|
export interface JunctionSchema<ASide extends string, BSide extends string>
|
|
@@ -8,7 +8,7 @@ import type { Json } from "atom.io/json"
|
|
|
8
8
|
import { selectJson } from "atom.io/json"
|
|
9
9
|
|
|
10
10
|
import type { MutableAtom } from ".."
|
|
11
|
-
import {
|
|
11
|
+
import { resetInStore, setIntoStore } from ".."
|
|
12
12
|
import { newest } from "../lineage"
|
|
13
13
|
import { deposit, type Store } from "../store"
|
|
14
14
|
import { Subject } from "../subject"
|
|
@@ -31,7 +31,7 @@ export function createMutableAtom<
|
|
|
31
31
|
`creating in store "${store.config.name}"`,
|
|
32
32
|
)
|
|
33
33
|
const target = newest(store)
|
|
34
|
-
const { key
|
|
34
|
+
const { key } = options
|
|
35
35
|
const existing = target.atoms.get(key)
|
|
36
36
|
const type = `mutable_atom`
|
|
37
37
|
if (existing && existing.type === type) {
|
|
@@ -56,9 +56,7 @@ export function createMutableAtom<
|
|
|
56
56
|
if (family) {
|
|
57
57
|
newAtom.family = family
|
|
58
58
|
}
|
|
59
|
-
const initialValue = def()
|
|
60
59
|
target.atoms.set(newAtom.key, newAtom)
|
|
61
|
-
cacheValue(target, key, initialValue, subject)
|
|
62
60
|
const token = deposit(newAtom)
|
|
63
61
|
if (options.effects) {
|
|
64
62
|
let effectIndex = 0
|
|
@@ -23,6 +23,16 @@ export const createReadonlyHeldSelector = <T extends object>(
|
|
|
23
23
|
const type = `readonly_held_selector` as const
|
|
24
24
|
const { get, find, json } = registerSelector(target, type, key, covered)
|
|
25
25
|
const getSelf = () => {
|
|
26
|
+
const innerTarget = newest(store)
|
|
27
|
+
const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
|
|
28
|
+
downstreamSelectorKey: key,
|
|
29
|
+
})
|
|
30
|
+
for (const [downstreamSelectorKey, { source }] of upstreamStates) {
|
|
31
|
+
if (source !== key) {
|
|
32
|
+
innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
innerTarget.selectorAtoms.delete(key)
|
|
26
36
|
options.get({ get, find, json }, constant)
|
|
27
37
|
cacheValue(newest(store), key, constant, subject)
|
|
28
38
|
covered.clear()
|
|
@@ -23,10 +23,21 @@ export const createReadonlyPureSelector = <T>(
|
|
|
23
23
|
const type = `readonly_pure_selector` as const
|
|
24
24
|
const { get, find, json } = registerSelector(target, type, key, covered)
|
|
25
25
|
const getSelf = () => {
|
|
26
|
+
const innerTarget = newest(store)
|
|
27
|
+
const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
|
|
28
|
+
downstreamSelectorKey: key,
|
|
29
|
+
})
|
|
30
|
+
for (const [downstreamSelectorKey, { source }] of upstreamStates) {
|
|
31
|
+
if (source !== key) {
|
|
32
|
+
innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
innerTarget.selectorAtoms.delete(key)
|
|
26
36
|
const value = options.get({ get, find, json })
|
|
27
|
-
cacheValue(
|
|
37
|
+
const cached = cacheValue(innerTarget, key, value, subject)
|
|
38
|
+
store.logger.info(`✨`, type, key, `=`, cached)
|
|
28
39
|
covered.clear()
|
|
29
|
-
return
|
|
40
|
+
return cached
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
const readonlySelector: ReadonlyPureSelector<T> = {
|
|
@@ -38,8 +49,6 @@ export const createReadonlyPureSelector = <T>(
|
|
|
38
49
|
...(family && { family }),
|
|
39
50
|
}
|
|
40
51
|
target.readonlySelectors.set(key, readonlySelector)
|
|
41
|
-
const initialValue = getSelf()
|
|
42
|
-
store.logger.info(`✨`, type, key, `=`, initialValue)
|
|
43
52
|
const token: ReadonlyPureSelectorToken<T> = {
|
|
44
53
|
key,
|
|
45
54
|
type,
|
|
@@ -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
|
|
package/src/json/index.ts
CHANGED
|
@@ -51,6 +51,9 @@ export const stringifyJson = <J extends Json.Serializable>(
|
|
|
51
51
|
json: J,
|
|
52
52
|
): stringified<J> => JSON.stringify(json) as stringified<J>
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Always serializes to the same string.
|
|
56
|
+
*/
|
|
54
57
|
export type Canonical = primitive | ReadonlyArray<Canonical>
|
|
55
58
|
|
|
56
59
|
export type JsonIO = (...params: Json.Serializable[]) => Json.Serializable | void
|
package/src/main/atom.ts
CHANGED
|
@@ -22,7 +22,6 @@ import type { AtomToken, MutableAtomToken, RegularAtomToken, Setter } from "."
|
|
|
22
22
|
export function atom<T extends Transceiver<any>, J extends Json.Serializable>(
|
|
23
23
|
options: MutableAtomOptions<T, J>,
|
|
24
24
|
): MutableAtomToken<T, J>
|
|
25
|
-
|
|
26
25
|
/**
|
|
27
26
|
* @public
|
|
28
27
|
* Create a regular atom, a global reactive variable in the implicit store
|
|
@@ -71,14 +70,24 @@ export type RegularAtomOptions<T> = {
|
|
|
71
70
|
/** Hooks used to run side effects when the atom is set */
|
|
72
71
|
effects?: AtomEffect<T>[]
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
|
|
74
|
+
/** @public */
|
|
75
|
+
// biome-ignore format: intersection
|
|
76
|
+
export type MutableAtomOptions<
|
|
77
|
+
T extends Transceiver<any>,
|
|
78
|
+
J extends Json.Serializable,
|
|
79
|
+
> =
|
|
76
80
|
& JsonInterface<T, J>
|
|
77
|
-
&
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
& {
|
|
82
|
+
/** Used to signal that the atom is mutable */
|
|
83
|
+
mutable: true
|
|
84
|
+
/** The unique identifier of the atom */
|
|
85
|
+
key: string
|
|
86
|
+
/** A function to create an initial value for the atom */
|
|
87
|
+
default: () => T
|
|
88
|
+
/** Hooks used to run side effects when the atom is set */
|
|
89
|
+
effects?: AtomEffect<T>[]
|
|
90
|
+
}
|
|
82
91
|
|
|
83
92
|
/** @public */
|
|
84
93
|
export type RegularAtomFamilyOptions<T, K extends Canonical> = {
|
|
@@ -91,24 +100,33 @@ export type RegularAtomFamilyOptions<T, K extends Canonical> = {
|
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
export type RegularAtomFamilyToken<T, K extends Canonical> = {
|
|
103
|
+
/** The unique identifier of the atom family */
|
|
94
104
|
key: string
|
|
105
|
+
/** Discriminator */
|
|
95
106
|
type: `atom_family`
|
|
107
|
+
/** Never present. This is a marker that preserves the type of atoms in this family */
|
|
96
108
|
__T?: T
|
|
109
|
+
/** Never present. This is a marker that preserves the type of keys used for atoms in this family */
|
|
97
110
|
__K?: K
|
|
98
111
|
}
|
|
99
112
|
|
|
113
|
+
/** @public */
|
|
100
114
|
// biome-ignore format: intersection
|
|
101
115
|
export type MutableAtomFamilyOptions<
|
|
102
116
|
T extends Transceiver<any>,
|
|
103
117
|
J extends Json.Serializable,
|
|
104
118
|
K extends Canonical,
|
|
105
|
-
> =
|
|
106
|
-
& JsonInterface<T, J>
|
|
107
|
-
& {
|
|
119
|
+
> =
|
|
120
|
+
& JsonInterface<T, J>
|
|
121
|
+
& {
|
|
122
|
+
/** Used to signal that the atoms created from this family are mutable */
|
|
123
|
+
mutable: true
|
|
124
|
+
/** The unique identifier of the atom family */
|
|
108
125
|
key: string
|
|
126
|
+
/** A function to create an initial value for each atom in the family */
|
|
109
127
|
default: (key: K) => T
|
|
128
|
+
/** Hooks used to run side effects when an atom in the family is set */
|
|
110
129
|
effects?: (key: K) => AtomEffect<T>[]
|
|
111
|
-
mutable: true,
|
|
112
130
|
}
|
|
113
131
|
|
|
114
132
|
export type MutableAtomFamilyToken<
|
|
@@ -116,22 +134,45 @@ export type MutableAtomFamilyToken<
|
|
|
116
134
|
J extends Json.Serializable,
|
|
117
135
|
K extends Canonical,
|
|
118
136
|
> = {
|
|
137
|
+
/** The unique identifier of the atom family */
|
|
119
138
|
key: string
|
|
139
|
+
/** Discriminator */
|
|
120
140
|
type: `mutable_atom_family`
|
|
141
|
+
/** Never present. This is a marker that preserves the type of atoms in this family */
|
|
121
142
|
__T?: T
|
|
143
|
+
/** Never present. This is a marker that preserves the type of the JSON form of atoms in this family */
|
|
122
144
|
__J?: J
|
|
145
|
+
/** Never present. This is a marker that preserves the type of keys used for atoms in this family */
|
|
123
146
|
__K?: K
|
|
124
147
|
}
|
|
125
|
-
|
|
126
148
|
export type AtomFamilyToken<T, K extends Canonical = Canonical> =
|
|
127
149
|
| MutableAtomFamilyToken<T extends Transceiver<any> ? T : never, any, K>
|
|
128
150
|
| RegularAtomFamilyToken<T, K>
|
|
129
151
|
|
|
152
|
+
/**
|
|
153
|
+
* @public
|
|
154
|
+
* Create a family of mutable atoms, allowing for the dynamic creation and disposal of atoms.
|
|
155
|
+
*
|
|
156
|
+
* The value of a mutable atom must be some kind of {@link Transceiver}.
|
|
157
|
+
*
|
|
158
|
+
* @param options - {@link MutableAtomFamilyOptions}
|
|
159
|
+
* @returns
|
|
160
|
+
* A reference to the atom family created: a {@link MutableAtomFamilyToken}
|
|
161
|
+
* @overload Mutable
|
|
162
|
+
*/
|
|
130
163
|
export function atomFamily<
|
|
131
164
|
T extends Transceiver<any>,
|
|
132
165
|
J extends Json.Serializable,
|
|
133
166
|
K extends Canonical,
|
|
134
167
|
>(options: MutableAtomFamilyOptions<T, J, K>): MutableAtomFamilyToken<T, J, K>
|
|
168
|
+
/**
|
|
169
|
+
* @public
|
|
170
|
+
* Create a family of regular atoms, allowing for the dynamic creation and disposal of atoms.
|
|
171
|
+
* @param options - {@link RegularAtomFamilyOptions}
|
|
172
|
+
* @returns
|
|
173
|
+
* A reference to the atom family created: a {@link RegularAtomFamilyToken}
|
|
174
|
+
* @overload Regular
|
|
175
|
+
*/
|
|
135
176
|
export function atomFamily<T, K extends Canonical>(
|
|
136
177
|
options: RegularAtomFamilyOptions<T, K>,
|
|
137
178
|
): RegularAtomFamilyToken<T, K>
|
|
@@ -5,14 +5,20 @@ import type { ReadableFamilyToken, ReadableToken } from "."
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @public
|
|
8
|
-
* Disposes of a state in the implicit store
|
|
8
|
+
* Disposes of a state in the implicit store.
|
|
9
|
+
*
|
|
10
|
+
* Only family members can be disposed of.
|
|
11
|
+
*
|
|
9
12
|
* @param token - The token of the state to dispose
|
|
10
13
|
* @overload Default
|
|
11
14
|
*/
|
|
12
15
|
export function disposeState(token: ReadableToken<any>): void
|
|
13
16
|
/**
|
|
14
17
|
* @public
|
|
15
|
-
* Disposes of a state
|
|
18
|
+
* Disposes of a state in the implicit store.
|
|
19
|
+
*
|
|
20
|
+
* Only family members can be disposed of.
|
|
21
|
+
*
|
|
16
22
|
* @param token - The token of the state family to dispose
|
|
17
23
|
* @param key - The unique key of the state to dispose
|
|
18
24
|
*/
|