atom.io 0.5.0 → 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/dist/index.d.mts +82 -66
- package/dist/index.d.ts +82 -66
- package/dist/index.js +482 -360
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +481 -360
- package/dist/index.mjs.map +1 -1
- package/json/dist/index.js.map +1 -1
- package/json/dist/index.mjs.map +1 -1
- package/package.json +12 -5
- package/react/dist/index.d.mts +18 -11
- 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 +4 -4
- package/react-devtools/dist/index.d.ts +4 -4
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs.map +1 -1
- package/realtime/dist/index.d.mts +3 -1
- package/realtime/dist/index.d.ts +3 -1
- package/realtime/dist/index.js +23 -0
- package/realtime/dist/index.js.map +1 -1
- package/realtime/dist/index.mjs +22 -0
- package/realtime/dist/index.mjs.map +1 -1
- 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 +0 -6
- package/src/internal/get.ts +17 -3
- 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 -207
- package/src/internal/store.ts +43 -16
- package/src/internal/subscribe-internal.ts +1 -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 +37 -156
- 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 +14 -146
- 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/space-states.ts +2 -2
- package/src/realtime/README.md +33 -0
- package/src/realtime/hook-composition/index.ts +1 -0
- package/src/realtime/hook-composition/receive-state.ts +29 -0
- package/src/realtime/hook-composition/receive-transaction.ts +2 -3
- 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/silo.ts +12 -4
- package/src/subscribe.ts +30 -2
- package/src/timeline.ts +10 -0
- package/src/transaction.ts +15 -10
- package/src/realtime-client/hook-composition/compose-realtime-hooks.ts +0 -62
- package/src/realtime-client/hook-composition/realtime-client-family-member.ts +0 -28
- package/src/realtime-client/hook-composition/realtime-client-family.ts +0 -26
- package/src/realtime-client/hook-composition/realtime-client-single.ts +0 -24
- package/src/realtime-client/hook-composition/realtime-client-transaction.ts +0 -35
- package/src/realtime-client/index.ts +0 -1
|
@@ -1,187 +1,66 @@
|
|
|
1
1
|
import HAMT from "hamt_plus"
|
|
2
|
+
import * as Rx from "rxjs"
|
|
2
3
|
|
|
3
4
|
import type { ƒn } from "~/packages/anvl/src/function"
|
|
4
5
|
|
|
5
|
-
import type {
|
|
6
|
-
import { target, IMPLICIT
|
|
7
|
-
import
|
|
6
|
+
import type { Store } from "."
|
|
7
|
+
import { target, IMPLICIT } from "."
|
|
8
|
+
import { addAtomToTimeline } from "./timeline/add-atom-to-timeline"
|
|
9
|
+
import type {
|
|
10
|
+
StateUpdate,
|
|
11
|
+
TimelineOptions,
|
|
12
|
+
TimelineToken,
|
|
13
|
+
TimelineUpdate,
|
|
14
|
+
TransactionUpdate,
|
|
15
|
+
} from ".."
|
|
8
16
|
|
|
9
|
-
export type
|
|
17
|
+
export type TimelineAtomUpdate = StateUpdate<unknown> & {
|
|
10
18
|
key: string
|
|
11
|
-
type: `timeline`
|
|
12
|
-
next: () => void
|
|
13
|
-
prev: () => void
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type TimelineAtomUpdate = KeyedStateUpdate<unknown> & {
|
|
17
19
|
type: `atom_update`
|
|
20
|
+
timestamp: number
|
|
18
21
|
}
|
|
19
22
|
export type TimelineSelectorUpdate = {
|
|
20
23
|
key: string
|
|
21
24
|
type: `selector_update`
|
|
22
|
-
|
|
25
|
+
timestamp: number
|
|
26
|
+
atomUpdates: Omit<TimelineAtomUpdate, `timestamp`>[]
|
|
23
27
|
}
|
|
24
28
|
export type TimelineTransactionUpdate = TransactionUpdate<ƒn> & {
|
|
29
|
+
key: string
|
|
25
30
|
type: `transaction_update`
|
|
31
|
+
timestamp: number
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
export type
|
|
34
|
+
export type Timeline = {
|
|
35
|
+
key: string
|
|
29
36
|
at: number
|
|
30
37
|
timeTraveling: boolean
|
|
31
|
-
history:
|
|
32
|
-
| TimelineAtomUpdate
|
|
33
|
-
| TimelineSelectorUpdate
|
|
34
|
-
| TimelineTransactionUpdate
|
|
35
|
-
)[]
|
|
38
|
+
history: TimelineUpdate[]
|
|
36
39
|
selectorTime: number | null
|
|
37
40
|
transactionKey: string | null
|
|
41
|
+
install: (store: Store) => void
|
|
42
|
+
subject: Rx.Subject<
|
|
43
|
+
TimelineAtomUpdate | TimelineSelectorUpdate | TimelineTransactionUpdate
|
|
44
|
+
>
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
export function timeline__INTERNAL(
|
|
41
48
|
options: TimelineOptions,
|
|
42
|
-
store: Store = IMPLICIT.STORE
|
|
49
|
+
store: Store = IMPLICIT.STORE,
|
|
50
|
+
data: Timeline | null = null
|
|
43
51
|
): TimelineToken {
|
|
44
|
-
const
|
|
52
|
+
const tl: Timeline = {
|
|
53
|
+
key: options.key,
|
|
45
54
|
at: 0,
|
|
46
55
|
timeTraveling: false,
|
|
47
|
-
history: [],
|
|
48
56
|
selectorTime: null,
|
|
49
57
|
transactionKey: null,
|
|
58
|
+
...data,
|
|
59
|
+
history: data?.history.map((update) => ({ ...update })) ?? [],
|
|
60
|
+
install: (store) => timeline__INTERNAL(options, store, tl),
|
|
61
|
+
subject: new Rx.Subject(),
|
|
50
62
|
}
|
|
51
63
|
|
|
52
|
-
const subscribeToAtom = (token: AtomToken<any>) => {
|
|
53
|
-
const state = withdraw(token, store)
|
|
54
|
-
if (state === null) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`Cannot subscribe to atom "${token.key}" because it has not been initialized in store "${store.config.name}"`
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
state.subject.subscribe((update) => {
|
|
60
|
-
const storeCurrentSelectorKey =
|
|
61
|
-
store.operation.open && store.operation.token.type === `selector`
|
|
62
|
-
? store.operation.token.key
|
|
63
|
-
: null
|
|
64
|
-
const storeCurrentSelectorTime =
|
|
65
|
-
store.operation.open && store.operation.token.type === `selector`
|
|
66
|
-
? store.operation.time
|
|
67
|
-
: null
|
|
68
|
-
|
|
69
|
-
const storeCurrentTransactionKey =
|
|
70
|
-
store.transactionStatus.phase === `applying`
|
|
71
|
-
? store.transactionStatus.key
|
|
72
|
-
: null
|
|
73
|
-
store.config.logger?.info(
|
|
74
|
-
`⏳ timeline "${options.key}" saw atom "${token.key}" go (`,
|
|
75
|
-
update.oldValue,
|
|
76
|
-
`->`,
|
|
77
|
-
update.newValue,
|
|
78
|
-
storeCurrentTransactionKey
|
|
79
|
-
? `) in transaction "${storeCurrentTransactionKey}"`
|
|
80
|
-
: storeCurrentSelectorKey
|
|
81
|
-
? `) in selector "${storeCurrentSelectorKey}"`
|
|
82
|
-
: `)`
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
storeCurrentTransactionKey &&
|
|
87
|
-
store.transactionStatus.phase === `applying`
|
|
88
|
-
) {
|
|
89
|
-
const currentTransaction = withdraw(
|
|
90
|
-
{ key: storeCurrentTransactionKey, type: `transaction` },
|
|
91
|
-
store
|
|
92
|
-
)
|
|
93
|
-
if (currentTransaction === null) {
|
|
94
|
-
throw new Error(
|
|
95
|
-
`Transaction "${storeCurrentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${storeCurrentTransactionKey}".`
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
if (timelineData.transactionKey !== storeCurrentTransactionKey) {
|
|
99
|
-
if (timelineData.transactionKey) {
|
|
100
|
-
store.config.logger?.error(
|
|
101
|
-
`Timeline "${options.key}" was unable to resolve transaction "${timelineData.transactionKey}. This is probably a bug.`
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
timelineData.transactionKey = storeCurrentTransactionKey
|
|
105
|
-
const subscription = currentTransaction.subject.subscribe((update) => {
|
|
106
|
-
if (timelineData.timeTraveling === false) {
|
|
107
|
-
if (timelineData.at !== timelineData.history.length) {
|
|
108
|
-
timelineData.history.splice(timelineData.at)
|
|
109
|
-
}
|
|
110
|
-
timelineData.history.push({
|
|
111
|
-
type: `transaction_update`,
|
|
112
|
-
...update,
|
|
113
|
-
atomUpdates: update.atomUpdates.filter((atomUpdate) =>
|
|
114
|
-
options.atoms.some((atom) => atom.key === atomUpdate.key)
|
|
115
|
-
),
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
timelineData.at = timelineData.history.length
|
|
119
|
-
subscription.unsubscribe()
|
|
120
|
-
timelineData.transactionKey = null
|
|
121
|
-
store.config.logger?.info(
|
|
122
|
-
`⌛ timeline "${options.key}" got a transaction_update "${update.key}"`
|
|
123
|
-
)
|
|
124
|
-
})
|
|
125
|
-
}
|
|
126
|
-
} else if (storeCurrentSelectorKey) {
|
|
127
|
-
if (timelineData.timeTraveling === false) {
|
|
128
|
-
if (storeCurrentSelectorTime !== timelineData.selectorTime) {
|
|
129
|
-
const newSelectorUpdate: TimelineSelectorUpdate = {
|
|
130
|
-
type: `selector_update`,
|
|
131
|
-
key: storeCurrentSelectorKey,
|
|
132
|
-
atomUpdates: [],
|
|
133
|
-
}
|
|
134
|
-
newSelectorUpdate.atomUpdates.push({
|
|
135
|
-
key: token.key,
|
|
136
|
-
type: `atom_update`,
|
|
137
|
-
...update,
|
|
138
|
-
})
|
|
139
|
-
if (timelineData.at !== timelineData.history.length) {
|
|
140
|
-
timelineData.history.splice(timelineData.at)
|
|
141
|
-
}
|
|
142
|
-
timelineData.history.push(newSelectorUpdate)
|
|
143
|
-
|
|
144
|
-
store.config.logger?.info(
|
|
145
|
-
`⌛ timeline "${options.key}" got a selector_update "${storeCurrentSelectorKey}" with`,
|
|
146
|
-
newSelectorUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
|
|
147
|
-
)
|
|
148
|
-
timelineData.at = timelineData.history.length
|
|
149
|
-
timelineData.selectorTime = storeCurrentSelectorTime
|
|
150
|
-
} else {
|
|
151
|
-
const latestUpdate = timelineData.history.at(-1)
|
|
152
|
-
if (latestUpdate?.type === `selector_update`) {
|
|
153
|
-
latestUpdate.atomUpdates.push({
|
|
154
|
-
key: token.key,
|
|
155
|
-
type: `atom_update`,
|
|
156
|
-
...update,
|
|
157
|
-
})
|
|
158
|
-
store.config.logger?.info(
|
|
159
|
-
` ⌛ timeline "${options.key}" set selector_update "${storeCurrentSelectorKey}" to`,
|
|
160
|
-
latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key)
|
|
161
|
-
)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
} else {
|
|
166
|
-
if (timelineData.timeTraveling === false) {
|
|
167
|
-
timelineData.selectorTime = null
|
|
168
|
-
if (timelineData.at !== timelineData.history.length) {
|
|
169
|
-
timelineData.history.splice(timelineData.at)
|
|
170
|
-
}
|
|
171
|
-
timelineData.history.push({
|
|
172
|
-
type: `atom_update`,
|
|
173
|
-
key: token.key,
|
|
174
|
-
oldValue: update.oldValue,
|
|
175
|
-
newValue: update.newValue,
|
|
176
|
-
})
|
|
177
|
-
store.config.logger?.info(
|
|
178
|
-
`⌛ timeline "${options.key}" got a state_update to "${token.key}"`
|
|
179
|
-
)
|
|
180
|
-
timelineData.at = timelineData.history.length
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
64
|
const core = target(store)
|
|
186
65
|
for (const tokenOrFamily of options.atoms) {
|
|
187
66
|
const timelineKey = core.timelineAtoms.getRelatedId(tokenOrFamily.key)
|
|
@@ -193,7 +72,9 @@ export function timeline__INTERNAL(
|
|
|
193
72
|
}
|
|
194
73
|
if (tokenOrFamily.type === `atom_family`) {
|
|
195
74
|
const family = tokenOrFamily
|
|
196
|
-
family.subject.subscribe((token) =>
|
|
75
|
+
family.subject.subscribe((token) =>
|
|
76
|
+
addAtomToTimeline(token, options.atoms, tl, store)
|
|
77
|
+
)
|
|
197
78
|
} else {
|
|
198
79
|
const token = tokenOrFamily
|
|
199
80
|
if (`family` in token && token.family) {
|
|
@@ -207,7 +88,7 @@ export function timeline__INTERNAL(
|
|
|
207
88
|
continue
|
|
208
89
|
}
|
|
209
90
|
}
|
|
210
|
-
|
|
91
|
+
addAtomToTimeline(token, options.atoms, tl, store)
|
|
211
92
|
}
|
|
212
93
|
core.timelineAtoms = core.timelineAtoms.set({
|
|
213
94
|
atomKey: tokenOrFamily.key,
|
|
@@ -215,7 +96,7 @@ export function timeline__INTERNAL(
|
|
|
215
96
|
})
|
|
216
97
|
}
|
|
217
98
|
|
|
218
|
-
store.
|
|
99
|
+
store.timelines = HAMT.set(options.key, tl, store.timelines)
|
|
219
100
|
const token: TimelineToken = {
|
|
220
101
|
key: options.key,
|
|
221
102
|
type: `timeline`,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Store } from ".."
|
|
2
|
+
|
|
3
|
+
export const abortTransaction = (store: Store): void => {
|
|
4
|
+
if (store.transactionStatus.phase === `idle`) {
|
|
5
|
+
store.config.logger?.warn(
|
|
6
|
+
`abortTransaction called outside of a transaction. This is probably a bug.`
|
|
7
|
+
)
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
store.transactionStatus = { phase: `idle` }
|
|
11
|
+
store.config.logger?.info(`🪂`, `transaction fail`)
|
|
12
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import HAMT from "hamt_plus"
|
|
2
|
+
|
|
3
|
+
import type { ƒn } from "~/packages/anvl/src/function"
|
|
4
|
+
|
|
5
|
+
import type { Store } from ".."
|
|
6
|
+
import { withdraw } from ".."
|
|
7
|
+
import type { AtomToken } from "../.."
|
|
8
|
+
import { setState } from "../.."
|
|
9
|
+
|
|
10
|
+
export const applyTransaction = <ƒ extends ƒn>(
|
|
11
|
+
output: ReturnType<ƒ>,
|
|
12
|
+
store: Store
|
|
13
|
+
): void => {
|
|
14
|
+
if (store.transactionStatus.phase !== `building`) {
|
|
15
|
+
store.config.logger?.warn(
|
|
16
|
+
`abortTransaction called outside of a transaction. This is probably a bug.`
|
|
17
|
+
)
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
store.config.logger?.info(
|
|
21
|
+
`🛃 apply transaction "${store.transactionStatus.key}"`
|
|
22
|
+
)
|
|
23
|
+
store.transactionStatus.phase = `applying`
|
|
24
|
+
store.transactionStatus.output = output
|
|
25
|
+
const { atomUpdates } = store.transactionStatus
|
|
26
|
+
|
|
27
|
+
for (const { key, newValue } of atomUpdates) {
|
|
28
|
+
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
29
|
+
if (!HAMT.has(token.key, store.valueMap)) {
|
|
30
|
+
const newAtom = HAMT.get(token.key, store.transactionStatus.core.atoms)
|
|
31
|
+
store.atoms = HAMT.set(newAtom.key, newAtom, store.atoms)
|
|
32
|
+
store.valueMap = HAMT.set(newAtom.key, newAtom.default, store.valueMap)
|
|
33
|
+
store.config.logger?.info(`🔧`, `add atom "${newAtom.key}"`)
|
|
34
|
+
}
|
|
35
|
+
setState(token, newValue, store)
|
|
36
|
+
}
|
|
37
|
+
const myTransaction = withdraw<ƒ>(
|
|
38
|
+
{ key: store.transactionStatus.key, type: `transaction` },
|
|
39
|
+
store
|
|
40
|
+
)
|
|
41
|
+
if (myTransaction === null) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Transaction "${store.transactionStatus.key}" not found. Absurd. How is this running?`
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
myTransaction.subject.next({
|
|
47
|
+
key: store.transactionStatus.key,
|
|
48
|
+
atomUpdates,
|
|
49
|
+
output,
|
|
50
|
+
params: store.transactionStatus.params as Parameters<ƒ>,
|
|
51
|
+
})
|
|
52
|
+
store.transactionStatus = { phase: `idle` }
|
|
53
|
+
store.config.logger?.info(`🛬`, `transaction done`)
|
|
54
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Store } from ".."
|
|
2
|
+
|
|
3
|
+
export const buildTransaction = (
|
|
4
|
+
key: string,
|
|
5
|
+
params: any[],
|
|
6
|
+
store: Store
|
|
7
|
+
): void => {
|
|
8
|
+
store.transactionStatus = {
|
|
9
|
+
key,
|
|
10
|
+
phase: `building`,
|
|
11
|
+
time: Date.now(),
|
|
12
|
+
core: {
|
|
13
|
+
atoms: store.atoms,
|
|
14
|
+
atomsThatAreDefault: store.atomsThatAreDefault,
|
|
15
|
+
operation: { open: false },
|
|
16
|
+
readonlySelectors: store.readonlySelectors,
|
|
17
|
+
timelines: store.timelines,
|
|
18
|
+
timelineAtoms: store.timelineAtoms,
|
|
19
|
+
transactions: store.transactions,
|
|
20
|
+
selectorAtoms: store.selectorAtoms,
|
|
21
|
+
selectorGraph: store.selectorGraph,
|
|
22
|
+
selectors: store.selectors,
|
|
23
|
+
valueMap: store.valueMap,
|
|
24
|
+
},
|
|
25
|
+
atomUpdates: [],
|
|
26
|
+
params,
|
|
27
|
+
output: undefined,
|
|
28
|
+
}
|
|
29
|
+
store.config.logger?.info(
|
|
30
|
+
`🛫`,
|
|
31
|
+
`transaction "${key}" started in store "${store.config.name}"`
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ƒn } from "~/packages/anvl/src/function"
|
|
2
|
+
|
|
3
|
+
import type { StoreCore } from ".."
|
|
4
|
+
import type { StateUpdate, TransactionUpdate } from "../.."
|
|
5
|
+
|
|
6
|
+
export * from "./abort-transaction"
|
|
7
|
+
export * from "./apply-transaction"
|
|
8
|
+
export * from "./build-transaction"
|
|
9
|
+
export * from "./redo-transaction"
|
|
10
|
+
export * from "./undo-transaction"
|
|
11
|
+
|
|
12
|
+
export const TRANSACTION_PHASES = [`idle`, `building`, `applying`] as const
|
|
13
|
+
export type TransactionPhase = (typeof TRANSACTION_PHASES)[number]
|
|
14
|
+
|
|
15
|
+
export type TransactionUpdateInProgress<ƒ extends ƒn> = TransactionUpdate<ƒ> & {
|
|
16
|
+
phase: `applying` | `building`
|
|
17
|
+
time: number
|
|
18
|
+
core: StoreCore
|
|
19
|
+
}
|
|
20
|
+
export type TransactionIdle = {
|
|
21
|
+
phase: `idle`
|
|
22
|
+
}
|
|
23
|
+
export type TransactionStatus<ƒ extends ƒn> =
|
|
24
|
+
| TransactionIdle
|
|
25
|
+
| TransactionUpdateInProgress<ƒ>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ƒn } from "~/packages/anvl/src/function"
|
|
2
|
+
|
|
3
|
+
import type { Store } from ".."
|
|
4
|
+
import { withdraw } from ".."
|
|
5
|
+
import type { AtomToken, TransactionUpdate } from "../.."
|
|
6
|
+
import { setState } from "../.."
|
|
7
|
+
|
|
8
|
+
export const redoTransactionUpdate = <ƒ extends ƒn>(
|
|
9
|
+
update: TransactionUpdate<ƒ>,
|
|
10
|
+
store: Store
|
|
11
|
+
): void => {
|
|
12
|
+
store.config.logger?.info(` ⏭ redo transaction "${update.key}" (redo)`)
|
|
13
|
+
for (const { key, newValue } of update.atomUpdates) {
|
|
14
|
+
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
15
|
+
const state = withdraw(token, store)
|
|
16
|
+
if (state === null) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
setState(state, newValue, store)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ƒn } from "~/packages/anvl/src/function"
|
|
2
|
+
|
|
3
|
+
import type { Store } from ".."
|
|
4
|
+
import { withdraw } from ".."
|
|
5
|
+
import type { AtomToken, TransactionUpdate } from "../.."
|
|
6
|
+
import { setState } from "../.."
|
|
7
|
+
|
|
8
|
+
export const undoTransactionUpdate = <ƒ extends ƒn>(
|
|
9
|
+
update: TransactionUpdate<ƒ>,
|
|
10
|
+
store: Store
|
|
11
|
+
): void => {
|
|
12
|
+
store.config.logger?.info(` ⏮ undo transaction "${update.key}" (undo)`)
|
|
13
|
+
for (const { key, oldValue } of update.atomUpdates) {
|
|
14
|
+
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
15
|
+
const state = withdraw(token, store)
|
|
16
|
+
if (state === null) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
setState(state, oldValue, store)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -4,155 +4,22 @@ import * as Rx from "rxjs"
|
|
|
4
4
|
import type { ƒn } from "~/packages/anvl/src/function"
|
|
5
5
|
|
|
6
6
|
import type { Store, StoreCore } from "."
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
abortTransaction,
|
|
9
|
+
applyTransaction,
|
|
10
|
+
buildTransaction,
|
|
11
|
+
deposit,
|
|
12
|
+
IMPLICIT,
|
|
13
|
+
} from "."
|
|
14
|
+
import type { TransactionOptions, TransactionToken, TransactionUpdate } from ".."
|
|
8
15
|
import { getState, setState } from ".."
|
|
9
|
-
import type {
|
|
10
|
-
AtomToken,
|
|
11
|
-
StateUpdate,
|
|
12
|
-
Transaction,
|
|
13
|
-
TransactionOptions,
|
|
14
|
-
TransactionToken,
|
|
15
|
-
} from ".."
|
|
16
16
|
|
|
17
|
-
export
|
|
18
|
-
export type TransactionPhase = (typeof TRANSACTION_PHASES)[number]
|
|
19
|
-
|
|
20
|
-
export type KeyedStateUpdate<T> = StateUpdate<T> & {
|
|
21
|
-
key: string
|
|
22
|
-
}
|
|
23
|
-
export type TransactionUpdate<ƒ extends ƒn> = {
|
|
17
|
+
export type Transaction<ƒ extends ƒn> = {
|
|
24
18
|
key: string
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export type TransactionUpdateInProgress<ƒ extends ƒn> = TransactionUpdate<ƒ> & {
|
|
31
|
-
phase: `applying` | `building`
|
|
32
|
-
core: StoreCore
|
|
33
|
-
}
|
|
34
|
-
export type TransactionIdle = {
|
|
35
|
-
phase: `idle`
|
|
36
|
-
}
|
|
37
|
-
export type TransactionStatus<ƒ extends ƒn> =
|
|
38
|
-
| TransactionIdle
|
|
39
|
-
| TransactionUpdateInProgress<ƒ>
|
|
40
|
-
|
|
41
|
-
export const buildTransaction = (
|
|
42
|
-
key: string,
|
|
43
|
-
params: any[],
|
|
44
|
-
store: Store
|
|
45
|
-
): void => {
|
|
46
|
-
store.transactionStatus = {
|
|
47
|
-
key,
|
|
48
|
-
phase: `building`,
|
|
49
|
-
core: {
|
|
50
|
-
atoms: store.atoms,
|
|
51
|
-
atomsThatAreDefault: store.atomsThatAreDefault,
|
|
52
|
-
operation: { open: false },
|
|
53
|
-
readonlySelectors: store.readonlySelectors,
|
|
54
|
-
timelines: store.timelines,
|
|
55
|
-
timelineAtoms: store.timelineAtoms,
|
|
56
|
-
transactions: store.transactions,
|
|
57
|
-
selectorAtoms: store.selectorAtoms,
|
|
58
|
-
selectorGraph: store.selectorGraph,
|
|
59
|
-
selectors: store.selectors,
|
|
60
|
-
valueMap: store.valueMap,
|
|
61
|
-
},
|
|
62
|
-
atomUpdates: [],
|
|
63
|
-
params,
|
|
64
|
-
output: undefined,
|
|
65
|
-
}
|
|
66
|
-
store.config.logger?.info(`🛫`, `transaction "${key}" started`)
|
|
67
|
-
}
|
|
68
|
-
export const applyTransaction = <ƒ extends ƒn>(
|
|
69
|
-
output: ReturnType<ƒ>,
|
|
70
|
-
store: Store
|
|
71
|
-
): void => {
|
|
72
|
-
if (store.transactionStatus.phase !== `building`) {
|
|
73
|
-
store.config.logger?.warn(
|
|
74
|
-
`abortTransaction called outside of a transaction. This is probably a bug.`
|
|
75
|
-
)
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
store.config.logger?.info(
|
|
79
|
-
`🛃 apply transaction "${store.transactionStatus.key}"`
|
|
80
|
-
)
|
|
81
|
-
store.transactionStatus.phase = `applying`
|
|
82
|
-
store.transactionStatus.output = output
|
|
83
|
-
const { atomUpdates } = store.transactionStatus
|
|
84
|
-
|
|
85
|
-
for (const { key, newValue } of atomUpdates) {
|
|
86
|
-
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
87
|
-
if (!HAMT.has(token.key, store.valueMap)) {
|
|
88
|
-
const newAtom = HAMT.get(token.key, store.transactionStatus.core.atoms)
|
|
89
|
-
store.atoms = HAMT.set(newAtom.key, newAtom, store.atoms)
|
|
90
|
-
store.valueMap = HAMT.set(newAtom.key, newAtom.default, store.valueMap)
|
|
91
|
-
store.config.logger?.info(`🔧`, `add atom "${newAtom.key}"`)
|
|
92
|
-
}
|
|
93
|
-
setState(token, newValue, store)
|
|
94
|
-
}
|
|
95
|
-
const myTransaction = withdraw<ƒ>(
|
|
96
|
-
{ key: store.transactionStatus.key, type: `transaction` },
|
|
97
|
-
store
|
|
98
|
-
)
|
|
99
|
-
if (myTransaction === null) {
|
|
100
|
-
throw new Error(
|
|
101
|
-
`Transaction "${store.transactionStatus.key}" not found. Absurd. How is this running?`
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
myTransaction.subject.next({
|
|
105
|
-
key: store.transactionStatus.key,
|
|
106
|
-
atomUpdates,
|
|
107
|
-
output,
|
|
108
|
-
params: store.transactionStatus.params as Parameters<ƒ>,
|
|
109
|
-
})
|
|
110
|
-
store.transactionStatus = { phase: `idle` }
|
|
111
|
-
store.config.logger?.info(`🛬`, `transaction done`)
|
|
112
|
-
}
|
|
113
|
-
export const undoTransactionUpdate = <ƒ extends ƒn>(
|
|
114
|
-
update: TransactionUpdate<ƒ>,
|
|
115
|
-
store: Store
|
|
116
|
-
): void => {
|
|
117
|
-
store.config.logger?.info(` ⏮ undo transaction "${update.key}" (undo)`)
|
|
118
|
-
for (const { key, oldValue } of update.atomUpdates) {
|
|
119
|
-
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
120
|
-
const state = withdraw(token, store)
|
|
121
|
-
if (state === null) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
`State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
|
|
124
|
-
)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
setState(state, oldValue, store)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
export const redoTransactionUpdate = <ƒ extends ƒn>(
|
|
131
|
-
update: TransactionUpdate<ƒ>,
|
|
132
|
-
store: Store
|
|
133
|
-
): void => {
|
|
134
|
-
store.config.logger?.info(` ⏭ redo transaction "${update.key}" (redo)`)
|
|
135
|
-
for (const { key, newValue } of update.atomUpdates) {
|
|
136
|
-
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
137
|
-
const state = withdraw(token, store)
|
|
138
|
-
if (state === null) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
setState(state, newValue, store)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export const abortTransaction = (store: Store): void => {
|
|
148
|
-
if (store.transactionStatus.phase === `idle`) {
|
|
149
|
-
store.config.logger?.warn(
|
|
150
|
-
`abortTransaction called outside of a transaction. This is probably a bug.`
|
|
151
|
-
)
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
store.transactionStatus = { phase: `idle` }
|
|
155
|
-
store.config.logger?.info(`🪂`, `transaction fail`)
|
|
19
|
+
type: `transaction`
|
|
20
|
+
install: (store: Store) => void
|
|
21
|
+
subject: Rx.Subject<TransactionUpdate<ƒ>>
|
|
22
|
+
run: (...parameters: Parameters<ƒ>) => ReturnType<ƒ>
|
|
156
23
|
}
|
|
157
24
|
|
|
158
25
|
export function transaction__INTERNAL<ƒ extends ƒn>(
|
|
@@ -180,6 +47,7 @@ export function transaction__INTERNAL<ƒ extends ƒn>(
|
|
|
180
47
|
throw thrown
|
|
181
48
|
}
|
|
182
49
|
},
|
|
50
|
+
install: (store) => transaction__INTERNAL(options, store),
|
|
183
51
|
subject: new Rx.Subject(),
|
|
184
52
|
}
|
|
185
53
|
const core = target(store)
|
package/src/react/index.ts
CHANGED
|
@@ -1,46 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { subscribe, setState, __INTERNAL__, getState } from "atom.io"
|
|
4
|
-
import type { ReadonlySelectorToken, StateToken } from "atom.io"
|
|
5
|
-
|
|
6
|
-
import type { Modifier } from "~/packages/anvl/src/function"
|
|
7
|
-
|
|
8
|
-
export type StoreHooks = {
|
|
9
|
-
useI: <T>(token: StateToken<T>) => (next: Modifier<T> | T) => void
|
|
10
|
-
useO: <T>(token: ReadonlySelectorToken<T> | StateToken<T>) => T
|
|
11
|
-
useIO: <T>(token: StateToken<T>) => [T, (next: Modifier<T> | T) => void]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const composeStoreHooks = (
|
|
15
|
-
store: __INTERNAL__.Store = __INTERNAL__.IMPLICIT.STORE
|
|
16
|
-
): StoreHooks => {
|
|
17
|
-
function useI<T>(token: StateToken<T>): (next: Modifier<T> | T) => void {
|
|
18
|
-
const updateState = (next: Modifier<T> | T) => setState(token, next, store)
|
|
19
|
-
return updateState
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function useO<T>(token: ReadonlySelectorToken<T> | StateToken<T>): T {
|
|
23
|
-
return useSyncExternalStore<T>(
|
|
24
|
-
(observe) => subscribe(token, observe, store),
|
|
25
|
-
() => getState(token, store)
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function useIO<T>(token: StateToken<T>): [T, (next: Modifier<T> | T) => void] {
|
|
30
|
-
return [useO(token), useI(token)]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return { useI, useO, useIO }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const { useI, useO, useIO } = composeStoreHooks()
|
|
37
|
-
|
|
38
|
-
export function useStore<T>(
|
|
39
|
-
token: StateToken<T>
|
|
40
|
-
): [T, (next: Modifier<T> | T) => void]
|
|
41
|
-
export function useStore<T>(token: ReadonlySelectorToken<T>): T
|
|
42
|
-
export function useStore<T>(
|
|
43
|
-
token: ReadonlySelectorToken<T> | StateToken<T>
|
|
44
|
-
): T | [T, (next: Modifier<T> | T) => void] {
|
|
45
|
-
return token.type === `readonly_selector` ? useO(token) : useIO(token)
|
|
46
|
-
}
|
|
1
|
+
export * from "./store-context"
|
|
2
|
+
export * from "./store-hooks"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import * as AtomIO from "atom.io"
|
|
4
|
+
|
|
5
|
+
export const StoreContext = React.createContext<AtomIO.Store>(
|
|
6
|
+
AtomIO.__INTERNAL__.IMPLICIT.STORE
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
export const StoreProvider: React.FC<{
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
store?: AtomIO.Store
|
|
12
|
+
}> = ({ children, store = AtomIO.__INTERNAL__.IMPLICIT.STORE }) => (
|
|
13
|
+
<StoreContext.Provider value={store}>{children}</StoreContext.Provider>
|
|
14
|
+
)
|