atom.io 0.6.9 → 0.8.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 +21 -2
- package/dist/index.d.mts +34 -421
- package/dist/index.d.ts +34 -421
- package/dist/index.js +248 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +209 -4
- package/dist/index.mjs.map +1 -1
- package/internal/dist/index.d.mts +364 -0
- package/internal/dist/index.d.ts +364 -0
- package/internal/dist/index.js +1906 -0
- package/internal/dist/index.js.map +1 -0
- package/internal/dist/index.mjs +1830 -0
- package/internal/dist/index.mjs.map +1 -0
- package/internal/package.json +15 -0
- package/internal/src/atom/create-atom.ts +75 -0
- package/internal/src/atom/delete-atom.ts +10 -0
- package/internal/src/atom/index.ts +3 -0
- package/internal/src/atom/is-default.ts +37 -0
- package/internal/src/caching.ts +38 -0
- package/internal/src/families/create-atom-family.ts +59 -0
- package/internal/src/families/create-readonly-selector-family.ts +45 -0
- package/internal/src/families/create-selector-family.ts +67 -0
- package/internal/src/families/index.ts +3 -0
- package/internal/src/future.ts +39 -0
- package/internal/src/get-state-internal.ts +23 -0
- package/internal/src/index.ts +14 -0
- package/internal/src/mutable/create-mutable-atom-family.ts +25 -0
- package/internal/src/mutable/create-mutable-atom.ts +49 -0
- package/internal/src/mutable/get-json-token.ts +22 -0
- package/internal/src/mutable/get-update-token.ts +20 -0
- package/internal/src/mutable/index.ts +17 -0
- package/internal/src/mutable/is-atom-token-mutable.ts +7 -0
- package/internal/src/mutable/tracker-family.ts +61 -0
- package/internal/src/mutable/tracker.ts +164 -0
- package/internal/src/mutable/transceiver.ts +110 -0
- package/internal/src/operation.ts +68 -0
- package/internal/src/selector/create-read-write-selector.ts +65 -0
- package/internal/src/selector/create-readonly-selector.ts +49 -0
- package/internal/src/selector/create-selector.ts +65 -0
- package/internal/src/selector/index.ts +5 -0
- package/internal/src/selector/lookup-selector-sources.ts +20 -0
- package/internal/src/selector/register-selector.ts +61 -0
- package/internal/src/selector/trace-selector-atoms.ts +45 -0
- package/internal/src/selector/update-selector-atoms.ts +34 -0
- package/internal/src/set-state/become.ts +10 -0
- package/internal/src/set-state/copy-mutable-if-needed.ts +23 -0
- package/internal/src/set-state/copy-mutable-in-transaction.ts +59 -0
- package/internal/src/set-state/copy-mutable-into-new-store.ts +34 -0
- package/internal/src/set-state/emit-update.ts +23 -0
- package/internal/src/set-state/evict-downstream.ts +39 -0
- package/internal/src/set-state/index.ts +2 -0
- package/internal/src/set-state/set-atom-state.ts +38 -0
- package/internal/src/set-state/set-selector-state.ts +19 -0
- package/internal/src/set-state/set-state-internal.ts +18 -0
- package/internal/src/set-state/stow-update.ts +42 -0
- package/internal/src/store/deposit.ts +43 -0
- package/internal/src/store/index.ts +5 -0
- package/internal/src/store/lookup.ts +26 -0
- package/internal/src/store/store.ts +154 -0
- package/internal/src/store/withdraw-new-family-member.ts +53 -0
- package/internal/src/store/withdraw.ts +113 -0
- package/internal/src/subject.ts +21 -0
- package/internal/src/subscribe/index.ts +1 -0
- package/internal/src/subscribe/recall-state.ts +19 -0
- package/internal/src/subscribe/subscribe-to-root-atoms.ts +47 -0
- package/internal/src/timeline/add-atom-to-timeline.ts +189 -0
- package/internal/src/timeline/index.ts +3 -0
- package/internal/src/timeline/time-travel-internal.ts +91 -0
- package/internal/src/timeline/timeline-internal.ts +115 -0
- package/internal/src/transaction/abort-transaction.ts +12 -0
- package/internal/src/transaction/apply-transaction.ts +64 -0
- package/internal/src/transaction/build-transaction.ts +39 -0
- package/internal/src/transaction/index.ts +26 -0
- package/internal/src/transaction/redo-transaction.ts +22 -0
- package/internal/src/transaction/transaction-internal.ts +64 -0
- package/internal/src/transaction/undo-transaction.ts +22 -0
- package/introspection/dist/index.d.mts +3 -197
- package/introspection/dist/index.d.ts +3 -197
- package/introspection/dist/index.js +329 -4
- package/introspection/dist/index.js.map +1 -1
- package/introspection/dist/index.mjs +310 -4
- package/introspection/dist/index.mjs.map +1 -1
- package/introspection/src/attach-atom-index.ts +84 -0
- package/introspection/src/attach-introspection-states.ts +38 -0
- package/introspection/src/attach-selector-index.ts +90 -0
- package/introspection/src/attach-timeline-family.ts +59 -0
- package/introspection/src/attach-timeline-index.ts +38 -0
- package/introspection/src/attach-transaction-index.ts +40 -0
- package/introspection/src/attach-transaction-logs.ts +43 -0
- package/introspection/src/index.ts +20 -0
- package/json/dist/index.d.mts +10 -2
- package/json/dist/index.d.ts +10 -2
- package/json/dist/index.js +83 -26
- package/json/dist/index.js.map +1 -1
- package/json/dist/index.mjs +74 -3
- package/json/dist/index.mjs.map +1 -1
- package/json/src/index.ts +5 -0
- package/json/src/select-json-family.ts +35 -0
- package/json/src/select-json.ts +22 -0
- package/package.json +103 -63
- package/react/dist/index.d.mts +9 -17
- package/react/dist/index.d.ts +9 -17
- package/react/dist/index.js +44 -27
- package/react/dist/index.js.map +1 -1
- package/react/dist/index.mjs +24 -4
- package/react/dist/index.mjs.map +1 -1
- package/react/src/index.ts +2 -0
- package/react/src/store-context.tsx +12 -0
- package/react/src/store-hooks.ts +36 -0
- package/react-devtools/dist/index.css +50 -1
- package/react-devtools/dist/index.css.map +1 -1
- package/react-devtools/dist/index.d.mts +104 -71
- package/react-devtools/dist/index.d.ts +104 -71
- package/react-devtools/dist/index.js +2821 -45
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs +2790 -11
- package/react-devtools/dist/index.mjs.map +1 -1
- package/react-devtools/src/AtomIODevtools.tsx +109 -0
- package/react-devtools/src/Button.tsx +23 -0
- package/react-devtools/src/StateEditor.tsx +75 -0
- package/react-devtools/src/StateIndex.tsx +159 -0
- package/react-devtools/src/TimelineIndex.tsx +88 -0
- package/react-devtools/src/TransactionIndex.tsx +70 -0
- package/react-devtools/src/Updates.tsx +150 -0
- package/react-devtools/src/devtools.scss +310 -0
- package/react-devtools/src/index.ts +72 -0
- package/realtime-react/dist/index.d.mts +8 -22
- package/realtime-react/dist/index.d.ts +8 -22
- package/realtime-react/dist/index.js +87 -32
- package/realtime-react/dist/index.js.map +1 -1
- package/realtime-react/dist/index.mjs +62 -6
- package/realtime-react/dist/index.mjs.map +1 -1
- package/realtime-react/src/index.ts +7 -0
- package/realtime-react/src/realtime-context.tsx +29 -0
- package/realtime-react/src/use-pull-family-member.ts +15 -0
- package/realtime-react/src/use-pull-mutable-family-member.ts +20 -0
- package/realtime-react/src/use-pull-mutable.ts +17 -0
- package/realtime-react/src/use-pull.ts +15 -0
- package/realtime-react/src/use-push.ts +19 -0
- package/realtime-react/src/use-server-action.ts +18 -0
- package/realtime-testing/dist/index.d.mts +49 -0
- package/realtime-testing/dist/index.d.ts +49 -0
- package/realtime-testing/dist/index.js +147 -0
- package/realtime-testing/dist/index.js.map +1 -0
- package/realtime-testing/dist/index.mjs +116 -0
- package/realtime-testing/dist/index.mjs.map +1 -0
- package/realtime-testing/src/index.ts +1 -0
- package/realtime-testing/src/setup-realtime-test.tsx +161 -0
- package/src/atom.ts +64 -9
- package/src/index.ts +29 -31
- package/src/logger.ts +3 -3
- package/src/selector.ts +3 -3
- package/src/silo.ts +29 -20
- package/src/subscribe.ts +3 -3
- package/src/timeline.ts +2 -2
- package/transceivers/set-rtx/dist/index.d.mts +39 -0
- package/transceivers/set-rtx/dist/index.d.ts +39 -0
- package/transceivers/set-rtx/dist/index.js +213 -0
- package/transceivers/set-rtx/dist/index.js.map +1 -0
- package/transceivers/set-rtx/dist/index.mjs +211 -0
- package/transceivers/set-rtx/dist/index.mjs.map +1 -0
- package/{realtime → transceivers/set-rtx}/package.json +1 -1
- package/transceivers/set-rtx/src/index.ts +1 -0
- package/transceivers/set-rtx/src/set-rtx.ts +242 -0
- package/realtime/dist/index.d.mts +0 -23
- package/realtime/dist/index.d.ts +0 -23
- package/realtime/dist/index.js +0 -32
- package/realtime/dist/index.js.map +0 -1
- package/realtime/dist/index.mjs +0 -7
- package/realtime/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { StateToken } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import type { Store } from "./store"
|
|
4
|
+
import { IMPLICIT } from "./store"
|
|
5
|
+
import { target } from "./transaction"
|
|
6
|
+
|
|
7
|
+
export type OperationProgress =
|
|
8
|
+
| {
|
|
9
|
+
open: false
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
open: true
|
|
13
|
+
done: Set<string>
|
|
14
|
+
prev: Map<string, any>
|
|
15
|
+
time: number
|
|
16
|
+
token: StateToken<any>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const openOperation = (token: StateToken<any>, store: Store): void => {
|
|
20
|
+
const core = target(store)
|
|
21
|
+
if (core.operation.open) {
|
|
22
|
+
store.config.logger?.error(
|
|
23
|
+
`❌ failed to setState to "${token.key}" during a setState for "${core.operation.token.key}"`,
|
|
24
|
+
)
|
|
25
|
+
throw Symbol(`violation`)
|
|
26
|
+
}
|
|
27
|
+
core.operation = {
|
|
28
|
+
open: true,
|
|
29
|
+
done: new Set(),
|
|
30
|
+
prev: new Map(store.valueMap),
|
|
31
|
+
time: Date.now(),
|
|
32
|
+
token,
|
|
33
|
+
}
|
|
34
|
+
store.config.logger?.info(
|
|
35
|
+
`⭕ operation start from "${token.key}" in store "${store.config.name}"${
|
|
36
|
+
store.transactionStatus.phase === `idle`
|
|
37
|
+
? ``
|
|
38
|
+
: ` ${store.transactionStatus.phase} "${store.transactionStatus.key}"`
|
|
39
|
+
}`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
export const closeOperation = (store: Store): void => {
|
|
43
|
+
const core = target(store)
|
|
44
|
+
core.operation = { open: false }
|
|
45
|
+
store.config.logger?.info(`🔴 operation done`)
|
|
46
|
+
store.subject.operationStatus.next(core.operation)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const isDone = (key: string, store: Store = IMPLICIT.STORE): boolean => {
|
|
50
|
+
const core = target(store)
|
|
51
|
+
if (!core.operation.open) {
|
|
52
|
+
store.config.logger?.warn(
|
|
53
|
+
`isDone called outside of an operation. This is probably a bug.`,
|
|
54
|
+
)
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
return core.operation.done.has(key)
|
|
58
|
+
}
|
|
59
|
+
export const markDone = (key: string, store: Store = IMPLICIT.STORE): void => {
|
|
60
|
+
const core = target(store)
|
|
61
|
+
if (!core.operation.open) {
|
|
62
|
+
store.config.logger?.warn(
|
|
63
|
+
`markDone called outside of an operation. This is probably a bug.`,
|
|
64
|
+
)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
core.operation.done.add(key)
|
|
68
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { FamilyMetadata, SelectorOptions, SelectorToken } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import { cacheValue } from "../caching"
|
|
4
|
+
import { markDone } from "../operation"
|
|
5
|
+
import { become } from "../set-state/become"
|
|
6
|
+
import type { Store, StoreCore } from "../store"
|
|
7
|
+
import { Subject } from "../subject"
|
|
8
|
+
import type { Selector } from "./create-selector"
|
|
9
|
+
import { createSelector } from "./create-selector"
|
|
10
|
+
import { registerSelector } from "./register-selector"
|
|
11
|
+
|
|
12
|
+
export const createReadWriteSelector = <T>(
|
|
13
|
+
options: SelectorOptions<T>,
|
|
14
|
+
family: FamilyMetadata | undefined,
|
|
15
|
+
store: Store,
|
|
16
|
+
core: StoreCore,
|
|
17
|
+
): SelectorToken<T> => {
|
|
18
|
+
const subject = new Subject<{ newValue: T; oldValue: T }>()
|
|
19
|
+
|
|
20
|
+
const { get, set } = registerSelector(options.key, store)
|
|
21
|
+
const getSelf = () => {
|
|
22
|
+
const value = options.get({ get })
|
|
23
|
+
cacheValue(options.key, value, subject, store)
|
|
24
|
+
return value
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
28
|
+
const oldValue = getSelf()
|
|
29
|
+
store.config.logger?.info(
|
|
30
|
+
` <- "${options.key}" went (`,
|
|
31
|
+
oldValue,
|
|
32
|
+
`->`,
|
|
33
|
+
next,
|
|
34
|
+
`)`,
|
|
35
|
+
)
|
|
36
|
+
const newValue = become(next)(oldValue)
|
|
37
|
+
cacheValue(options.key, newValue, subject, store)
|
|
38
|
+
markDone(options.key, store)
|
|
39
|
+
if (store.transactionStatus.phase === `idle`) {
|
|
40
|
+
subject.next({ newValue, oldValue })
|
|
41
|
+
}
|
|
42
|
+
options.set({ get, set }, newValue)
|
|
43
|
+
}
|
|
44
|
+
const mySelector: Selector<T> = {
|
|
45
|
+
...options,
|
|
46
|
+
subject,
|
|
47
|
+
install: (s: Store) => createSelector(options, family, s),
|
|
48
|
+
get: getSelf,
|
|
49
|
+
set: setSelf,
|
|
50
|
+
type: `selector`,
|
|
51
|
+
...(family && { family }),
|
|
52
|
+
}
|
|
53
|
+
core.selectors.set(options.key, mySelector)
|
|
54
|
+
const initialValue = getSelf()
|
|
55
|
+
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
56
|
+
const token: SelectorToken<T> = {
|
|
57
|
+
key: options.key,
|
|
58
|
+
type: `selector`,
|
|
59
|
+
}
|
|
60
|
+
if (family) {
|
|
61
|
+
token.family = family
|
|
62
|
+
}
|
|
63
|
+
store.subject.selectorCreation.next(token)
|
|
64
|
+
return token
|
|
65
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FamilyMetadata,
|
|
3
|
+
ReadonlySelectorOptions,
|
|
4
|
+
ReadonlySelectorToken,
|
|
5
|
+
} from "atom.io"
|
|
6
|
+
|
|
7
|
+
import { cacheValue } from "../caching"
|
|
8
|
+
import type { Store, StoreCore } from "../store"
|
|
9
|
+
import { Subject } from "../subject"
|
|
10
|
+
import type { ReadonlySelector } from "./create-selector"
|
|
11
|
+
import { createSelector } from "./create-selector"
|
|
12
|
+
import { registerSelector } from "./register-selector"
|
|
13
|
+
|
|
14
|
+
export const createReadonlySelector = <T>(
|
|
15
|
+
options: ReadonlySelectorOptions<T>,
|
|
16
|
+
family: FamilyMetadata | undefined,
|
|
17
|
+
store: Store,
|
|
18
|
+
core: StoreCore,
|
|
19
|
+
): ReadonlySelectorToken<T> => {
|
|
20
|
+
const subject = new Subject<{ newValue: T; oldValue: T }>()
|
|
21
|
+
|
|
22
|
+
const { get } = registerSelector(options.key, store)
|
|
23
|
+
const getSelf = () => {
|
|
24
|
+
const value = options.get({ get })
|
|
25
|
+
cacheValue(options.key, value, subject, store)
|
|
26
|
+
return value
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const readonlySelector: ReadonlySelector<T> = {
|
|
30
|
+
...options,
|
|
31
|
+
subject,
|
|
32
|
+
install: (s: Store) => createSelector(options, family, s),
|
|
33
|
+
get: getSelf,
|
|
34
|
+
type: `readonly_selector`,
|
|
35
|
+
...(family && { family }),
|
|
36
|
+
}
|
|
37
|
+
core.readonlySelectors.set(options.key, readonlySelector)
|
|
38
|
+
const initialValue = getSelf()
|
|
39
|
+
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
40
|
+
const token: ReadonlySelectorToken<T> = {
|
|
41
|
+
key: options.key,
|
|
42
|
+
type: `readonly_selector`,
|
|
43
|
+
}
|
|
44
|
+
if (family) {
|
|
45
|
+
token.family = family
|
|
46
|
+
}
|
|
47
|
+
store.subject.selectorCreation.next(token)
|
|
48
|
+
return token
|
|
49
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FamilyMetadata,
|
|
3
|
+
ReadonlySelectorOptions,
|
|
4
|
+
ReadonlySelectorToken,
|
|
5
|
+
SelectorOptions,
|
|
6
|
+
SelectorToken,
|
|
7
|
+
} from "atom.io"
|
|
8
|
+
|
|
9
|
+
import type { Store } from "../store"
|
|
10
|
+
import { IMPLICIT } from "../store"
|
|
11
|
+
import type { Subject } from "../subject"
|
|
12
|
+
import { target } from "../transaction"
|
|
13
|
+
import { createReadWriteSelector } from "./create-read-write-selector"
|
|
14
|
+
import { createReadonlySelector } from "./create-readonly-selector"
|
|
15
|
+
|
|
16
|
+
export type Selector<T> = {
|
|
17
|
+
key: string
|
|
18
|
+
type: `selector`
|
|
19
|
+
family?: FamilyMetadata
|
|
20
|
+
install: (store: Store) => void
|
|
21
|
+
subject: Subject<{ newValue: T; oldValue: T }>
|
|
22
|
+
get: () => T
|
|
23
|
+
set: (newValue: T | ((oldValue: T) => T)) => void
|
|
24
|
+
}
|
|
25
|
+
export type ReadonlySelector<T> = {
|
|
26
|
+
key: string
|
|
27
|
+
type: `readonly_selector`
|
|
28
|
+
family?: FamilyMetadata
|
|
29
|
+
install: (store: Store) => void
|
|
30
|
+
subject: Subject<{ newValue: T; oldValue: T }>
|
|
31
|
+
get: () => T
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createSelector<T>(
|
|
35
|
+
options: SelectorOptions<T>,
|
|
36
|
+
family?: FamilyMetadata,
|
|
37
|
+
store?: Store,
|
|
38
|
+
): SelectorToken<T>
|
|
39
|
+
export function createSelector<T>(
|
|
40
|
+
options: ReadonlySelectorOptions<T>,
|
|
41
|
+
family?: FamilyMetadata,
|
|
42
|
+
store?: Store,
|
|
43
|
+
): ReadonlySelectorToken<T>
|
|
44
|
+
export function createSelector<T>(
|
|
45
|
+
options: ReadonlySelectorOptions<T> | SelectorOptions<T>,
|
|
46
|
+
family?: FamilyMetadata,
|
|
47
|
+
store: Store = IMPLICIT.STORE,
|
|
48
|
+
): ReadonlySelectorToken<T> | SelectorToken<T> {
|
|
49
|
+
const core = target(store)
|
|
50
|
+
const existingWritable = core.selectors.get(options.key)
|
|
51
|
+
const existingReadonly = core.readonlySelectors.get(options.key)
|
|
52
|
+
|
|
53
|
+
if (existingWritable || existingReadonly) {
|
|
54
|
+
store.config.logger?.error?.(
|
|
55
|
+
`Tried to create ${existingReadonly ? `readonly selector` : `selector`}`,
|
|
56
|
+
`"${options.key}", but it already exists in the store.`,
|
|
57
|
+
`(Ignore if you are using hot module replacement.)`,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (`set` in options) {
|
|
62
|
+
return createReadWriteSelector(options, family, store, core)
|
|
63
|
+
}
|
|
64
|
+
return createReadonlySelector(options, family, store, core)
|
|
65
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { lookup } from "../store"
|
|
5
|
+
import { target } from "../transaction"
|
|
6
|
+
|
|
7
|
+
export const lookupSelectorSources = (
|
|
8
|
+
key: string,
|
|
9
|
+
store: Store,
|
|
10
|
+
): (
|
|
11
|
+
| AtomToken<unknown>
|
|
12
|
+
| ReadonlySelectorToken<unknown>
|
|
13
|
+
| SelectorToken<unknown>
|
|
14
|
+
)[] => {
|
|
15
|
+
const sources = target(store)
|
|
16
|
+
.selectorGraph.getRelationEntries({ downstreamSelectorKey: key })
|
|
17
|
+
.filter(([_, { source }]) => source !== key)
|
|
18
|
+
.map(([_, { source }]) => lookup(source, store))
|
|
19
|
+
return sources
|
|
20
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Transactors } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import { getState__INTERNAL } from "../get-state-internal"
|
|
4
|
+
import { setState__INTERNAL } from "../set-state"
|
|
5
|
+
import type { Store } from "../store"
|
|
6
|
+
import { IMPLICIT, withdraw } from "../store"
|
|
7
|
+
import { target } from "../transaction/transaction-internal"
|
|
8
|
+
import { updateSelectorAtoms } from "./update-selector-atoms"
|
|
9
|
+
|
|
10
|
+
export const registerSelector = (
|
|
11
|
+
selectorKey: string,
|
|
12
|
+
store: Store = IMPLICIT.STORE,
|
|
13
|
+
): Transactors => ({
|
|
14
|
+
get: (dependency) => {
|
|
15
|
+
const core = target(store)
|
|
16
|
+
const alreadyRegistered = core.selectorGraph
|
|
17
|
+
.getRelationEntries({ downstreamSelectorKey: selectorKey })
|
|
18
|
+
.some(([_, { source }]) => source === dependency.key)
|
|
19
|
+
|
|
20
|
+
const dependencyState = withdraw(dependency, store)
|
|
21
|
+
if (dependencyState === null) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`,
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
const dependencyValue = getState__INTERNAL(dependencyState, store)
|
|
27
|
+
|
|
28
|
+
if (alreadyRegistered) {
|
|
29
|
+
store.config.logger?.info(
|
|
30
|
+
` || ${selectorKey} <- ${dependency.key} =`,
|
|
31
|
+
dependencyValue,
|
|
32
|
+
)
|
|
33
|
+
} else {
|
|
34
|
+
store.config.logger?.info(
|
|
35
|
+
`🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
|
|
36
|
+
dependencyValue,
|
|
37
|
+
`)`,
|
|
38
|
+
)
|
|
39
|
+
core.selectorGraph = core.selectorGraph.set(
|
|
40
|
+
{
|
|
41
|
+
upstreamSelectorKey: dependency.key,
|
|
42
|
+
downstreamSelectorKey: selectorKey,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
source: dependency.key,
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
updateSelectorAtoms(selectorKey, dependency, store)
|
|
50
|
+
return dependencyValue
|
|
51
|
+
},
|
|
52
|
+
set: (stateToken, newValue) => {
|
|
53
|
+
const state = withdraw(stateToken, store)
|
|
54
|
+
if (state === null) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
setState__INTERNAL(state, newValue, store)
|
|
60
|
+
},
|
|
61
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AtomToken, ReadonlySelectorToken, StateToken } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import type { Store } from ".."
|
|
4
|
+
import { lookupSelectorSources } from "./lookup-selector-sources"
|
|
5
|
+
|
|
6
|
+
export const traceSelectorAtoms = (
|
|
7
|
+
selectorKey: string,
|
|
8
|
+
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
9
|
+
store: Store,
|
|
10
|
+
): AtomToken<unknown>[] => {
|
|
11
|
+
const roots: AtomToken<unknown>[] = []
|
|
12
|
+
|
|
13
|
+
const sources = lookupSelectorSources(dependency.key, store)
|
|
14
|
+
let depth = 0
|
|
15
|
+
while (sources.length > 0) {
|
|
16
|
+
// biome-ignore lint/style/noNonNullAssertion: just checked length ^^^
|
|
17
|
+
const source = sources.shift()!
|
|
18
|
+
++depth
|
|
19
|
+
if (depth > 999) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Maximum selector dependency depth exceeded in selector "${selectorKey}".`,
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (source.type !== `atom`) {
|
|
26
|
+
sources.push(...lookupSelectorSources(source.key, store))
|
|
27
|
+
} else {
|
|
28
|
+
roots.push(source)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return roots
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const traceAllSelectorAtoms = (
|
|
36
|
+
selectorKey: string,
|
|
37
|
+
store: Store,
|
|
38
|
+
): AtomToken<unknown>[] => {
|
|
39
|
+
const sources = lookupSelectorSources(selectorKey, store)
|
|
40
|
+
return sources.flatMap((source) =>
|
|
41
|
+
source.type === `atom`
|
|
42
|
+
? source
|
|
43
|
+
: traceSelectorAtoms(selectorKey, source, store),
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReadonlySelectorToken, StateToken } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { target } from "../transaction"
|
|
5
|
+
import { traceSelectorAtoms } from "./trace-selector-atoms"
|
|
6
|
+
|
|
7
|
+
export const updateSelectorAtoms = (
|
|
8
|
+
selectorKey: string,
|
|
9
|
+
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
10
|
+
store: Store,
|
|
11
|
+
): void => {
|
|
12
|
+
const core = target(store)
|
|
13
|
+
if (dependency.type === `atom`) {
|
|
14
|
+
core.selectorAtoms = core.selectorAtoms.set({
|
|
15
|
+
selectorKey,
|
|
16
|
+
atomKey: dependency.key,
|
|
17
|
+
})
|
|
18
|
+
store.config.logger?.info(
|
|
19
|
+
` || adding root for "${selectorKey}": ${dependency.key}`,
|
|
20
|
+
)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
const roots = traceSelectorAtoms(selectorKey, dependency, store)
|
|
24
|
+
store.config.logger?.info(
|
|
25
|
+
` || adding roots for "${selectorKey}":`,
|
|
26
|
+
roots.map((r) => r.key),
|
|
27
|
+
)
|
|
28
|
+
for (const root of roots) {
|
|
29
|
+
core.selectorAtoms = core.selectorAtoms.set({
|
|
30
|
+
selectorKey,
|
|
31
|
+
atomKey: root.key,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Modify<T> = (thing: T) => T
|
|
2
|
+
|
|
3
|
+
export const become =
|
|
4
|
+
<T>(nextVersionOfThing: Modify<T> | T) =>
|
|
5
|
+
(originalThing: T): T =>
|
|
6
|
+
nextVersionOfThing instanceof Function
|
|
7
|
+
? nextVersionOfThing(
|
|
8
|
+
originalThing instanceof Function ? originalThing() : originalThing,
|
|
9
|
+
)
|
|
10
|
+
: nextVersionOfThing
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JsonInterface } from "atom.io/json"
|
|
2
|
+
|
|
3
|
+
import type { Atom } from "../atom"
|
|
4
|
+
import { Tracker } from "../mutable"
|
|
5
|
+
import type { Store, StoreCore } from "../store"
|
|
6
|
+
|
|
7
|
+
export function copyMutableIfNeeded<T>(
|
|
8
|
+
atom: Atom<T>,
|
|
9
|
+
transform: JsonInterface<T>,
|
|
10
|
+
origin: Store,
|
|
11
|
+
target: StoreCore,
|
|
12
|
+
): T {
|
|
13
|
+
const originValue = origin.valueMap.get(atom.key)
|
|
14
|
+
const targetValue = target.valueMap.get(atom.key)
|
|
15
|
+
if (originValue === targetValue) {
|
|
16
|
+
origin.config.logger?.info(`📃 copying`, `${atom.key}`)
|
|
17
|
+
const copiedValue = transform.fromJson(transform.toJson(originValue))
|
|
18
|
+
target.valueMap.set(atom.key, copiedValue)
|
|
19
|
+
new Tracker(atom, origin)
|
|
20
|
+
return copiedValue
|
|
21
|
+
}
|
|
22
|
+
return targetValue
|
|
23
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AtomFamily } from "atom.io"
|
|
2
|
+
import type { Json, JsonInterface } from "atom.io/json"
|
|
3
|
+
import { getState__INTERNAL } from ".."
|
|
4
|
+
import type { Store, StoreCore } from ".."
|
|
5
|
+
import type { Atom } from "../atom"
|
|
6
|
+
import { copyMutableIfNeeded } from "./copy-mutable-if-needed"
|
|
7
|
+
|
|
8
|
+
export function copyMutableIfWithinTransaction<T>(
|
|
9
|
+
atom: Atom<T> | (Atom<T> & JsonInterface<T, Json.Serializable>),
|
|
10
|
+
store: Store,
|
|
11
|
+
): T {
|
|
12
|
+
if (
|
|
13
|
+
store.transactionStatus.phase === `building` ||
|
|
14
|
+
store.transactionStatus.phase === `applying`
|
|
15
|
+
) {
|
|
16
|
+
if (`toJson` in atom && `fromJson` in atom) {
|
|
17
|
+
store.config.logger?.info(
|
|
18
|
+
`📄 copyMutableIfWithinTransaction: ${atom.key} is mutable`,
|
|
19
|
+
)
|
|
20
|
+
const copiedValue = copyMutableIfNeeded(
|
|
21
|
+
atom,
|
|
22
|
+
atom,
|
|
23
|
+
store,
|
|
24
|
+
store.transactionStatus.core,
|
|
25
|
+
)
|
|
26
|
+
return copiedValue
|
|
27
|
+
}
|
|
28
|
+
if (`family` in atom) {
|
|
29
|
+
const family = store.transactionStatus.core.families.get(atom.family.key)
|
|
30
|
+
if (family && family.type === `atom_family`) {
|
|
31
|
+
const result = copyMutableFamilyMemberWithinTransaction<T>(
|
|
32
|
+
atom,
|
|
33
|
+
family,
|
|
34
|
+
store,
|
|
35
|
+
store.transactionStatus.core,
|
|
36
|
+
)
|
|
37
|
+
if (result) {
|
|
38
|
+
return result
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return getState__INTERNAL(atom, store)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function copyMutableFamilyMemberWithinTransaction<T>(
|
|
47
|
+
atom: Atom<T>,
|
|
48
|
+
family:
|
|
49
|
+
| AtomFamily<T, any>
|
|
50
|
+
| (AtomFamily<T, any> & JsonInterface<T, Json.Serializable>),
|
|
51
|
+
origin: Store,
|
|
52
|
+
target: StoreCore,
|
|
53
|
+
): T | null {
|
|
54
|
+
if (`toJson` in family && `fromJson` in family) {
|
|
55
|
+
const copyCreated = copyMutableIfNeeded(atom, family, origin, target)
|
|
56
|
+
return copyCreated
|
|
57
|
+
}
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AtomFamily } from "atom.io"
|
|
2
|
+
import type { Json, JsonInterface } from "atom.io/json"
|
|
3
|
+
import type { Store, StoreCore } from ".."
|
|
4
|
+
import type { Atom } from "../atom"
|
|
5
|
+
import { copyMutableIfNeeded } from "./copy-mutable-if-needed"
|
|
6
|
+
|
|
7
|
+
export function copyMutableIntoNewStore<T>(
|
|
8
|
+
atom: Atom<T> | (Atom<T> & JsonInterface<T, Json.Serializable>),
|
|
9
|
+
origin: Store,
|
|
10
|
+
target: Store,
|
|
11
|
+
): void {
|
|
12
|
+
if (`toJson` in atom && `fromJson` in atom) {
|
|
13
|
+
copyMutableIfNeeded(atom, atom, origin, target)
|
|
14
|
+
}
|
|
15
|
+
if (`family` in atom) {
|
|
16
|
+
const family = target.families.get(atom.family.key)
|
|
17
|
+
if (family && family.type === `atom_family`) {
|
|
18
|
+
copyMutableFamilyMemberIntoNewStore(atom, family, origin, target)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function copyMutableFamilyMemberIntoNewStore<T>(
|
|
24
|
+
atom: Atom<T>,
|
|
25
|
+
family:
|
|
26
|
+
| AtomFamily<T, any>
|
|
27
|
+
| (AtomFamily<T, any> & JsonInterface<T, Json.Serializable>),
|
|
28
|
+
origin: Store,
|
|
29
|
+
target: Store,
|
|
30
|
+
): void {
|
|
31
|
+
if (`toJson` in family && `fromJson` in family) {
|
|
32
|
+
copyMutableIfNeeded(atom, family, origin, target)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { StateUpdate } from "atom.io"
|
|
2
|
+
import type { Store } from "atom.io/internal"
|
|
3
|
+
|
|
4
|
+
import type { Atom } from "../atom"
|
|
5
|
+
import type { ReadonlySelector, Selector } from "../selector"
|
|
6
|
+
|
|
7
|
+
export const emitUpdate = <T>(
|
|
8
|
+
state: Atom<T> | ReadonlySelector<T> | Selector<T>,
|
|
9
|
+
update: StateUpdate<T>,
|
|
10
|
+
store: Store,
|
|
11
|
+
): void => {
|
|
12
|
+
const { key } = state
|
|
13
|
+
const { logger } = store.config
|
|
14
|
+
logger?.info(
|
|
15
|
+
`📢 ${state.type} "${key}" went (`,
|
|
16
|
+
update.oldValue,
|
|
17
|
+
`->`,
|
|
18
|
+
update.newValue,
|
|
19
|
+
`)`,
|
|
20
|
+
)
|
|
21
|
+
logger?.info(`📢 notifying subscribers:`, state.subject.subscribers)
|
|
22
|
+
state.subject.next(update)
|
|
23
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Atom } from "../atom"
|
|
2
|
+
import { isDone, markDone } from "../operation"
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { IMPLICIT } from "../store"
|
|
5
|
+
import { target } from "../transaction"
|
|
6
|
+
|
|
7
|
+
export const evictDownStream = <T>(
|
|
8
|
+
state: Atom<T>,
|
|
9
|
+
store: Store = IMPLICIT.STORE,
|
|
10
|
+
): void => {
|
|
11
|
+
const core = target(store)
|
|
12
|
+
const downstreamKeys = core.selectorAtoms.getRelatedKeys(state.key)
|
|
13
|
+
store.config.logger?.info(
|
|
14
|
+
` || ${downstreamKeys?.size ?? `none`} downstream:`,
|
|
15
|
+
downstreamKeys,
|
|
16
|
+
)
|
|
17
|
+
if (core.operation.open) {
|
|
18
|
+
store.config.logger?.info(` ||`, [...core.operation.done], `already done`)
|
|
19
|
+
}
|
|
20
|
+
if (downstreamKeys) {
|
|
21
|
+
for (const key of downstreamKeys) {
|
|
22
|
+
if (isDone(key, store)) {
|
|
23
|
+
store.config.logger?.info(` || ${key} already done`)
|
|
24
|
+
continue
|
|
25
|
+
}
|
|
26
|
+
const state = core.selectors.get(key) ?? core.readonlySelectors.get(key)
|
|
27
|
+
if (!state) {
|
|
28
|
+
store.config.logger?.info(
|
|
29
|
+
` || ${key} was not found in selectors or readonlySelectors`,
|
|
30
|
+
)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
core.valueMap.delete(key)
|
|
34
|
+
store.config.logger?.info(` xx evicted "${key}"`)
|
|
35
|
+
|
|
36
|
+
markDone(key, store)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Atom } from "../atom"
|
|
2
|
+
import { isAtomDefault, markAtomAsNotDefault } from "../atom"
|
|
3
|
+
import { cacheValue } from "../caching"
|
|
4
|
+
import { getState__INTERNAL } from "../get-state-internal"
|
|
5
|
+
import { markDone } from "../operation"
|
|
6
|
+
import type { Store } from "../store"
|
|
7
|
+
import { IMPLICIT } from "../store"
|
|
8
|
+
import { become } from "./become"
|
|
9
|
+
import { copyMutableIfWithinTransaction } from "./copy-mutable-in-transaction"
|
|
10
|
+
import { emitUpdate } from "./emit-update"
|
|
11
|
+
import { evictDownStream } from "./evict-downstream"
|
|
12
|
+
import { stowUpdate } from "./stow-update"
|
|
13
|
+
|
|
14
|
+
export const setAtomState = <T>(
|
|
15
|
+
atom: Atom<T>,
|
|
16
|
+
next: T | ((oldValue: T) => T),
|
|
17
|
+
store: Store = IMPLICIT.STORE,
|
|
18
|
+
): void => {
|
|
19
|
+
const oldValue = getState__INTERNAL(atom, store)
|
|
20
|
+
let newValue = copyMutableIfWithinTransaction(atom, store)
|
|
21
|
+
newValue = become(next)(newValue)
|
|
22
|
+
store.config.logger?.info(`<< setting atom "${atom.key}" to`, newValue)
|
|
23
|
+
cacheValue(atom.key, newValue, atom.subject, store)
|
|
24
|
+
if (isAtomDefault(atom.key, store)) {
|
|
25
|
+
markAtomAsNotDefault(atom.key, store)
|
|
26
|
+
}
|
|
27
|
+
markDone(atom.key, store)
|
|
28
|
+
store.config.logger?.info(
|
|
29
|
+
` || evicting caches downstream from "${atom.key}"`,
|
|
30
|
+
)
|
|
31
|
+
evictDownStream(atom, store)
|
|
32
|
+
const update = { oldValue, newValue }
|
|
33
|
+
if (store.transactionStatus.phase !== `building`) {
|
|
34
|
+
emitUpdate(atom, update, store)
|
|
35
|
+
} else {
|
|
36
|
+
stowUpdate(atom, update, store)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getState__INTERNAL } from "../get-state-internal"
|
|
2
|
+
import type { Selector } from "../selector"
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { IMPLICIT } from "../store"
|
|
5
|
+
import { become } from "./become"
|
|
6
|
+
|
|
7
|
+
export const setSelectorState = <T>(
|
|
8
|
+
selector: Selector<T>,
|
|
9
|
+
next: T | ((oldValue: T) => T),
|
|
10
|
+
store: Store = IMPLICIT.STORE,
|
|
11
|
+
): void => {
|
|
12
|
+
const oldValue = getState__INTERNAL(selector, store)
|
|
13
|
+
const newValue = become(next)(oldValue)
|
|
14
|
+
|
|
15
|
+
store.config.logger?.info(`<< setting selector "${selector.key}" to`, newValue)
|
|
16
|
+
store.config.logger?.info(` || propagating change made to "${selector.key}"`)
|
|
17
|
+
|
|
18
|
+
selector.set(newValue)
|
|
19
|
+
}
|