atom.io 0.2.0 → 0.3.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/README.md +6 -11
- package/dist/index.d.ts +505 -4
- package/dist/index.js +823 -360
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +813 -356
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -5
- package/{dist/react → react/dist}/index.d.ts +2 -5
- package/react/dist/index.js +68 -0
- package/react/dist/index.js.map +1 -0
- package/react/dist/index.mjs +44 -0
- package/react/dist/index.mjs.map +1 -0
- package/react/package.json +12 -3
- package/src/atom.ts +18 -53
- package/src/index.ts +23 -37
- package/src/internal/atom-internal.ts +50 -0
- package/src/internal/families-internal.ts +142 -0
- package/src/internal/get.ts +40 -42
- package/src/internal/index.ts +6 -17
- package/src/internal/is-default.ts +20 -4
- package/src/internal/logger.ts +46 -0
- package/src/internal/operation.ts +97 -14
- package/src/internal/selector-internal.ts +113 -13
- package/src/internal/set.ts +31 -17
- package/src/internal/store.ts +58 -45
- package/src/internal/subscribe-internal.ts +55 -11
- package/src/internal/timeline-internal.ts +196 -0
- package/src/internal/transaction-internal.ts +157 -16
- package/src/react/index.ts +5 -6
- package/src/selector.ts +29 -99
- package/src/subscribe.ts +55 -0
- package/src/timeline.ts +34 -0
- package/src/transaction.ts +22 -34
- package/dist/index-9d9f5a05.d.ts +0 -293
- package/dist/react/index.js +0 -909
- package/dist/react/index.js.map +0 -1
- package/dist/react/index.mjs +0 -880
- package/dist/react/index.mjs.map +0 -1
package/src/internal/index.ts
CHANGED
|
@@ -1,23 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export * from "./atom-internal"
|
|
2
|
+
export * from "./families-internal"
|
|
3
3
|
export * from "./get"
|
|
4
|
-
export * from "./set"
|
|
5
4
|
export * from "./is-default"
|
|
5
|
+
export * from "./logger"
|
|
6
|
+
export * from "./operation"
|
|
6
7
|
export * from "./selector-internal"
|
|
8
|
+
export * from "./set"
|
|
7
9
|
export * from "./store"
|
|
8
10
|
export * from "./subscribe-internal"
|
|
9
|
-
export * from "./
|
|
11
|
+
export * from "./timeline-internal"
|
|
10
12
|
export * from "./transaction-internal"
|
|
11
|
-
|
|
12
|
-
export type Atom<T> = {
|
|
13
|
-
key: string
|
|
14
|
-
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
15
|
-
default: T
|
|
16
|
-
}
|
|
17
|
-
export type Selector<T> = {
|
|
18
|
-
key: string
|
|
19
|
-
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
20
|
-
get: () => T
|
|
21
|
-
set: (newValue: T | ((oldValue: T) => T)) => void
|
|
22
|
-
}
|
|
23
|
-
export type ReadonlySelector<T> = Omit<Selector<T>, `set`>
|
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
import HAMT from "hamt_plus"
|
|
2
|
-
|
|
3
1
|
import type { Store } from "."
|
|
4
|
-
import { IMPLICIT, traceAllSelectorAtoms } from "."
|
|
2
|
+
import { target, IMPLICIT, traceAllSelectorAtoms } from "."
|
|
5
3
|
|
|
6
4
|
export const isAtomDefault = (
|
|
7
5
|
key: string,
|
|
8
6
|
store: Store = IMPLICIT.STORE
|
|
9
7
|
): boolean => {
|
|
10
|
-
|
|
8
|
+
const core = target(store)
|
|
9
|
+
return core.atomsThatAreDefault.has(key)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const markAtomAsDefault = (
|
|
13
|
+
key: string,
|
|
14
|
+
store: Store = IMPLICIT.STORE
|
|
15
|
+
): void => {
|
|
16
|
+
const core = target(store)
|
|
17
|
+
core.atomsThatAreDefault = new Set(core.atomsThatAreDefault).add(key)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const markAtomAsNotDefault = (
|
|
21
|
+
key: string,
|
|
22
|
+
store: Store = IMPLICIT.STORE
|
|
23
|
+
): void => {
|
|
24
|
+
const core = target(store)
|
|
25
|
+
core.atomsThatAreDefault = new Set(target(store).atomsThatAreDefault)
|
|
26
|
+
core.atomsThatAreDefault.delete(key)
|
|
11
27
|
}
|
|
12
28
|
|
|
13
29
|
export const isSelectorDefault = (
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { doNothing } from "~/packages/anvl/src/function"
|
|
2
|
+
|
|
3
|
+
import type { Store } from "./store"
|
|
4
|
+
import { IMPLICIT } from "./store"
|
|
5
|
+
|
|
6
|
+
export type Logger = Pick<Console, `error` | `info` | `warn`>
|
|
7
|
+
export const LOG_LEVELS: ReadonlyArray<keyof Logger> = [
|
|
8
|
+
`info`,
|
|
9
|
+
`warn`,
|
|
10
|
+
`error`,
|
|
11
|
+
] as const
|
|
12
|
+
|
|
13
|
+
export const setLogLevel = (
|
|
14
|
+
preferredLevel: `error` | `info` | `warn` | null,
|
|
15
|
+
store: Store = IMPLICIT.STORE
|
|
16
|
+
): void => {
|
|
17
|
+
const { logger__INTERNAL } = store.config
|
|
18
|
+
if (preferredLevel === null) {
|
|
19
|
+
store.config.logger = null
|
|
20
|
+
} else {
|
|
21
|
+
store.config.logger = { ...console }
|
|
22
|
+
LOG_LEVELS.forEach((logLevel) => {
|
|
23
|
+
if (LOG_LEVELS.indexOf(logLevel) < LOG_LEVELS.indexOf(preferredLevel)) {
|
|
24
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
25
|
+
store.config.logger![logLevel] = doNothing
|
|
26
|
+
} else {
|
|
27
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
28
|
+
store.config.logger![logLevel] = logger__INTERNAL[logLevel]
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const useLogger = (
|
|
35
|
+
logger: Logger,
|
|
36
|
+
store: Store = IMPLICIT.STORE
|
|
37
|
+
): void => {
|
|
38
|
+
const currentLogLevel =
|
|
39
|
+
store.config.logger === null
|
|
40
|
+
? null
|
|
41
|
+
: LOG_LEVELS.find(
|
|
42
|
+
(logLevel) => store.config.logger?.[logLevel] !== doNothing
|
|
43
|
+
) ?? null
|
|
44
|
+
store.config.logger__INTERNAL = { ...logger }
|
|
45
|
+
setLogLevel(currentLogLevel, store)
|
|
46
|
+
}
|
|
@@ -1,49 +1,132 @@
|
|
|
1
|
+
import type { Hamt } from "hamt_plus"
|
|
1
2
|
import HAMT from "hamt_plus"
|
|
2
3
|
|
|
3
4
|
import type { Atom, ReadonlySelector, Selector } from "."
|
|
5
|
+
import { target } from "."
|
|
4
6
|
import type { Store } from "./store"
|
|
5
7
|
import { IMPLICIT } from "./store"
|
|
6
8
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
+
export type OperationProgress =
|
|
10
|
+
| {
|
|
11
|
+
open: false
|
|
12
|
+
}
|
|
13
|
+
| {
|
|
14
|
+
open: true
|
|
15
|
+
done: Set<string>
|
|
16
|
+
prev: Hamt<any, string>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const openOperation = (store: Store): void => {
|
|
20
|
+
const core = target(store)
|
|
21
|
+
core.operation = {
|
|
9
22
|
open: true,
|
|
10
23
|
done: new Set(),
|
|
11
24
|
prev: store.valueMap,
|
|
12
25
|
}
|
|
13
26
|
store.config.logger?.info(`⭕`, `operation start`)
|
|
14
27
|
}
|
|
15
|
-
export const
|
|
16
|
-
|
|
28
|
+
export const closeOperation = (store: Store): void => {
|
|
29
|
+
const core = target(store)
|
|
30
|
+
core.operation = { open: false }
|
|
17
31
|
store.config.logger?.info(`🔴`, `operation done`)
|
|
18
32
|
}
|
|
19
33
|
|
|
20
34
|
export const isDone = (key: string, store: Store = IMPLICIT.STORE): boolean => {
|
|
21
|
-
|
|
35
|
+
const core = target(store)
|
|
36
|
+
if (!core.operation.open) {
|
|
22
37
|
store.config.logger?.warn(
|
|
23
|
-
`isDone called outside of an
|
|
38
|
+
`isDone called outside of an operation. This is probably a bug.`
|
|
24
39
|
)
|
|
25
40
|
return true
|
|
26
41
|
}
|
|
27
|
-
return
|
|
42
|
+
return core.operation.done.has(key)
|
|
28
43
|
}
|
|
29
44
|
export const markDone = (key: string, store: Store = IMPLICIT.STORE): void => {
|
|
30
|
-
|
|
45
|
+
const core = target(store)
|
|
46
|
+
if (!core.operation.open) {
|
|
31
47
|
store.config.logger?.warn(
|
|
32
|
-
`markDone called outside of an
|
|
48
|
+
`markDone called outside of an operation. This is probably a bug.`
|
|
33
49
|
)
|
|
34
50
|
return
|
|
35
51
|
}
|
|
36
|
-
|
|
52
|
+
core.operation.done.add(key)
|
|
37
53
|
}
|
|
38
54
|
export const recallState = <T>(
|
|
39
55
|
state: Atom<T> | ReadonlySelector<T> | Selector<T>,
|
|
40
56
|
store: Store = IMPLICIT.STORE
|
|
41
57
|
): T => {
|
|
42
|
-
|
|
58
|
+
const core = target(store)
|
|
59
|
+
if (!core.operation.open) {
|
|
43
60
|
store.config.logger?.warn(
|
|
44
|
-
`recall called outside of an
|
|
61
|
+
`recall called outside of an operation. This is probably a bug.`
|
|
45
62
|
)
|
|
46
|
-
return HAMT.get(state.key,
|
|
63
|
+
return HAMT.get(state.key, core.valueMap)
|
|
47
64
|
}
|
|
48
|
-
return HAMT.get(state.key,
|
|
65
|
+
return HAMT.get(state.key, core.operation.prev)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const cacheValue = (
|
|
69
|
+
key: string,
|
|
70
|
+
value: unknown,
|
|
71
|
+
store: Store = IMPLICIT.STORE
|
|
72
|
+
): void => {
|
|
73
|
+
const core = target(store)
|
|
74
|
+
core.valueMap = HAMT.set(key, value, core.valueMap)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const evictCachedValue = (
|
|
78
|
+
key: string,
|
|
79
|
+
store: Store = IMPLICIT.STORE
|
|
80
|
+
): void => {
|
|
81
|
+
const core = target(store)
|
|
82
|
+
core.valueMap = HAMT.remove(key, core.valueMap)
|
|
83
|
+
}
|
|
84
|
+
export const readCachedValue = <T>(
|
|
85
|
+
key: string,
|
|
86
|
+
store: Store = IMPLICIT.STORE
|
|
87
|
+
): T => HAMT.get(key, target(store).valueMap)
|
|
88
|
+
|
|
89
|
+
export const isValueCached = (
|
|
90
|
+
key: string,
|
|
91
|
+
store: Store = IMPLICIT.STORE
|
|
92
|
+
): boolean => HAMT.has(key, target(store).valueMap)
|
|
93
|
+
|
|
94
|
+
export const storeAtom = (
|
|
95
|
+
atom: Atom<any>,
|
|
96
|
+
store: Store = IMPLICIT.STORE
|
|
97
|
+
): void => {
|
|
98
|
+
const core = target(store)
|
|
99
|
+
core.atoms = HAMT.set(atom.key, atom, core.atoms)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const storeSelector = (
|
|
103
|
+
selector: Selector<any>,
|
|
104
|
+
store: Store = IMPLICIT.STORE
|
|
105
|
+
): void => {
|
|
106
|
+
const core = target(store)
|
|
107
|
+
core.selectors = HAMT.set(selector.key, selector, core.selectors)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const storeReadonlySelector = (
|
|
111
|
+
selector: ReadonlySelector<any>,
|
|
112
|
+
store: Store = IMPLICIT.STORE
|
|
113
|
+
): void => {
|
|
114
|
+
const core = target(store)
|
|
115
|
+
core.readonlySelectors = HAMT.set(
|
|
116
|
+
selector.key,
|
|
117
|
+
selector,
|
|
118
|
+
core.readonlySelectors
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const hasKeyBeenUsed = (
|
|
123
|
+
key: string,
|
|
124
|
+
store: Store = IMPLICIT.STORE
|
|
125
|
+
): boolean => {
|
|
126
|
+
const core = target(store)
|
|
127
|
+
return (
|
|
128
|
+
HAMT.has(key, core.atoms) ||
|
|
129
|
+
HAMT.has(key, core.selectors) ||
|
|
130
|
+
HAMT.has(key, core.readonlySelectors)
|
|
131
|
+
)
|
|
49
132
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import HAMT from "hamt_plus"
|
|
2
|
+
import * as Rx from "rxjs"
|
|
3
|
+
|
|
4
|
+
import { become } from "~/packages/anvl/src/function"
|
|
5
|
+
|
|
1
6
|
import type { Store } from "."
|
|
2
7
|
import {
|
|
8
|
+
target,
|
|
9
|
+
cacheValue,
|
|
10
|
+
markDone,
|
|
3
11
|
lookup,
|
|
4
12
|
IMPLICIT,
|
|
5
13
|
getState__INTERNAL,
|
|
@@ -8,12 +16,31 @@ import {
|
|
|
8
16
|
} from "."
|
|
9
17
|
import type {
|
|
10
18
|
AtomToken,
|
|
19
|
+
FamilyMetadata,
|
|
20
|
+
ReadonlySelectorOptions,
|
|
11
21
|
ReadonlyValueToken,
|
|
22
|
+
SelectorOptions,
|
|
12
23
|
SelectorToken,
|
|
13
24
|
StateToken,
|
|
14
25
|
} from ".."
|
|
15
26
|
import type { Transactors } from "../transaction"
|
|
16
27
|
|
|
28
|
+
export type Selector<T> = {
|
|
29
|
+
key: string
|
|
30
|
+
type: `selector`
|
|
31
|
+
family?: FamilyMetadata
|
|
32
|
+
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
33
|
+
get: () => T
|
|
34
|
+
set: (newValue: T | ((oldValue: T) => T)) => void
|
|
35
|
+
}
|
|
36
|
+
export type ReadonlySelector<T> = {
|
|
37
|
+
key: string
|
|
38
|
+
type: `readonly_selector`
|
|
39
|
+
family?: FamilyMetadata
|
|
40
|
+
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
41
|
+
get: () => T
|
|
42
|
+
}
|
|
43
|
+
|
|
17
44
|
export const lookupSelectorSources = (
|
|
18
45
|
key: string,
|
|
19
46
|
store: Store
|
|
@@ -22,8 +49,8 @@ export const lookupSelectorSources = (
|
|
|
22
49
|
| ReadonlyValueToken<unknown>
|
|
23
50
|
| SelectorToken<unknown>
|
|
24
51
|
)[] =>
|
|
25
|
-
store
|
|
26
|
-
.getRelations(key)
|
|
52
|
+
target(store)
|
|
53
|
+
.selectorGraph.getRelations(key)
|
|
27
54
|
.filter(({ source }) => source !== key)
|
|
28
55
|
.map(({ source }) => lookup(source, store))
|
|
29
56
|
|
|
@@ -73,17 +100,21 @@ export const updateSelectorAtoms = (
|
|
|
73
100
|
dependency: ReadonlyValueToken<unknown> | StateToken<unknown>,
|
|
74
101
|
store: Store
|
|
75
102
|
): void => {
|
|
103
|
+
const core = target(store)
|
|
76
104
|
if (dependency.type === `atom`) {
|
|
77
|
-
|
|
105
|
+
core.selectorAtoms = core.selectorAtoms.set(selectorKey, dependency.key)
|
|
78
106
|
store.config.logger?.info(
|
|
79
107
|
` || adding root for "${selectorKey}": ${dependency.key}`
|
|
80
108
|
)
|
|
81
109
|
return
|
|
82
110
|
}
|
|
83
111
|
const roots = traceSelectorAtoms(selectorKey, dependency, store)
|
|
84
|
-
store.config.logger?.info(
|
|
112
|
+
store.config.logger?.info(
|
|
113
|
+
` || adding roots for "${selectorKey}":`,
|
|
114
|
+
roots.map((r) => r.key)
|
|
115
|
+
)
|
|
85
116
|
for (const root of roots) {
|
|
86
|
-
|
|
117
|
+
core.selectorAtoms = core.selectorAtoms.set(selectorKey, root.key)
|
|
87
118
|
}
|
|
88
119
|
}
|
|
89
120
|
|
|
@@ -92,7 +123,8 @@ export const registerSelector = (
|
|
|
92
123
|
store: Store = IMPLICIT.STORE
|
|
93
124
|
): Transactors => ({
|
|
94
125
|
get: (dependency) => {
|
|
95
|
-
const
|
|
126
|
+
const core = target(store)
|
|
127
|
+
const alreadyRegistered = core.selectorGraph
|
|
96
128
|
.getRelations(selectorKey)
|
|
97
129
|
.some(({ source }) => source === dependency.key)
|
|
98
130
|
|
|
@@ -109,13 +141,9 @@ export const registerSelector = (
|
|
|
109
141
|
`🔌 registerSelector "${selectorKey}" <- "${dependency.key}" =`,
|
|
110
142
|
dependencyValue
|
|
111
143
|
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
source: dependency.key,
|
|
117
|
-
}
|
|
118
|
-
)
|
|
144
|
+
core.selectorGraph = core.selectorGraph.set(selectorKey, dependency.key, {
|
|
145
|
+
source: dependency.key,
|
|
146
|
+
})
|
|
119
147
|
}
|
|
120
148
|
updateSelectorAtoms(selectorKey, dependency, store)
|
|
121
149
|
return dependencyValue
|
|
@@ -125,3 +153,75 @@ export const registerSelector = (
|
|
|
125
153
|
setState__INTERNAL(state, newValue, store)
|
|
126
154
|
},
|
|
127
155
|
})
|
|
156
|
+
|
|
157
|
+
export function selector__INTERNAL<T>(
|
|
158
|
+
options: SelectorOptions<T>,
|
|
159
|
+
family?: FamilyMetadata,
|
|
160
|
+
store?: Store
|
|
161
|
+
): SelectorToken<T>
|
|
162
|
+
export function selector__INTERNAL<T>(
|
|
163
|
+
options: ReadonlySelectorOptions<T>,
|
|
164
|
+
family?: FamilyMetadata,
|
|
165
|
+
store?: Store
|
|
166
|
+
): ReadonlyValueToken<T>
|
|
167
|
+
export function selector__INTERNAL<T>(
|
|
168
|
+
options: ReadonlySelectorOptions<T> | SelectorOptions<T>,
|
|
169
|
+
family?: FamilyMetadata,
|
|
170
|
+
store: Store = IMPLICIT.STORE
|
|
171
|
+
): ReadonlyValueToken<T> | SelectorToken<T> {
|
|
172
|
+
const core = target(store)
|
|
173
|
+
if (HAMT.has(options.key, core.selectors)) {
|
|
174
|
+
store.config.logger?.error(
|
|
175
|
+
`Key "${options.key}" already exists in the store.`
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
|
|
180
|
+
|
|
181
|
+
const { get, set } = registerSelector(options.key, store)
|
|
182
|
+
const getSelf = () => {
|
|
183
|
+
const value = options.get({ get })
|
|
184
|
+
cacheValue(options.key, value, store)
|
|
185
|
+
return value
|
|
186
|
+
}
|
|
187
|
+
if (!(`set` in options)) {
|
|
188
|
+
const readonlySelector: ReadonlySelector<T> = {
|
|
189
|
+
...options,
|
|
190
|
+
subject,
|
|
191
|
+
get: getSelf,
|
|
192
|
+
type: `readonly_selector`,
|
|
193
|
+
...(family && { family }),
|
|
194
|
+
}
|
|
195
|
+
core.readonlySelectors = HAMT.set(
|
|
196
|
+
options.key,
|
|
197
|
+
readonlySelector,
|
|
198
|
+
core.readonlySelectors
|
|
199
|
+
)
|
|
200
|
+
const initialValue = getSelf()
|
|
201
|
+
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
202
|
+
return { ...readonlySelector, type: `readonly_selector` }
|
|
203
|
+
}
|
|
204
|
+
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
205
|
+
store.config.logger?.info(` <- "${options.key}" became`, next)
|
|
206
|
+
const oldValue = getSelf()
|
|
207
|
+
const newValue = become(next)(oldValue)
|
|
208
|
+
cacheValue(options.key, newValue, store)
|
|
209
|
+
markDone(options.key, store)
|
|
210
|
+
if (store.transactionStatus.phase === `idle`) {
|
|
211
|
+
subject.next({ newValue, oldValue })
|
|
212
|
+
}
|
|
213
|
+
options.set({ get, set }, newValue)
|
|
214
|
+
}
|
|
215
|
+
const mySelector: Selector<T> = {
|
|
216
|
+
...options,
|
|
217
|
+
subject,
|
|
218
|
+
get: getSelf,
|
|
219
|
+
set: setSelf,
|
|
220
|
+
type: `selector`,
|
|
221
|
+
...(family && { family }),
|
|
222
|
+
}
|
|
223
|
+
core.selectors = HAMT.set(options.key, mySelector, core.selectors)
|
|
224
|
+
const initialValue = getSelf()
|
|
225
|
+
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
226
|
+
return { ...mySelector, type: `selector` }
|
|
227
|
+
}
|
package/src/internal/set.ts
CHANGED
|
@@ -2,25 +2,34 @@ import HAMT from "hamt_plus"
|
|
|
2
2
|
|
|
3
3
|
import { become } from "~/packages/anvl/src/function"
|
|
4
4
|
|
|
5
|
-
import type { Atom, Selector } from "."
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import type { Atom, Selector, Store } from "."
|
|
6
|
+
import {
|
|
7
|
+
IMPLICIT,
|
|
8
|
+
cacheValue,
|
|
9
|
+
emitUpdate,
|
|
10
|
+
evictCachedValue,
|
|
11
|
+
getState__INTERNAL,
|
|
12
|
+
isAtomDefault,
|
|
13
|
+
isDone,
|
|
14
|
+
markAtomAsNotDefault,
|
|
15
|
+
markDone,
|
|
16
|
+
stowUpdate,
|
|
17
|
+
target,
|
|
18
|
+
} from "."
|
|
11
19
|
|
|
12
20
|
export const evictDownStream = <T>(
|
|
13
21
|
state: Atom<T>,
|
|
14
22
|
store: Store = IMPLICIT.STORE
|
|
15
23
|
): void => {
|
|
16
|
-
const
|
|
24
|
+
const core = target(store)
|
|
25
|
+
const downstream = core.selectorAtoms.getRelations(state.key)
|
|
17
26
|
const downstreamKeys = downstream.map(({ id }) => id)
|
|
18
27
|
store.config.logger?.info(
|
|
19
28
|
` || ${downstreamKeys.length} downstream:`,
|
|
20
29
|
downstreamKeys
|
|
21
30
|
)
|
|
22
|
-
if (
|
|
23
|
-
store.config.logger?.info(` ||`, [...
|
|
31
|
+
if (core.operation.open) {
|
|
32
|
+
store.config.logger?.info(` ||`, [...core.operation.done], `already done`)
|
|
24
33
|
}
|
|
25
34
|
downstream.forEach(({ id: stateKey }) => {
|
|
26
35
|
if (isDone(stateKey, store)) {
|
|
@@ -28,15 +37,15 @@ export const evictDownStream = <T>(
|
|
|
28
37
|
return
|
|
29
38
|
}
|
|
30
39
|
const state =
|
|
31
|
-
HAMT.get(stateKey,
|
|
32
|
-
HAMT.get(stateKey,
|
|
40
|
+
HAMT.get(stateKey, core.selectors) ??
|
|
41
|
+
HAMT.get(stateKey, core.readonlySelectors)
|
|
33
42
|
if (!state) {
|
|
34
43
|
store.config.logger?.info(
|
|
35
44
|
` || ${stateKey} is an atom, and can't be downstream`
|
|
36
45
|
)
|
|
37
46
|
return
|
|
38
47
|
}
|
|
39
|
-
|
|
48
|
+
evictCachedValue(stateKey, store)
|
|
40
49
|
store.config.logger?.info(` xx evicted "${stateKey}"`)
|
|
41
50
|
|
|
42
51
|
markDone(stateKey, store)
|
|
@@ -50,17 +59,22 @@ export const setAtomState = <T>(
|
|
|
50
59
|
): void => {
|
|
51
60
|
const oldValue = getState__INTERNAL(atom, store)
|
|
52
61
|
const newValue = become(next)(oldValue)
|
|
53
|
-
store.config.logger?.info(
|
|
54
|
-
|
|
62
|
+
store.config.logger?.info(`<< setting atom "${atom.key}" to`, newValue)
|
|
63
|
+
cacheValue(atom.key, newValue, store)
|
|
55
64
|
if (isAtomDefault(atom.key)) {
|
|
56
|
-
|
|
65
|
+
markAtomAsNotDefault(atom.key, store)
|
|
57
66
|
}
|
|
58
67
|
markDone(atom.key, store)
|
|
59
68
|
store.config.logger?.info(
|
|
60
69
|
` || evicting caches downstream from "${atom.key}"`
|
|
61
70
|
)
|
|
62
71
|
evictDownStream(atom, store)
|
|
63
|
-
|
|
72
|
+
const update = { oldValue, newValue }
|
|
73
|
+
if (store.transactionStatus.phase !== `building`) {
|
|
74
|
+
emitUpdate(atom, update, store)
|
|
75
|
+
} else {
|
|
76
|
+
stowUpdate(atom, update, store)
|
|
77
|
+
}
|
|
64
78
|
}
|
|
65
79
|
export const setSelectorState = <T>(
|
|
66
80
|
selector: Selector<T>,
|
|
@@ -70,7 +84,7 @@ export const setSelectorState = <T>(
|
|
|
70
84
|
const oldValue = getState__INTERNAL(selector, store)
|
|
71
85
|
const newValue = become(next)(oldValue)
|
|
72
86
|
|
|
73
|
-
store.config.logger?.info(
|
|
87
|
+
store.config.logger?.info(`<< setting selector "${selector.key}" to`, newValue)
|
|
74
88
|
store.config.logger?.info(` || propagating change made to "${selector.key}"`)
|
|
75
89
|
|
|
76
90
|
selector.set(newValue)
|
package/src/internal/store.ts
CHANGED
|
@@ -1,66 +1,85 @@
|
|
|
1
1
|
import type { Hamt } from "hamt_plus"
|
|
2
2
|
import HAMT from "hamt_plus"
|
|
3
3
|
|
|
4
|
+
import { doNothing } from "~/packages/anvl/src/function"
|
|
4
5
|
import { Join } from "~/packages/anvl/src/join"
|
|
5
6
|
|
|
6
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
Atom,
|
|
9
|
+
OperationProgress,
|
|
10
|
+
ReadonlySelector,
|
|
11
|
+
Selector,
|
|
12
|
+
TransactionStatus,
|
|
13
|
+
Logger,
|
|
14
|
+
Timeline,
|
|
15
|
+
TimelineData,
|
|
16
|
+
} from "."
|
|
17
|
+
import type { Transaction, ƒn } from ".."
|
|
18
|
+
|
|
19
|
+
export type StoreCore = Pick<
|
|
20
|
+
Store,
|
|
21
|
+
| `atoms`
|
|
22
|
+
| `atomsThatAreDefault`
|
|
23
|
+
| `operation`
|
|
24
|
+
| `readonlySelectors`
|
|
25
|
+
| `selectorAtoms`
|
|
26
|
+
| `selectorGraph`
|
|
27
|
+
| `selectors`
|
|
28
|
+
| `timelineAtoms`
|
|
29
|
+
| `timelines`
|
|
30
|
+
| `transactions`
|
|
31
|
+
| `valueMap`
|
|
32
|
+
>
|
|
7
33
|
|
|
8
34
|
export interface Store {
|
|
9
|
-
valueMap: Hamt<any, string>
|
|
10
|
-
selectorGraph: Join<{ source: string }>
|
|
11
|
-
selectorAtoms: Join
|
|
12
35
|
atoms: Hamt<Atom<any>, string>
|
|
13
|
-
|
|
14
|
-
selectors: Hamt<Selector<any>, string>
|
|
36
|
+
atomsThatAreDefault: Set<string>
|
|
15
37
|
readonlySelectors: Hamt<ReadonlySelector<any>, string>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
open: false
|
|
28
|
-
}
|
|
29
|
-
| {
|
|
30
|
-
open: true
|
|
31
|
-
prev: Pick<
|
|
32
|
-
Store,
|
|
33
|
-
| `atoms`
|
|
34
|
-
| `readonlySelectors`
|
|
35
|
-
| `selectorGraph`
|
|
36
|
-
| `selectors`
|
|
37
|
-
| `valueMap`
|
|
38
|
-
>
|
|
39
|
-
}
|
|
38
|
+
selectorAtoms: Join
|
|
39
|
+
selectorGraph: Join<{ source: string }>
|
|
40
|
+
selectors: Hamt<Selector<any>, string>
|
|
41
|
+
timelines: Hamt<Timeline, string>
|
|
42
|
+
timelineAtoms: Join
|
|
43
|
+
timelineStore: Hamt<TimelineData, string>
|
|
44
|
+
transactions: Hamt<Transaction<any>, string>
|
|
45
|
+
valueMap: Hamt<any, string>
|
|
46
|
+
|
|
47
|
+
operation: OperationProgress
|
|
48
|
+
transactionStatus: TransactionStatus<ƒn>
|
|
40
49
|
config: {
|
|
41
50
|
name: string
|
|
42
|
-
logger:
|
|
51
|
+
logger: Logger | null
|
|
52
|
+
logger__INTERNAL: Logger
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
export const createStore = (name: string): Store =>
|
|
47
57
|
({
|
|
48
|
-
valueMap: HAMT.make<any, string>(),
|
|
49
|
-
selectorGraph: new Join({ relationType: `n:n` }),
|
|
50
|
-
selectorAtoms: new Join({ relationType: `n:n` }),
|
|
51
58
|
atoms: HAMT.make<Atom<any>, string>(),
|
|
52
|
-
|
|
53
|
-
selectors: HAMT.make<Selector<any>, string>(),
|
|
59
|
+
atomsThatAreDefault: new Set(),
|
|
54
60
|
readonlySelectors: HAMT.make<ReadonlySelector<any>, string>(),
|
|
61
|
+
selectorAtoms: new Join({ relationType: `n:n` }),
|
|
62
|
+
selectorGraph: new Join({ relationType: `n:n` }),
|
|
63
|
+
selectors: HAMT.make<Selector<any>, string>(),
|
|
64
|
+
timelines: HAMT.make<Timeline, string>(),
|
|
65
|
+
timelineAtoms: new Join({ relationType: `1:n` }),
|
|
66
|
+
timelineStore: HAMT.make<TimelineData, string>(),
|
|
67
|
+
transactions: HAMT.make<Transaction<any>, string>(),
|
|
68
|
+
valueMap: HAMT.make<any, string>(),
|
|
69
|
+
|
|
55
70
|
operation: {
|
|
56
71
|
open: false,
|
|
57
72
|
},
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
transactionStatus: {
|
|
74
|
+
phase: `idle`,
|
|
60
75
|
},
|
|
61
76
|
config: {
|
|
62
77
|
name,
|
|
63
|
-
logger:
|
|
78
|
+
logger: {
|
|
79
|
+
...console,
|
|
80
|
+
info: doNothing,
|
|
81
|
+
},
|
|
82
|
+
logger__INTERNAL: console,
|
|
64
83
|
},
|
|
65
84
|
} satisfies Store)
|
|
66
85
|
|
|
@@ -70,12 +89,6 @@ export const IMPLICIT = {
|
|
|
70
89
|
return this.STORE_INTERNAL ?? (this.STORE_INTERNAL = createStore(`DEFAULT`))
|
|
71
90
|
},
|
|
72
91
|
}
|
|
73
|
-
export const configure = (
|
|
74
|
-
config: Partial<Store[`config`]>,
|
|
75
|
-
store: Store = IMPLICIT.STORE
|
|
76
|
-
): void => {
|
|
77
|
-
Object.assign(store.config, config)
|
|
78
|
-
}
|
|
79
92
|
|
|
80
93
|
export const clearStore = (store: Store = IMPLICIT.STORE): void => {
|
|
81
94
|
const { config } = store
|