atom.io 0.6.9 → 0.7.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 +342 -0
- package/internal/dist/index.d.ts +342 -0
- package/internal/dist/index.js +1873 -0
- package/internal/dist/index.js.map +1 -0
- package/internal/dist/index.mjs +1798 -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 +21 -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/get-state-internal.ts +23 -0
- package/internal/src/index.ts +13 -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 +2806 -44
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs +2775 -10
- 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 +36 -32
- 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,43 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AtomToken,
|
|
3
|
+
ReadonlySelectorToken,
|
|
4
|
+
SelectorToken,
|
|
5
|
+
StateToken,
|
|
6
|
+
TransactionToken,
|
|
7
|
+
ƒn,
|
|
8
|
+
} from "atom.io"
|
|
9
|
+
|
|
10
|
+
import type { Atom } from "../atom"
|
|
11
|
+
import type { ReadonlySelector, Selector } from "../selector"
|
|
12
|
+
import type { Transaction } from "../transaction"
|
|
13
|
+
|
|
14
|
+
export function deposit<T>(state: Atom<T>): AtomToken<T>
|
|
15
|
+
export function deposit<T>(state: Selector<T>): SelectorToken<T>
|
|
16
|
+
export function deposit<T>(state: Atom<T> | Selector<T>): StateToken<T>
|
|
17
|
+
export function deposit<T>(state: ReadonlySelector<T>): ReadonlySelectorToken<T>
|
|
18
|
+
export function deposit<T>(
|
|
19
|
+
state: Transaction<T extends ƒn ? T : never>,
|
|
20
|
+
): TransactionToken<T>
|
|
21
|
+
export function deposit<T>(
|
|
22
|
+
state: Atom<T> | ReadonlySelector<T> | Selector<T>,
|
|
23
|
+
): ReadonlySelectorToken<T> | StateToken<T>
|
|
24
|
+
export function deposit<T>(
|
|
25
|
+
state:
|
|
26
|
+
| Atom<T>
|
|
27
|
+
| ReadonlySelector<T>
|
|
28
|
+
| Selector<T>
|
|
29
|
+
| Transaction<T extends ƒn ? T : never>,
|
|
30
|
+
):
|
|
31
|
+
| AtomToken<T>
|
|
32
|
+
| ReadonlySelectorToken<T>
|
|
33
|
+
| SelectorToken<T>
|
|
34
|
+
| TransactionToken<T> {
|
|
35
|
+
const token = {
|
|
36
|
+
key: state.key,
|
|
37
|
+
type: state.type,
|
|
38
|
+
} as any
|
|
39
|
+
if (`family` in state) {
|
|
40
|
+
token.family = state.family
|
|
41
|
+
}
|
|
42
|
+
return token
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import { target } from "../transaction"
|
|
4
|
+
import type { Store } from "./store"
|
|
5
|
+
|
|
6
|
+
export function lookup(
|
|
7
|
+
key: string,
|
|
8
|
+
store: Store,
|
|
9
|
+
): AtomToken<unknown> | ReadonlySelectorToken<unknown> | SelectorToken<unknown> {
|
|
10
|
+
const core = target(store)
|
|
11
|
+
let type: string = core.atoms.has(key)
|
|
12
|
+
? `atom`
|
|
13
|
+
: core.selectors.has(key)
|
|
14
|
+
? `selector`
|
|
15
|
+
: core.readonlySelectors.has(key)
|
|
16
|
+
? `readonly_selector`
|
|
17
|
+
: ``
|
|
18
|
+
if (!type) {
|
|
19
|
+
const errorId = Math.random().toString(36)
|
|
20
|
+
type = `🚨 This state could not be found by lookup! Check the console for "${errorId}"`
|
|
21
|
+
store.config.logger?.error(
|
|
22
|
+
`${errorId}: Key "${key}" does not exist in the store.`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
return { key, type } as any
|
|
26
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AtomFamily,
|
|
3
|
+
AtomToken,
|
|
4
|
+
Logger,
|
|
5
|
+
ReadonlySelectorFamily,
|
|
6
|
+
ReadonlySelectorToken,
|
|
7
|
+
SelectorFamily,
|
|
8
|
+
SelectorToken,
|
|
9
|
+
TimelineToken,
|
|
10
|
+
TransactionToken,
|
|
11
|
+
ƒn,
|
|
12
|
+
} from "atom.io"
|
|
13
|
+
|
|
14
|
+
import { Junction } from "~/packages/rel8/junction/src"
|
|
15
|
+
|
|
16
|
+
import type { Atom } from "../atom"
|
|
17
|
+
import type { MutableAtom, Tracker, Transceiver } from "../mutable"
|
|
18
|
+
import type { OperationProgress } from "../operation"
|
|
19
|
+
import type { ReadonlySelector, Selector } from "../selector"
|
|
20
|
+
import { Subject } from "../subject"
|
|
21
|
+
import type { Timeline } from "../timeline"
|
|
22
|
+
import type { Transaction, TransactionStatus } from "../transaction"
|
|
23
|
+
|
|
24
|
+
export type StoreCore = Pick<
|
|
25
|
+
Store,
|
|
26
|
+
| `atoms`
|
|
27
|
+
| `atomsThatAreDefault`
|
|
28
|
+
| `families`
|
|
29
|
+
| `operation`
|
|
30
|
+
| `readonlySelectors`
|
|
31
|
+
| `selectorAtoms`
|
|
32
|
+
| `selectorGraph`
|
|
33
|
+
| `selectors`
|
|
34
|
+
| `timelineAtoms`
|
|
35
|
+
| `timelines`
|
|
36
|
+
| `trackers`
|
|
37
|
+
| `transactions`
|
|
38
|
+
| `valueMap`
|
|
39
|
+
>
|
|
40
|
+
|
|
41
|
+
export class Store {
|
|
42
|
+
public valueMap = new Map<string, any>()
|
|
43
|
+
|
|
44
|
+
public atoms = new Map<string, Atom<any> | MutableAtom<any>>()
|
|
45
|
+
public selectors = new Map<string, Selector<any>>()
|
|
46
|
+
public readonlySelectors = new Map<string, ReadonlySelector<any>>()
|
|
47
|
+
|
|
48
|
+
public trackers = new Map<string, Tracker<Transceiver<any>>>()
|
|
49
|
+
public families = new Map<
|
|
50
|
+
string,
|
|
51
|
+
| AtomFamily<any, any>
|
|
52
|
+
| ReadonlySelectorFamily<any, any>
|
|
53
|
+
| SelectorFamily<any, any>
|
|
54
|
+
>()
|
|
55
|
+
|
|
56
|
+
public timelines = new Map<string, Timeline>()
|
|
57
|
+
public transactions = new Map<string, Transaction<ƒn>>()
|
|
58
|
+
|
|
59
|
+
public atomsThatAreDefault = new Set<string>()
|
|
60
|
+
|
|
61
|
+
public timelineAtoms = new Junction({
|
|
62
|
+
between: [`timelineKey`, `atomKey`],
|
|
63
|
+
cardinality: `1:n`,
|
|
64
|
+
})
|
|
65
|
+
public selectorAtoms = new Junction({
|
|
66
|
+
between: [`selectorKey`, `atomKey`],
|
|
67
|
+
cardinality: `n:n`,
|
|
68
|
+
})
|
|
69
|
+
public selectorGraph = new Junction<
|
|
70
|
+
`upstreamSelectorKey`,
|
|
71
|
+
`downstreamSelectorKey`,
|
|
72
|
+
{ source: string }
|
|
73
|
+
>(
|
|
74
|
+
{
|
|
75
|
+
between: [`upstreamSelectorKey`, `downstreamSelectorKey`],
|
|
76
|
+
cardinality: `n:n`,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
makeContentKey: (...keys) => keys.sort().join(`:`),
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
public subject = {
|
|
84
|
+
atomCreation: new Subject<AtomToken<unknown>>(),
|
|
85
|
+
selectorCreation: new Subject<
|
|
86
|
+
ReadonlySelectorToken<unknown> | SelectorToken<unknown>
|
|
87
|
+
>(),
|
|
88
|
+
transactionCreation: new Subject<TransactionToken<ƒn>>(),
|
|
89
|
+
timelineCreation: new Subject<TimelineToken>(),
|
|
90
|
+
operationStatus: new Subject<OperationProgress>(),
|
|
91
|
+
}
|
|
92
|
+
public operation: OperationProgress = { open: false }
|
|
93
|
+
public transactionStatus: TransactionStatus<ƒn> = { phase: `idle` }
|
|
94
|
+
|
|
95
|
+
public config: {
|
|
96
|
+
name: string
|
|
97
|
+
logger: Logger | null
|
|
98
|
+
logger__INTERNAL: Logger
|
|
99
|
+
} = {
|
|
100
|
+
name: `IMPLICIT_STORE`,
|
|
101
|
+
logger: { ...console, info: () => undefined },
|
|
102
|
+
logger__INTERNAL: console,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public constructor(name: string, store: Store | null = null) {
|
|
106
|
+
if (store !== null) {
|
|
107
|
+
this.valueMap = new Map(store?.valueMap)
|
|
108
|
+
|
|
109
|
+
this.operation = { ...store?.operation }
|
|
110
|
+
this.transactionStatus = { ...store?.transactionStatus }
|
|
111
|
+
this.config = {
|
|
112
|
+
...store?.config,
|
|
113
|
+
logger__INTERNAL: console,
|
|
114
|
+
logger: {
|
|
115
|
+
...console,
|
|
116
|
+
info: () => undefined,
|
|
117
|
+
...store?.config?.logger,
|
|
118
|
+
},
|
|
119
|
+
name,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
store?.atoms.forEach((atom) => {
|
|
124
|
+
atom.install(this)
|
|
125
|
+
})
|
|
126
|
+
store?.readonlySelectors.forEach((selector) => {
|
|
127
|
+
selector.install(this)
|
|
128
|
+
})
|
|
129
|
+
store?.selectors.forEach((selector) => {
|
|
130
|
+
selector.install(this)
|
|
131
|
+
})
|
|
132
|
+
store?.transactions.forEach((tx) => {
|
|
133
|
+
tx.install(this)
|
|
134
|
+
})
|
|
135
|
+
store?.timelines.forEach((timeline) => {
|
|
136
|
+
timeline.install(this)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const IMPLICIT = {
|
|
142
|
+
STORE_INTERNAL: undefined as Store | undefined,
|
|
143
|
+
get STORE(): Store {
|
|
144
|
+
return (
|
|
145
|
+
this.STORE_INTERNAL ?? (this.STORE_INTERNAL = new Store(`IMPLICIT_STORE`))
|
|
146
|
+
)
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const clearStore = (store: Store = IMPLICIT.STORE): void => {
|
|
151
|
+
const { config } = store
|
|
152
|
+
Object.assign(store, new Store(config.name))
|
|
153
|
+
store.config = config
|
|
154
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AtomToken,
|
|
3
|
+
ReadonlySelectorToken,
|
|
4
|
+
SelectorToken,
|
|
5
|
+
StateToken,
|
|
6
|
+
} from "atom.io"
|
|
7
|
+
import type { Atom, ReadonlySelector, Selector, Store } from ".."
|
|
8
|
+
import { withdraw } from ".."
|
|
9
|
+
import { target } from "../transaction"
|
|
10
|
+
|
|
11
|
+
export function withdrawNewFamilyMember<T>(
|
|
12
|
+
token: AtomToken<T>,
|
|
13
|
+
store: Store,
|
|
14
|
+
): Atom<T> | null
|
|
15
|
+
export function withdrawNewFamilyMember<T>(
|
|
16
|
+
token: SelectorToken<T>,
|
|
17
|
+
store: Store,
|
|
18
|
+
): Selector<T> | null
|
|
19
|
+
export function withdrawNewFamilyMember<T>(
|
|
20
|
+
token: ReadonlySelectorToken<T>,
|
|
21
|
+
store: Store,
|
|
22
|
+
): ReadonlySelector<T> | null
|
|
23
|
+
export function withdrawNewFamilyMember<T>(
|
|
24
|
+
token: StateToken<T>,
|
|
25
|
+
store: Store,
|
|
26
|
+
): Atom<T> | Selector<T> | null
|
|
27
|
+
export function withdrawNewFamilyMember<T>(
|
|
28
|
+
token: ReadonlySelectorToken<T> | StateToken<T>,
|
|
29
|
+
store: Store,
|
|
30
|
+
): Atom<T> | ReadonlySelector<T> | Selector<T> | null
|
|
31
|
+
export function withdrawNewFamilyMember<T>(
|
|
32
|
+
token:
|
|
33
|
+
| AtomToken<T>
|
|
34
|
+
| ReadonlySelectorToken<T>
|
|
35
|
+
| SelectorToken<T>
|
|
36
|
+
| StateToken<T>,
|
|
37
|
+
store: Store,
|
|
38
|
+
): Atom<T> | ReadonlySelector<T> | Selector<T> | null {
|
|
39
|
+
store.config.logger?.info(
|
|
40
|
+
`👪 creating new family member "${token.key}" in store "${store.config.name}"`,
|
|
41
|
+
)
|
|
42
|
+
if (token.family) {
|
|
43
|
+
const core = target(store)
|
|
44
|
+
const family = core.families.get(token.family.key)
|
|
45
|
+
if (family) {
|
|
46
|
+
const jsonSubKey = JSON.parse(token.family.subKey)
|
|
47
|
+
family(jsonSubKey)
|
|
48
|
+
const state = withdraw(token, store)
|
|
49
|
+
return state
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AtomToken,
|
|
3
|
+
ReadonlySelectorToken,
|
|
4
|
+
SelectorToken,
|
|
5
|
+
StateToken,
|
|
6
|
+
TimelineToken,
|
|
7
|
+
TransactionToken,
|
|
8
|
+
ƒn,
|
|
9
|
+
} from "atom.io"
|
|
10
|
+
|
|
11
|
+
import type { Atom } from "../atom"
|
|
12
|
+
import type { ReadonlySelector, Selector } from "../selector"
|
|
13
|
+
import { addAtomToTimeline } from "../timeline"
|
|
14
|
+
import type { Timeline } from "../timeline"
|
|
15
|
+
import type { Transaction } from "../transaction"
|
|
16
|
+
import { target } from "../transaction"
|
|
17
|
+
import type { Store } from "./store"
|
|
18
|
+
|
|
19
|
+
export function withdraw<T>(token: AtomToken<T>, store: Store): Atom<T> | null
|
|
20
|
+
export function withdraw<T>(
|
|
21
|
+
token: SelectorToken<T>,
|
|
22
|
+
store: Store,
|
|
23
|
+
): Selector<T> | null
|
|
24
|
+
export function withdraw<T>(
|
|
25
|
+
token: StateToken<T>,
|
|
26
|
+
store: Store,
|
|
27
|
+
): Atom<T> | Selector<T> | null
|
|
28
|
+
export function withdraw<T>(
|
|
29
|
+
token: ReadonlySelectorToken<T>,
|
|
30
|
+
store: Store,
|
|
31
|
+
): ReadonlySelector<T> | null
|
|
32
|
+
export function withdraw<T>(
|
|
33
|
+
token: TransactionToken<T>,
|
|
34
|
+
store: Store,
|
|
35
|
+
): Transaction<T extends ƒn ? T : never> | null
|
|
36
|
+
export function withdraw<T>(
|
|
37
|
+
token: ReadonlySelectorToken<T> | StateToken<T>,
|
|
38
|
+
store: Store,
|
|
39
|
+
): Atom<T> | ReadonlySelector<T> | Selector<T> | null
|
|
40
|
+
export function withdraw<T>(token: TimelineToken, store: Store): Timeline | null
|
|
41
|
+
export function withdraw<T>(
|
|
42
|
+
token:
|
|
43
|
+
| ReadonlySelectorToken<T>
|
|
44
|
+
| StateToken<T>
|
|
45
|
+
| TimelineToken
|
|
46
|
+
| TransactionToken<T>,
|
|
47
|
+
store: Store,
|
|
48
|
+
):
|
|
49
|
+
| Atom<T>
|
|
50
|
+
| ReadonlySelector<T>
|
|
51
|
+
| Selector<T>
|
|
52
|
+
| Timeline
|
|
53
|
+
| Transaction<T extends ƒn ? T : never>
|
|
54
|
+
| null {
|
|
55
|
+
let core = target(store)
|
|
56
|
+
let state =
|
|
57
|
+
core.atoms.get(token.key) ??
|
|
58
|
+
core.selectors.get(token.key) ??
|
|
59
|
+
core.readonlySelectors.get(token.key) ??
|
|
60
|
+
core.transactions.get(token.key) ??
|
|
61
|
+
core.timelines.get(token.key)
|
|
62
|
+
if (state) {
|
|
63
|
+
return state
|
|
64
|
+
}
|
|
65
|
+
if (store.transactionStatus.phase === `applying`) {
|
|
66
|
+
core = store.transactionStatus.core
|
|
67
|
+
state =
|
|
68
|
+
core.atoms.get(token.key) ??
|
|
69
|
+
core.selectors.get(token.key) ??
|
|
70
|
+
core.readonlySelectors.get(token.key) ??
|
|
71
|
+
core.transactions.get(token.key) ??
|
|
72
|
+
core.timelines.get(token.key)
|
|
73
|
+
|
|
74
|
+
if (state) {
|
|
75
|
+
store.config.logger?.info(`🛠️ add ${token.type} "${token.key}"`)
|
|
76
|
+
switch (state.type) {
|
|
77
|
+
case `atom`: {
|
|
78
|
+
store.atoms.set(token.key, state)
|
|
79
|
+
store.valueMap.set(token.key, state.default)
|
|
80
|
+
const stateKey = state.key
|
|
81
|
+
const familyKey = state.family?.key
|
|
82
|
+
let timelineKey = core.timelineAtoms.getRelatedKey(stateKey)
|
|
83
|
+
if (timelineKey === undefined && typeof familyKey === `string`) {
|
|
84
|
+
timelineKey = core.timelineAtoms.getRelatedKey(familyKey)
|
|
85
|
+
}
|
|
86
|
+
const timeline =
|
|
87
|
+
typeof timelineKey === `string`
|
|
88
|
+
? store.timelines.get(timelineKey)
|
|
89
|
+
: undefined
|
|
90
|
+
|
|
91
|
+
if (timeline) {
|
|
92
|
+
addAtomToTimeline(state, timeline, store)
|
|
93
|
+
}
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
case `selector`:
|
|
97
|
+
core.selectors.set(token.key, state)
|
|
98
|
+
break
|
|
99
|
+
case `readonly_selector`:
|
|
100
|
+
core.readonlySelectors.set(token.key, state)
|
|
101
|
+
break
|
|
102
|
+
case `transaction`:
|
|
103
|
+
core.transactions.set(token.key, state)
|
|
104
|
+
break
|
|
105
|
+
case `timeline`:
|
|
106
|
+
core.timelines.set(token.key, state)
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
return state
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class Subject<T> {
|
|
2
|
+
public Subscriber: (value: T) => void
|
|
3
|
+
|
|
4
|
+
public subscribers: Map<string, this[`Subscriber`]> = new Map()
|
|
5
|
+
|
|
6
|
+
public subscribe(key: string, subscriber: this[`Subscriber`]): () => void {
|
|
7
|
+
this.subscribers.set(key, subscriber)
|
|
8
|
+
const unsubscribe = () => this.unsubscribe(key)
|
|
9
|
+
return unsubscribe
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
private unsubscribe(key: string) {
|
|
13
|
+
this.subscribers.delete(key)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public next(value: T): void {
|
|
17
|
+
for (const subscriber of this.subscribers.values()) {
|
|
18
|
+
subscriber(value)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./subscribe-to-root-atoms"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Atom } from "../atom"
|
|
2
|
+
import type { ReadonlySelector, Selector } from "../selector"
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { IMPLICIT } from "../store"
|
|
5
|
+
import { target } from "../transaction"
|
|
6
|
+
|
|
7
|
+
export const recallState = <T>(
|
|
8
|
+
state: Atom<T> | ReadonlySelector<T> | Selector<T>,
|
|
9
|
+
store: Store = IMPLICIT.STORE,
|
|
10
|
+
): T => {
|
|
11
|
+
const core = target(store)
|
|
12
|
+
if (!core.operation.open) {
|
|
13
|
+
store.config.logger?.warn(
|
|
14
|
+
`recall called outside of an operation. This is probably a bug.`,
|
|
15
|
+
)
|
|
16
|
+
return core.valueMap.get(state.key)
|
|
17
|
+
}
|
|
18
|
+
return core.operation.prev.get(state.key)
|
|
19
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getState__INTERNAL } from "../get-state-internal"
|
|
2
|
+
import type { ReadonlySelector, Selector } from "../selector"
|
|
3
|
+
import { traceAllSelectorAtoms } from "../selector"
|
|
4
|
+
import type { Store } from "../store"
|
|
5
|
+
import { withdraw } from "../store"
|
|
6
|
+
import { recallState } from "./recall-state"
|
|
7
|
+
|
|
8
|
+
export const subscribeToRootAtoms = <T>(
|
|
9
|
+
state: ReadonlySelector<T> | Selector<T>,
|
|
10
|
+
store: Store,
|
|
11
|
+
): (() => void)[] | null => {
|
|
12
|
+
const dependencySubscriptions =
|
|
13
|
+
`default` in state
|
|
14
|
+
? null
|
|
15
|
+
: traceAllSelectorAtoms(state.key, store).map((atomToken) => {
|
|
16
|
+
const atom = withdraw(atomToken, store)
|
|
17
|
+
if (atom === null) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Atom "${atomToken.key}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`,
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
return atom.subject.subscribe(
|
|
23
|
+
`${state.type}:${state.key}`,
|
|
24
|
+
(atomChange) => {
|
|
25
|
+
store.config.logger?.info(
|
|
26
|
+
`📢 selector "${state.key}" saw root "${atomToken.key}" go (`,
|
|
27
|
+
atomChange.oldValue,
|
|
28
|
+
`->`,
|
|
29
|
+
atomChange.newValue,
|
|
30
|
+
`)`,
|
|
31
|
+
)
|
|
32
|
+
const oldValue = recallState(state, store)
|
|
33
|
+
// ❗ this retrieves a stale cached value when applying a transaction on the server
|
|
34
|
+
const newValue = getState__INTERNAL(state, store)
|
|
35
|
+
store.config.logger?.info(
|
|
36
|
+
` <- "${state.key}" went (`,
|
|
37
|
+
oldValue,
|
|
38
|
+
`->`,
|
|
39
|
+
newValue,
|
|
40
|
+
`)`,
|
|
41
|
+
)
|
|
42
|
+
state.subject.next({ newValue, oldValue })
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
return dependencySubscriptions
|
|
47
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { AtomToken, TimelineUpdate } from "atom.io"
|
|
2
|
+
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { IMPLICIT, withdraw } from "../store"
|
|
5
|
+
import { target } from "../transaction"
|
|
6
|
+
import type {
|
|
7
|
+
Timeline,
|
|
8
|
+
TimelineAtomUpdate,
|
|
9
|
+
TimelineTransactionUpdate,
|
|
10
|
+
} from "./timeline-internal"
|
|
11
|
+
|
|
12
|
+
export const addAtomToTimeline = (
|
|
13
|
+
atomToken: AtomToken<any>,
|
|
14
|
+
tl: Timeline,
|
|
15
|
+
store: Store = IMPLICIT.STORE,
|
|
16
|
+
): void => {
|
|
17
|
+
const atom = withdraw(atomToken, store)
|
|
18
|
+
if (atom === null) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Cannot subscribe to atom "${atomToken.key}" because it has not been initialized in store "${store.config.name}"`,
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
atom.subject.subscribe(`timeline`, (update) => {
|
|
24
|
+
const currentSelectorKey =
|
|
25
|
+
store.operation.open && store.operation.token.type === `selector`
|
|
26
|
+
? store.operation.token.key
|
|
27
|
+
: null
|
|
28
|
+
const currentSelectorTime =
|
|
29
|
+
store.operation.open && store.operation.token.type === `selector`
|
|
30
|
+
? store.operation.time
|
|
31
|
+
: null
|
|
32
|
+
const currentTransactionKey =
|
|
33
|
+
store.transactionStatus.phase === `applying`
|
|
34
|
+
? store.transactionStatus.key
|
|
35
|
+
: null
|
|
36
|
+
const currentTransactionTime =
|
|
37
|
+
store.transactionStatus.phase === `applying`
|
|
38
|
+
? store.transactionStatus.time
|
|
39
|
+
: null
|
|
40
|
+
|
|
41
|
+
store.config.logger?.info(
|
|
42
|
+
`⏳ timeline "${tl.key}" saw atom "${atomToken.key}" go (`,
|
|
43
|
+
update.oldValue,
|
|
44
|
+
`->`,
|
|
45
|
+
update.newValue,
|
|
46
|
+
currentTransactionKey
|
|
47
|
+
? `) in transaction "${currentTransactionKey}"`
|
|
48
|
+
: currentSelectorKey
|
|
49
|
+
? `) in selector "${currentSelectorKey}"`
|
|
50
|
+
: `)`,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if (tl.timeTraveling === null) {
|
|
54
|
+
if (tl.selectorTime && tl.selectorTime !== currentSelectorTime) {
|
|
55
|
+
const mostRecentUpdate: TimelineUpdate | undefined = tl.history.at(-1)
|
|
56
|
+
if (mostRecentUpdate === undefined) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Timeline "${tl.key}" has a selectorTime, but no history. This is most likely a bug in AtomIO.`,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
currentTransactionKey &&
|
|
64
|
+
store.transactionStatus.phase === `applying`
|
|
65
|
+
) {
|
|
66
|
+
const currentTransaction = withdraw(
|
|
67
|
+
{ key: currentTransactionKey, type: `transaction` },
|
|
68
|
+
store,
|
|
69
|
+
)
|
|
70
|
+
if (currentTransaction === null) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Transaction "${currentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${currentTransactionKey}".`,
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
if (tl.transactionKey !== currentTransactionKey) {
|
|
76
|
+
if (tl.transactionKey) {
|
|
77
|
+
store.config.logger?.error(
|
|
78
|
+
`Timeline "${tl.key}" was unable to resolve transaction "${tl.transactionKey}. This is probably a bug.`,
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
tl.transactionKey = currentTransactionKey
|
|
82
|
+
const unsubscribe = currentTransaction.subject.subscribe(
|
|
83
|
+
`timeline:${tl.key}`,
|
|
84
|
+
(update) => {
|
|
85
|
+
unsubscribe()
|
|
86
|
+
if (tl.timeTraveling === null && currentTransactionTime) {
|
|
87
|
+
if (tl.at !== tl.history.length) {
|
|
88
|
+
tl.history.splice(tl.at)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const atomUpdates = update.atomUpdates.filter((atomUpdate) => {
|
|
92
|
+
const core = target(store)
|
|
93
|
+
const atomOrFamilyKeys = core.timelineAtoms.getRelatedKeys(
|
|
94
|
+
tl.key,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return atomOrFamilyKeys
|
|
98
|
+
? [...atomOrFamilyKeys].some(
|
|
99
|
+
(key) =>
|
|
100
|
+
key === atomUpdate.key ||
|
|
101
|
+
key === atomUpdate.family?.key,
|
|
102
|
+
)
|
|
103
|
+
: false
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const timelineTransactionUpdate: TimelineTransactionUpdate = {
|
|
107
|
+
type: `transaction_update`,
|
|
108
|
+
timestamp: currentTransactionTime,
|
|
109
|
+
...update,
|
|
110
|
+
atomUpdates,
|
|
111
|
+
}
|
|
112
|
+
tl.history.push(timelineTransactionUpdate)
|
|
113
|
+
tl.at = tl.history.length
|
|
114
|
+
tl.subject.next(timelineTransactionUpdate)
|
|
115
|
+
}
|
|
116
|
+
tl.transactionKey = null
|
|
117
|
+
store.config.logger?.info(
|
|
118
|
+
`⌛ timeline "${tl.key}" got a transaction_update "${update.key}"`,
|
|
119
|
+
)
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
} else if (currentSelectorKey && currentSelectorTime) {
|
|
124
|
+
let latestUpdate: TimelineUpdate | undefined = tl.history.at(-1)
|
|
125
|
+
|
|
126
|
+
if (currentSelectorTime !== tl.selectorTime) {
|
|
127
|
+
latestUpdate = {
|
|
128
|
+
type: `selector_update`,
|
|
129
|
+
timestamp: currentSelectorTime,
|
|
130
|
+
key: currentSelectorKey,
|
|
131
|
+
atomUpdates: [],
|
|
132
|
+
}
|
|
133
|
+
latestUpdate.atomUpdates.push({
|
|
134
|
+
key: atom.key,
|
|
135
|
+
type: `atom_update`,
|
|
136
|
+
...update,
|
|
137
|
+
})
|
|
138
|
+
if (tl.at !== tl.history.length) {
|
|
139
|
+
tl.history.splice(tl.at)
|
|
140
|
+
}
|
|
141
|
+
tl.history.push(latestUpdate)
|
|
142
|
+
|
|
143
|
+
store.config.logger?.info(
|
|
144
|
+
`⌛ timeline "${tl.key}" got a selector_update "${currentSelectorKey}" with`,
|
|
145
|
+
latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
tl.at = tl.history.length
|
|
149
|
+
tl.selectorTime = currentSelectorTime
|
|
150
|
+
} else {
|
|
151
|
+
if (latestUpdate?.type === `selector_update`) {
|
|
152
|
+
latestUpdate.atomUpdates.push({
|
|
153
|
+
key: atom.key,
|
|
154
|
+
type: `atom_update`,
|
|
155
|
+
...update,
|
|
156
|
+
})
|
|
157
|
+
store.config.logger?.info(
|
|
158
|
+
` ⌛ timeline "${tl.key}" set selector_update "${currentSelectorKey}" to`,
|
|
159
|
+
latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key),
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (latestUpdate) tl.subject.next(latestUpdate)
|
|
164
|
+
} else {
|
|
165
|
+
const timestamp = Date.now()
|
|
166
|
+
tl.selectorTime = null
|
|
167
|
+
if (tl.at !== tl.history.length) {
|
|
168
|
+
tl.history.splice(tl.at)
|
|
169
|
+
}
|
|
170
|
+
const atomUpdate: TimelineAtomUpdate = {
|
|
171
|
+
type: `atom_update`,
|
|
172
|
+
timestamp,
|
|
173
|
+
key: atom.key,
|
|
174
|
+
oldValue: update.oldValue,
|
|
175
|
+
newValue: update.newValue,
|
|
176
|
+
}
|
|
177
|
+
if (atom.family) {
|
|
178
|
+
atomUpdate.family = atom.family
|
|
179
|
+
}
|
|
180
|
+
tl.history.push(atomUpdate)
|
|
181
|
+
tl.subject.next(atomUpdate)
|
|
182
|
+
store.config.logger?.info(
|
|
183
|
+
`⌛ timeline "${tl.key}" got an atom_update to "${atom.key}"`,
|
|
184
|
+
)
|
|
185
|
+
tl.at = tl.history.length
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
}
|