atom.io 0.4.1 → 0.6.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 +38 -10
- package/dist/index.d.mts +614 -0
- package/dist/index.d.ts +130 -77
- package/dist/index.js +584 -347
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +582 -347
- package/dist/index.mjs.map +1 -1
- package/json/dist/index.d.mts +18 -0
- package/json/dist/index.d.ts +18 -0
- package/json/dist/index.js +51 -0
- package/json/dist/index.js.map +1 -0
- package/json/dist/index.mjs +15 -0
- package/json/dist/index.mjs.map +1 -0
- package/json/package.json +15 -0
- package/package.json +43 -9
- package/react/dist/index.d.mts +24 -0
- package/react/dist/index.d.ts +18 -11
- package/react/dist/index.js +45 -21
- package/react/dist/index.js.map +1 -1
- package/react/dist/index.mjs +31 -21
- package/react/dist/index.mjs.map +1 -1
- package/react-devtools/dist/index.d.mts +15 -0
- package/react-devtools/dist/index.d.ts +4 -4
- package/react-devtools/dist/index.js +1 -1
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs +1 -1
- package/react-devtools/dist/index.mjs.map +1 -1
- package/realtime/dist/index.d.mts +27 -0
- package/realtime/dist/index.d.ts +27 -0
- package/realtime/dist/index.js +191 -0
- package/realtime/dist/index.js.map +1 -0
- package/realtime/dist/index.mjs +152 -0
- package/realtime/dist/index.mjs.map +1 -0
- package/realtime/package.json +15 -0
- package/realtime-react/dist/index.d.mts +45 -0
- package/realtime-react/dist/index.d.ts +45 -0
- package/realtime-react/dist/index.js +213 -0
- package/realtime-react/dist/index.js.map +1 -0
- package/realtime-react/dist/index.mjs +168 -0
- package/realtime-react/dist/index.mjs.map +1 -0
- package/realtime-react/package.json +15 -0
- package/src/index.ts +21 -5
- package/src/internal/atom-internal.ts +1 -1
- package/src/internal/families-internal.ts +3 -3
- package/src/internal/get.ts +39 -15
- package/src/internal/index.ts +2 -0
- package/src/internal/meta/meta-state.ts +1 -1
- package/src/internal/operation.ts +3 -1
- package/src/internal/selector/create-read-write-selector.ts +62 -0
- package/src/internal/selector/create-readonly-selector.ts +52 -0
- package/src/internal/selector/index.ts +4 -0
- package/src/internal/selector/lookup-selector-sources.ts +16 -0
- package/src/internal/selector/register-selector.ts +57 -0
- package/src/internal/selector/trace-selector-atoms.ts +43 -0
- package/src/internal/selector/update-selector-atoms.ts +33 -0
- package/src/internal/selector-internal.ts +9 -197
- package/src/internal/set.ts +1 -1
- package/src/internal/store.ts +44 -17
- package/src/internal/subscribe-internal.ts +6 -1
- package/src/internal/time-travel-internal.ts +7 -7
- package/src/internal/timeline/add-atom-to-timeline.ts +164 -0
- package/src/internal/timeline/index.ts +1 -0
- package/src/internal/timeline-internal.ts +39 -146
- package/src/internal/transaction/abort-transaction.ts +12 -0
- package/src/internal/transaction/apply-transaction.ts +54 -0
- package/src/internal/transaction/build-transaction.ts +33 -0
- package/src/internal/transaction/index.ts +25 -0
- package/src/internal/transaction/redo-transaction.ts +23 -0
- package/src/internal/transaction/undo-transaction.ts +23 -0
- package/src/internal/transaction-internal.ts +16 -133
- package/src/json/index.ts +1 -0
- package/src/json/select-json.ts +18 -0
- package/src/react/index.ts +2 -46
- package/src/react/store-context.tsx +14 -0
- package/src/react/store-hooks.ts +48 -0
- package/src/react-devtools/AtomIODevtools.tsx +1 -1
- package/src/react-explorer/AtomIOExplorer.tsx +2 -2
- package/src/react-explorer/explorer-states.ts +5 -5
- package/src/react-explorer/index.ts +1 -1
- package/src/react-explorer/space-states.ts +8 -9
- package/src/realtime/README.md +33 -0
- package/src/realtime/hook-composition/expose-family.ts +101 -0
- package/src/realtime/hook-composition/expose-single.ts +38 -0
- package/src/realtime/hook-composition/index.ts +12 -0
- package/src/realtime/hook-composition/receive-state.ts +29 -0
- package/src/realtime/hook-composition/receive-transaction.ts +18 -0
- package/src/realtime/index.ts +1 -0
- package/src/realtime-react/index.ts +3 -0
- package/src/realtime-react/realtime-context.tsx +31 -0
- package/src/realtime-react/realtime-hooks.ts +39 -0
- package/src/realtime-react/realtime-state.ts +10 -0
- package/src/realtime-react/use-pull-family-member.ts +27 -0
- package/src/realtime-react/use-pull-family.ts +25 -0
- package/src/realtime-react/use-pull.ts +23 -0
- package/src/realtime-react/use-push.ts +26 -0
- package/src/realtime-react/use-server-action.ts +34 -0
- package/src/selector.ts +9 -6
- package/src/silo.ts +53 -0
- package/src/subscribe.ts +42 -2
- package/src/timeline.ts +10 -0
- package/src/transaction.ts +24 -12
package/src/internal/index.ts
CHANGED
|
@@ -5,9 +5,11 @@ export * from "./is-default"
|
|
|
5
5
|
export * as META from "./meta"
|
|
6
6
|
export * from "./operation"
|
|
7
7
|
export * from "./selector-internal"
|
|
8
|
+
export * from "./selector"
|
|
8
9
|
export * from "./set"
|
|
9
10
|
export * from "./store"
|
|
10
11
|
export * from "./subscribe-internal"
|
|
11
12
|
export * from "./time-travel-internal"
|
|
12
13
|
export * from "./timeline-internal"
|
|
13
14
|
export * from "./transaction-internal"
|
|
15
|
+
export * from "./transaction"
|
|
@@ -34,7 +34,9 @@ export const openOperation = (token: StateToken<any>, store: Store): void => {
|
|
|
34
34
|
time: Date.now(),
|
|
35
35
|
token,
|
|
36
36
|
}
|
|
37
|
-
store.config.logger?.info(
|
|
37
|
+
store.config.logger?.info(
|
|
38
|
+
`⭕ operation start from "${token.key}" in store "${store.config.name}"`
|
|
39
|
+
)
|
|
38
40
|
}
|
|
39
41
|
export const closeOperation = (store: Store): void => {
|
|
40
42
|
const core = target(store)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import HAMT from "hamt_plus"
|
|
2
|
+
import * as Rx from "rxjs"
|
|
3
|
+
|
|
4
|
+
import { become } from "~/packages/anvl/src/function"
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type Store,
|
|
8
|
+
type Selector,
|
|
9
|
+
type StoreCore,
|
|
10
|
+
registerSelector,
|
|
11
|
+
selector__INTERNAL,
|
|
12
|
+
} from ".."
|
|
13
|
+
import type { FamilyMetadata, SelectorToken } from "../.."
|
|
14
|
+
import type { SelectorOptions } from "../../selector"
|
|
15
|
+
import { cacheValue, markDone } from "../operation"
|
|
16
|
+
|
|
17
|
+
export const createReadWriteSelector = <T>(
|
|
18
|
+
options: SelectorOptions<T>,
|
|
19
|
+
family: FamilyMetadata | undefined,
|
|
20
|
+
store: Store,
|
|
21
|
+
core: StoreCore
|
|
22
|
+
): SelectorToken<T> => {
|
|
23
|
+
const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
|
|
24
|
+
|
|
25
|
+
const { get, set } = registerSelector(options.key, store)
|
|
26
|
+
const getSelf = () => {
|
|
27
|
+
const value = options.get({ get })
|
|
28
|
+
cacheValue(options.key, value, store)
|
|
29
|
+
return value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
33
|
+
store.config.logger?.info(` <- "${options.key}" became`, next)
|
|
34
|
+
const oldValue = getSelf()
|
|
35
|
+
const newValue = become(next)(oldValue)
|
|
36
|
+
cacheValue(options.key, newValue, store)
|
|
37
|
+
markDone(options.key, store)
|
|
38
|
+
if (store.transactionStatus.phase === `idle`) {
|
|
39
|
+
subject.next({ newValue, oldValue })
|
|
40
|
+
}
|
|
41
|
+
options.set({ get, set }, newValue)
|
|
42
|
+
}
|
|
43
|
+
const mySelector: Selector<T> = {
|
|
44
|
+
...options,
|
|
45
|
+
subject,
|
|
46
|
+
install: (s: Store) => selector__INTERNAL(options, family, s),
|
|
47
|
+
get: getSelf,
|
|
48
|
+
set: setSelf,
|
|
49
|
+
type: `selector`,
|
|
50
|
+
...(family && { family }),
|
|
51
|
+
}
|
|
52
|
+
core.selectors = HAMT.set(options.key, mySelector, core.selectors)
|
|
53
|
+
const initialValue = getSelf()
|
|
54
|
+
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
55
|
+
const token: SelectorToken<T> = {
|
|
56
|
+
key: options.key,
|
|
57
|
+
type: `selector`,
|
|
58
|
+
family,
|
|
59
|
+
}
|
|
60
|
+
store.subject.selectorCreation.next(token)
|
|
61
|
+
return token
|
|
62
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import HAMT from "hamt_plus"
|
|
2
|
+
import * as Rx from "rxjs"
|
|
3
|
+
|
|
4
|
+
import { registerSelector } from "./register-selector"
|
|
5
|
+
import type {
|
|
6
|
+
FamilyMetadata,
|
|
7
|
+
ReadonlySelectorOptions,
|
|
8
|
+
ReadonlySelectorToken,
|
|
9
|
+
Store,
|
|
10
|
+
} from "../.."
|
|
11
|
+
import { cacheValue } from "../operation"
|
|
12
|
+
import { selector__INTERNAL, type ReadonlySelector } from "../selector-internal"
|
|
13
|
+
import type { StoreCore } from "../store"
|
|
14
|
+
|
|
15
|
+
export const createReadonlySelector = <T>(
|
|
16
|
+
options: ReadonlySelectorOptions<T>,
|
|
17
|
+
family: FamilyMetadata | undefined,
|
|
18
|
+
store: Store,
|
|
19
|
+
core: StoreCore
|
|
20
|
+
): ReadonlySelectorToken<T> => {
|
|
21
|
+
const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
|
|
22
|
+
|
|
23
|
+
const { get } = registerSelector(options.key, store)
|
|
24
|
+
const getSelf = () => {
|
|
25
|
+
const value = options.get({ get })
|
|
26
|
+
cacheValue(options.key, value, store)
|
|
27
|
+
return value
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const readonlySelector: ReadonlySelector<T> = {
|
|
31
|
+
...options,
|
|
32
|
+
subject,
|
|
33
|
+
install: (s: Store) => selector__INTERNAL(options, family, s),
|
|
34
|
+
get: getSelf,
|
|
35
|
+
type: `readonly_selector`,
|
|
36
|
+
...(family && { family }),
|
|
37
|
+
}
|
|
38
|
+
core.readonlySelectors = HAMT.set(
|
|
39
|
+
options.key,
|
|
40
|
+
readonlySelector,
|
|
41
|
+
core.readonlySelectors
|
|
42
|
+
)
|
|
43
|
+
const initialValue = getSelf()
|
|
44
|
+
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
45
|
+
const token: ReadonlySelectorToken<T> = {
|
|
46
|
+
key: options.key,
|
|
47
|
+
type: `readonly_selector`,
|
|
48
|
+
family,
|
|
49
|
+
}
|
|
50
|
+
store.subject.selectorCreation.next(token)
|
|
51
|
+
return token
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Store } from ".."
|
|
2
|
+
import { target, lookup } from ".."
|
|
3
|
+
import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "../.."
|
|
4
|
+
|
|
5
|
+
export const lookupSelectorSources = (
|
|
6
|
+
key: string,
|
|
7
|
+
store: Store
|
|
8
|
+
): (
|
|
9
|
+
| AtomToken<unknown>
|
|
10
|
+
| ReadonlySelectorToken<unknown>
|
|
11
|
+
| SelectorToken<unknown>
|
|
12
|
+
)[] =>
|
|
13
|
+
target(store)
|
|
14
|
+
.selectorGraph.getRelations(key)
|
|
15
|
+
.filter(({ source }) => source !== key)
|
|
16
|
+
.map(({ source }) => lookup(source, store))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { updateSelectorAtoms } from "./update-selector-atoms"
|
|
2
|
+
import type { Transactors } from "../../transaction"
|
|
3
|
+
import { getState__INTERNAL, withdraw } from "../get"
|
|
4
|
+
import { setState__INTERNAL } from "../set"
|
|
5
|
+
import type { Store } from "../store"
|
|
6
|
+
import { IMPLICIT } from "../store"
|
|
7
|
+
import { target } from "../transaction-internal"
|
|
8
|
+
|
|
9
|
+
export const registerSelector = (
|
|
10
|
+
selectorKey: string,
|
|
11
|
+
store: Store = IMPLICIT.STORE
|
|
12
|
+
): Transactors => ({
|
|
13
|
+
get: (dependency) => {
|
|
14
|
+
const core = target(store)
|
|
15
|
+
const alreadyRegistered = core.selectorGraph
|
|
16
|
+
.getRelations(selectorKey)
|
|
17
|
+
.some(({ source }) => source === dependency.key)
|
|
18
|
+
|
|
19
|
+
const dependencyState = withdraw(dependency, store)
|
|
20
|
+
if (dependencyState === null) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
const dependencyValue = getState__INTERNAL(dependencyState, store)
|
|
26
|
+
|
|
27
|
+
if (alreadyRegistered) {
|
|
28
|
+
store.config.logger?.info(
|
|
29
|
+
` || ${selectorKey} <- ${dependency.key} =`,
|
|
30
|
+
dependencyValue
|
|
31
|
+
)
|
|
32
|
+
} else {
|
|
33
|
+
store.config.logger?.info(
|
|
34
|
+
`🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
|
|
35
|
+
dependencyValue,
|
|
36
|
+
`)`
|
|
37
|
+
)
|
|
38
|
+
core.selectorGraph = core.selectorGraph.set(
|
|
39
|
+
{ from: dependency.key, to: selectorKey },
|
|
40
|
+
{
|
|
41
|
+
source: dependency.key,
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
updateSelectorAtoms(selectorKey, dependency, store)
|
|
46
|
+
return dependencyValue
|
|
47
|
+
},
|
|
48
|
+
set: (stateToken, newValue) => {
|
|
49
|
+
const state = withdraw(stateToken, store)
|
|
50
|
+
if (state === null) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
setState__INTERNAL(state, newValue, store)
|
|
56
|
+
},
|
|
57
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { lookupSelectorSources } from "./lookup-selector-sources"
|
|
2
|
+
import type { Store, AtomToken, ReadonlySelectorToken, StateToken } from "../.."
|
|
3
|
+
|
|
4
|
+
export const traceSelectorAtoms = (
|
|
5
|
+
selectorKey: string,
|
|
6
|
+
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
7
|
+
store: Store
|
|
8
|
+
): AtomToken<unknown>[] => {
|
|
9
|
+
const roots: AtomToken<unknown>[] = []
|
|
10
|
+
|
|
11
|
+
const sources = lookupSelectorSources(dependency.key, store)
|
|
12
|
+
let depth = 0
|
|
13
|
+
while (sources.length > 0) {
|
|
14
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
15
|
+
const source = sources.shift()!
|
|
16
|
+
++depth
|
|
17
|
+
if (depth > 999) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Maximum selector dependency depth exceeded in selector "${selectorKey}".`
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (source.type !== `atom`) {
|
|
24
|
+
sources.push(...lookupSelectorSources(source.key, store))
|
|
25
|
+
} else {
|
|
26
|
+
roots.push(source)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return roots
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const traceAllSelectorAtoms = (
|
|
34
|
+
selectorKey: string,
|
|
35
|
+
store: Store
|
|
36
|
+
): AtomToken<unknown>[] => {
|
|
37
|
+
const sources = lookupSelectorSources(selectorKey, store)
|
|
38
|
+
return sources.flatMap((source) =>
|
|
39
|
+
source.type === `atom`
|
|
40
|
+
? source
|
|
41
|
+
: traceSelectorAtoms(selectorKey, source, store)
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { traceSelectorAtoms } from "./trace-selector-atoms"
|
|
2
|
+
import type { Store } from ".."
|
|
3
|
+
import { target } from ".."
|
|
4
|
+
import type { ReadonlySelectorToken, StateToken } from "../.."
|
|
5
|
+
|
|
6
|
+
export const updateSelectorAtoms = (
|
|
7
|
+
selectorKey: string,
|
|
8
|
+
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
9
|
+
store: Store
|
|
10
|
+
): void => {
|
|
11
|
+
const core = target(store)
|
|
12
|
+
if (dependency.type === `atom`) {
|
|
13
|
+
core.selectorAtoms = core.selectorAtoms.set({
|
|
14
|
+
selectorKey,
|
|
15
|
+
atomKey: dependency.key,
|
|
16
|
+
})
|
|
17
|
+
store.config.logger?.info(
|
|
18
|
+
` || adding root for "${selectorKey}": ${dependency.key}`
|
|
19
|
+
)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
const roots = traceSelectorAtoms(selectorKey, dependency, store)
|
|
23
|
+
store.config.logger?.info(
|
|
24
|
+
` || adding roots for "${selectorKey}":`,
|
|
25
|
+
roots.map((r) => r.key)
|
|
26
|
+
)
|
|
27
|
+
for (const root of roots) {
|
|
28
|
+
core.selectorAtoms = core.selectorAtoms.set({
|
|
29
|
+
selectorKey,
|
|
30
|
+
atomKey: root.key,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,34 +1,23 @@
|
|
|
1
1
|
import HAMT from "hamt_plus"
|
|
2
|
-
import * as Rx from "rxjs"
|
|
3
|
-
|
|
4
|
-
import { become } from "~/packages/anvl/src/function"
|
|
2
|
+
import type * as Rx from "rxjs"
|
|
5
3
|
|
|
6
4
|
import type { Store } from "."
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
markDone,
|
|
11
|
-
lookup,
|
|
12
|
-
IMPLICIT,
|
|
13
|
-
getState__INTERNAL,
|
|
14
|
-
setState__INTERNAL,
|
|
15
|
-
withdraw,
|
|
16
|
-
} from "."
|
|
5
|
+
import { target, IMPLICIT } from "."
|
|
6
|
+
import { createReadWriteSelector } from "./selector/create-read-write-selector"
|
|
7
|
+
import { createReadonlySelector } from "./selector/create-readonly-selector"
|
|
17
8
|
import type {
|
|
18
|
-
AtomToken,
|
|
19
9
|
FamilyMetadata,
|
|
20
10
|
ReadonlySelectorOptions,
|
|
21
11
|
ReadonlySelectorToken,
|
|
22
12
|
SelectorOptions,
|
|
23
13
|
SelectorToken,
|
|
24
|
-
StateToken,
|
|
25
14
|
} from ".."
|
|
26
|
-
import type { Transactors } from "../transaction"
|
|
27
15
|
|
|
28
16
|
export type Selector<T> = {
|
|
29
17
|
key: string
|
|
30
18
|
type: `selector`
|
|
31
19
|
family?: FamilyMetadata
|
|
20
|
+
install: (store: Store) => void
|
|
32
21
|
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
33
22
|
get: () => T
|
|
34
23
|
set: (newValue: T | ((oldValue: T) => T)) => void
|
|
@@ -37,133 +26,11 @@ export type ReadonlySelector<T> = {
|
|
|
37
26
|
key: string
|
|
38
27
|
type: `readonly_selector`
|
|
39
28
|
family?: FamilyMetadata
|
|
29
|
+
install: (store: Store) => void
|
|
40
30
|
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
41
31
|
get: () => T
|
|
42
32
|
}
|
|
43
33
|
|
|
44
|
-
export const lookupSelectorSources = (
|
|
45
|
-
key: string,
|
|
46
|
-
store: Store
|
|
47
|
-
): (
|
|
48
|
-
| AtomToken<unknown>
|
|
49
|
-
| ReadonlySelectorToken<unknown>
|
|
50
|
-
| SelectorToken<unknown>
|
|
51
|
-
)[] =>
|
|
52
|
-
target(store)
|
|
53
|
-
.selectorGraph.getRelations(key)
|
|
54
|
-
.filter(({ source }) => source !== key)
|
|
55
|
-
.map(({ source }) => lookup(source, store))
|
|
56
|
-
|
|
57
|
-
export const traceSelectorAtoms = (
|
|
58
|
-
selectorKey: string,
|
|
59
|
-
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
60
|
-
store: Store
|
|
61
|
-
): AtomToken<unknown>[] => {
|
|
62
|
-
const roots: AtomToken<unknown>[] = []
|
|
63
|
-
|
|
64
|
-
const sources = lookupSelectorSources(dependency.key, store)
|
|
65
|
-
let depth = 0
|
|
66
|
-
while (sources.length > 0) {
|
|
67
|
-
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
68
|
-
const source = sources.shift()!
|
|
69
|
-
++depth
|
|
70
|
-
if (depth > 999) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Maximum selector dependency depth exceeded in selector "${selectorKey}".`
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (source.type !== `atom`) {
|
|
77
|
-
sources.push(...lookupSelectorSources(source.key, store))
|
|
78
|
-
} else {
|
|
79
|
-
roots.push(source)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return roots
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const traceAllSelectorAtoms = (
|
|
87
|
-
selectorKey: string,
|
|
88
|
-
store: Store
|
|
89
|
-
): AtomToken<unknown>[] => {
|
|
90
|
-
const sources = lookupSelectorSources(selectorKey, store)
|
|
91
|
-
return sources.flatMap((source) =>
|
|
92
|
-
source.type === `atom`
|
|
93
|
-
? source
|
|
94
|
-
: traceSelectorAtoms(selectorKey, source, store)
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const updateSelectorAtoms = (
|
|
99
|
-
selectorKey: string,
|
|
100
|
-
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
101
|
-
store: Store
|
|
102
|
-
): void => {
|
|
103
|
-
const core = target(store)
|
|
104
|
-
if (dependency.type === `atom`) {
|
|
105
|
-
core.selectorAtoms = core.selectorAtoms.set({
|
|
106
|
-
selectorKey,
|
|
107
|
-
atomKey: dependency.key,
|
|
108
|
-
})
|
|
109
|
-
store.config.logger?.info(
|
|
110
|
-
` || adding root for "${selectorKey}": ${dependency.key}`
|
|
111
|
-
)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
const roots = traceSelectorAtoms(selectorKey, dependency, store)
|
|
115
|
-
store.config.logger?.info(
|
|
116
|
-
` || adding roots for "${selectorKey}":`,
|
|
117
|
-
roots.map((r) => r.key)
|
|
118
|
-
)
|
|
119
|
-
for (const root of roots) {
|
|
120
|
-
core.selectorAtoms = core.selectorAtoms.set({
|
|
121
|
-
selectorKey,
|
|
122
|
-
atomKey: root.key,
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export const registerSelector = (
|
|
128
|
-
selectorKey: string,
|
|
129
|
-
store: Store = IMPLICIT.STORE
|
|
130
|
-
): Transactors => ({
|
|
131
|
-
get: (dependency) => {
|
|
132
|
-
const core = target(store)
|
|
133
|
-
const alreadyRegistered = core.selectorGraph
|
|
134
|
-
.getRelations(selectorKey)
|
|
135
|
-
.some(({ source }) => source === dependency.key)
|
|
136
|
-
|
|
137
|
-
const dependencyState = withdraw(dependency, store)
|
|
138
|
-
const dependencyValue = getState__INTERNAL(dependencyState, store)
|
|
139
|
-
|
|
140
|
-
if (alreadyRegistered) {
|
|
141
|
-
store.config.logger?.info(
|
|
142
|
-
` || ${selectorKey} <- ${dependency.key} =`,
|
|
143
|
-
dependencyValue
|
|
144
|
-
)
|
|
145
|
-
} else {
|
|
146
|
-
store.config.logger?.info(
|
|
147
|
-
`🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
|
|
148
|
-
dependencyValue,
|
|
149
|
-
`)`
|
|
150
|
-
)
|
|
151
|
-
core.selectorGraph = core.selectorGraph.set(
|
|
152
|
-
{ from: dependency.key, to: selectorKey },
|
|
153
|
-
{
|
|
154
|
-
source: dependency.key,
|
|
155
|
-
}
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
updateSelectorAtoms(selectorKey, dependency, store)
|
|
159
|
-
return dependencyValue
|
|
160
|
-
},
|
|
161
|
-
set: (stateToken, newValue) => {
|
|
162
|
-
const state = withdraw(stateToken, store)
|
|
163
|
-
setState__INTERNAL(state, newValue, store)
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
|
|
167
34
|
export function selector__INTERNAL<T>(
|
|
168
35
|
options: SelectorOptions<T>,
|
|
169
36
|
family?: FamilyMetadata,
|
|
@@ -180,70 +47,15 @@ export function selector__INTERNAL<T>(
|
|
|
180
47
|
store: Store = IMPLICIT.STORE
|
|
181
48
|
): ReadonlySelectorToken<T> | SelectorToken<T> {
|
|
182
49
|
const core = target(store)
|
|
50
|
+
|
|
183
51
|
if (HAMT.has(options.key, core.selectors)) {
|
|
184
52
|
store.config.logger?.error(
|
|
185
53
|
`Key "${options.key}" already exists in the store.`
|
|
186
54
|
)
|
|
187
55
|
}
|
|
188
56
|
|
|
189
|
-
const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
|
|
190
|
-
|
|
191
|
-
const { get, set } = registerSelector(options.key, store)
|
|
192
|
-
const getSelf = () => {
|
|
193
|
-
const value = options.get({ get })
|
|
194
|
-
cacheValue(options.key, value, store)
|
|
195
|
-
return value
|
|
196
|
-
}
|
|
197
57
|
if (!(`set` in options)) {
|
|
198
|
-
|
|
199
|
-
...options,
|
|
200
|
-
subject,
|
|
201
|
-
get: getSelf,
|
|
202
|
-
type: `readonly_selector`,
|
|
203
|
-
...(family && { family }),
|
|
204
|
-
}
|
|
205
|
-
core.readonlySelectors = HAMT.set(
|
|
206
|
-
options.key,
|
|
207
|
-
readonlySelector,
|
|
208
|
-
core.readonlySelectors
|
|
209
|
-
)
|
|
210
|
-
const initialValue = getSelf()
|
|
211
|
-
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
212
|
-
const token: ReadonlySelectorToken<T> = {
|
|
213
|
-
key: options.key,
|
|
214
|
-
type: `readonly_selector`,
|
|
215
|
-
family,
|
|
216
|
-
}
|
|
217
|
-
store.subject.selectorCreation.next(token)
|
|
218
|
-
return token
|
|
219
|
-
}
|
|
220
|
-
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
221
|
-
store.config.logger?.info(` <- "${options.key}" became`, next)
|
|
222
|
-
const oldValue = getSelf()
|
|
223
|
-
const newValue = become(next)(oldValue)
|
|
224
|
-
cacheValue(options.key, newValue, store)
|
|
225
|
-
markDone(options.key, store)
|
|
226
|
-
if (store.transactionStatus.phase === `idle`) {
|
|
227
|
-
subject.next({ newValue, oldValue })
|
|
228
|
-
}
|
|
229
|
-
options.set({ get, set }, newValue)
|
|
230
|
-
}
|
|
231
|
-
const mySelector: Selector<T> = {
|
|
232
|
-
...options,
|
|
233
|
-
subject,
|
|
234
|
-
get: getSelf,
|
|
235
|
-
set: setSelf,
|
|
236
|
-
type: `selector`,
|
|
237
|
-
...(family && { family }),
|
|
238
|
-
}
|
|
239
|
-
core.selectors = HAMT.set(options.key, mySelector, core.selectors)
|
|
240
|
-
const initialValue = getSelf()
|
|
241
|
-
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
242
|
-
const token: SelectorToken<T> = {
|
|
243
|
-
key: options.key,
|
|
244
|
-
type: `selector`,
|
|
245
|
-
family,
|
|
58
|
+
return createReadonlySelector(options, family, store, core)
|
|
246
59
|
}
|
|
247
|
-
store
|
|
248
|
-
return token
|
|
60
|
+
return createReadWriteSelector(options, family, store, core)
|
|
249
61
|
}
|
package/src/internal/set.ts
CHANGED
|
@@ -61,7 +61,7 @@ export const setAtomState = <T>(
|
|
|
61
61
|
const newValue = become(next)(oldValue)
|
|
62
62
|
store.config.logger?.info(`<< setting atom "${atom.key}" to`, newValue)
|
|
63
63
|
cacheValue(atom.key, newValue, store)
|
|
64
|
-
if (isAtomDefault(atom.key)) {
|
|
64
|
+
if (isAtomDefault(atom.key, store)) {
|
|
65
65
|
markAtomAsNotDefault(atom.key, store)
|
|
66
66
|
}
|
|
67
67
|
markDone(atom.key, store)
|
package/src/internal/store.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Hamt } from "hamt_plus"
|
|
|
2
2
|
import HAMT from "hamt_plus"
|
|
3
3
|
import * as Rx from "rxjs"
|
|
4
4
|
|
|
5
|
+
import type { ƒn } from "~/packages/anvl/src/function"
|
|
5
6
|
import { doNothing } from "~/packages/anvl/src/function"
|
|
6
7
|
import { Join } from "~/packages/anvl/src/join"
|
|
7
8
|
|
|
@@ -12,7 +13,7 @@ import type {
|
|
|
12
13
|
Selector,
|
|
13
14
|
TransactionStatus,
|
|
14
15
|
Timeline,
|
|
15
|
-
|
|
16
|
+
Transaction,
|
|
16
17
|
} from "."
|
|
17
18
|
import type {
|
|
18
19
|
AtomToken,
|
|
@@ -20,9 +21,7 @@ import type {
|
|
|
20
21
|
ReadonlySelectorToken,
|
|
21
22
|
SelectorToken,
|
|
22
23
|
TimelineToken,
|
|
23
|
-
Transaction,
|
|
24
24
|
TransactionToken,
|
|
25
|
-
ƒn,
|
|
26
25
|
} from ".."
|
|
27
26
|
|
|
28
27
|
export type StoreCore = Pick<
|
|
@@ -47,9 +46,8 @@ export interface Store {
|
|
|
47
46
|
selectorAtoms: Join<null, `selectorKey`, `atomKey`>
|
|
48
47
|
selectorGraph: Join<{ source: string }>
|
|
49
48
|
selectors: Hamt<Selector<any>, string>
|
|
50
|
-
timelines: Hamt<Timeline, string>
|
|
51
49
|
timelineAtoms: Join<null, `timelineKey`, `atomKey`>
|
|
52
|
-
|
|
50
|
+
timelines: Hamt<Timeline, string>
|
|
53
51
|
transactions: Hamt<Transaction<any>, string>
|
|
54
52
|
valueMap: Hamt<any, string>
|
|
55
53
|
|
|
@@ -71,46 +69,75 @@ export interface Store {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
export const createStore = (name: string): Store =>
|
|
75
|
-
|
|
72
|
+
export const createStore = (name: string, store: Store | null = null): Store => {
|
|
73
|
+
const copiedStore = {
|
|
74
|
+
...(store ??
|
|
75
|
+
(() => ({
|
|
76
|
+
atomsThatAreDefault: new Set(),
|
|
77
|
+
selectorAtoms: new Join({ relationType: `n:n` })
|
|
78
|
+
.from(`selectorKey`)
|
|
79
|
+
.to(`atomKey`),
|
|
80
|
+
selectorGraph: new Join({ relationType: `n:n` }),
|
|
81
|
+
valueMap: HAMT.make<any, string>(),
|
|
82
|
+
}))()),
|
|
83
|
+
|
|
76
84
|
atoms: HAMT.make<Atom<any>, string>(),
|
|
77
|
-
atomsThatAreDefault: new Set(),
|
|
78
85
|
readonlySelectors: HAMT.make<ReadonlySelector<any>, string>(),
|
|
79
|
-
selectorAtoms: new Join({ relationType: `n:n` })
|
|
80
|
-
.from(`selectorKey`)
|
|
81
|
-
.to(`atomKey`),
|
|
82
|
-
selectorGraph: new Join({ relationType: `n:n` }),
|
|
83
86
|
selectors: HAMT.make<Selector<any>, string>(),
|
|
87
|
+
transactions: HAMT.make<Transaction<any>, string>(),
|
|
84
88
|
timelines: HAMT.make<Timeline, string>(),
|
|
89
|
+
|
|
85
90
|
timelineAtoms: new Join({ relationType: `1:n` })
|
|
86
91
|
.from(`timelineKey`)
|
|
87
92
|
.to(`atomKey`),
|
|
88
|
-
timelineStore: HAMT.make<TimelineData, string>(),
|
|
89
|
-
transactions: HAMT.make<Transaction<any>, string>(),
|
|
90
|
-
valueMap: HAMT.make<any, string>(),
|
|
91
93
|
|
|
92
94
|
subject: {
|
|
93
95
|
atomCreation: new Rx.Subject(),
|
|
94
96
|
selectorCreation: new Rx.Subject(),
|
|
95
97
|
transactionCreation: new Rx.Subject(),
|
|
96
98
|
timelineCreation: new Rx.Subject(),
|
|
99
|
+
...store?.subject,
|
|
97
100
|
},
|
|
98
101
|
|
|
99
102
|
operation: {
|
|
100
103
|
open: false,
|
|
104
|
+
...store?.operation,
|
|
101
105
|
},
|
|
102
106
|
transactionStatus: {
|
|
103
107
|
phase: `idle`,
|
|
108
|
+
...store?.transactionStatus,
|
|
104
109
|
},
|
|
105
110
|
config: {
|
|
106
|
-
name,
|
|
107
111
|
logger: {
|
|
108
112
|
...console,
|
|
109
113
|
info: doNothing,
|
|
114
|
+
...store?.config?.logger,
|
|
110
115
|
},
|
|
111
116
|
logger__INTERNAL: console,
|
|
117
|
+
...store?.config,
|
|
118
|
+
name,
|
|
112
119
|
},
|
|
113
|
-
} satisfies Store
|
|
120
|
+
} satisfies Store
|
|
121
|
+
|
|
122
|
+
store?.atoms.forEach((atom) => {
|
|
123
|
+
const copiedAtom = { ...atom, subject: new Rx.Subject() } satisfies Atom<any>
|
|
124
|
+
copiedStore.atoms = HAMT.set(atom.key, copiedAtom, copiedStore.atoms)
|
|
125
|
+
})
|
|
126
|
+
store?.readonlySelectors.forEach((selector) => {
|
|
127
|
+
selector.install(copiedStore)
|
|
128
|
+
})
|
|
129
|
+
store?.selectors.forEach((selector) => {
|
|
130
|
+
selector.install(copiedStore)
|
|
131
|
+
})
|
|
132
|
+
store?.transactions.forEach((tx) => {
|
|
133
|
+
tx.install(copiedStore)
|
|
134
|
+
})
|
|
135
|
+
store?.timelines.forEach((timeline) => {
|
|
136
|
+
timeline.install(copiedStore)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
return copiedStore
|
|
140
|
+
}
|
|
114
141
|
|
|
115
142
|
export const IMPLICIT = {
|
|
116
143
|
STORE_INTERNAL: undefined as Store | undefined,
|