atom.io 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -11
- package/dist/index.d.ts +505 -4
- package/dist/index.js +823 -360
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +813 -356
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -5
- package/{dist/react → react/dist}/index.d.ts +2 -5
- package/react/dist/index.js +68 -0
- package/react/dist/index.js.map +1 -0
- package/react/dist/index.mjs +44 -0
- package/react/dist/index.mjs.map +1 -0
- package/react/package.json +12 -3
- package/src/atom.ts +18 -53
- package/src/index.ts +23 -37
- package/src/internal/atom-internal.ts +50 -0
- package/src/internal/families-internal.ts +142 -0
- package/src/internal/get.ts +40 -42
- package/src/internal/index.ts +6 -17
- package/src/internal/is-default.ts +20 -4
- package/src/internal/logger.ts +46 -0
- package/src/internal/operation.ts +97 -14
- package/src/internal/selector-internal.ts +113 -13
- package/src/internal/set.ts +31 -17
- package/src/internal/store.ts +58 -45
- package/src/internal/subscribe-internal.ts +55 -11
- package/src/internal/timeline-internal.ts +196 -0
- package/src/internal/transaction-internal.ts +157 -16
- package/src/react/index.ts +5 -6
- package/src/selector.ts +29 -99
- package/src/subscribe.ts +55 -0
- package/src/timeline.ts +34 -0
- package/src/transaction.ts +22 -34
- package/dist/index-9d9f5a05.d.ts +0 -293
- package/dist/react/index.js +0 -909
- package/dist/react/index.js.map +0 -1
- package/dist/react/index.mjs +0 -880
- package/dist/react/index.mjs.map +0 -1
|
@@ -1,14 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getState__INTERNAL,
|
|
3
|
+
withdraw,
|
|
4
|
+
recallState,
|
|
5
|
+
traceAllSelectorAtoms,
|
|
6
|
+
} from "."
|
|
7
|
+
import type { Atom, ReadonlySelector, Selector, Store } from "."
|
|
8
|
+
import type { StateUpdate } from ".."
|
|
8
9
|
|
|
9
|
-
export const
|
|
10
|
+
export const prepareUpdate = <T>(
|
|
10
11
|
state: Atom<T> | ReadonlySelector<T> | Selector<T>,
|
|
11
|
-
store: Store
|
|
12
|
+
store: Store
|
|
13
|
+
): StateUpdate<T> => {
|
|
14
|
+
const oldValue = recallState(state, store)
|
|
15
|
+
const newValue = getState__INTERNAL(state, store)
|
|
16
|
+
return { newValue, oldValue }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const stowUpdate = <T>(
|
|
20
|
+
state: Atom<T>,
|
|
21
|
+
update: StateUpdate<T>,
|
|
22
|
+
store: Store
|
|
23
|
+
): void => {
|
|
24
|
+
const { key } = state
|
|
25
|
+
const { logger } = store.config
|
|
26
|
+
if (store.transactionStatus.phase !== `building`) {
|
|
27
|
+
store.config.logger?.warn(
|
|
28
|
+
`stowUpdate called outside of a transaction. This is probably a bug.`
|
|
29
|
+
)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
store.transactionStatus.atomUpdates.push({ key, ...update })
|
|
33
|
+
logger?.info(`📝 ${key} stowed (`, update.oldValue, `->`, update.newValue, `)`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const emitUpdate = <T>(
|
|
37
|
+
state: Atom<T> | ReadonlySelector<T> | Selector<T>,
|
|
38
|
+
update: StateUpdate<T>,
|
|
39
|
+
store: Store
|
|
40
|
+
): void => {
|
|
41
|
+
const { key } = state
|
|
42
|
+
const { logger } = store.config
|
|
43
|
+
logger?.info(
|
|
44
|
+
`📢 ${state.type} "${key}" went (`,
|
|
45
|
+
update.oldValue,
|
|
46
|
+
`->`,
|
|
47
|
+
update.newValue,
|
|
48
|
+
`)`
|
|
49
|
+
)
|
|
50
|
+
state.subject.next(update)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const subscribeToRootAtoms = <T>(
|
|
54
|
+
state: ReadonlySelector<T> | Selector<T>,
|
|
55
|
+
store: Store
|
|
12
56
|
): { unsubscribe: () => void }[] | null => {
|
|
13
57
|
const dependencySubscriptions =
|
|
14
58
|
`default` in state
|
|
@@ -17,11 +61,11 @@ export const subscribeToRootAtoms = <T>(
|
|
|
17
61
|
const atom = withdraw(atomToken, store)
|
|
18
62
|
return atom.subject.subscribe((atomChange) => {
|
|
19
63
|
store.config.logger?.info(
|
|
20
|
-
`📢
|
|
64
|
+
`📢 selector "${state.key}" saw root "${atomToken.key}" go (`,
|
|
21
65
|
atomChange.oldValue,
|
|
22
66
|
`->`,
|
|
23
67
|
atomChange.newValue,
|
|
24
|
-
`)
|
|
68
|
+
`)`
|
|
25
69
|
)
|
|
26
70
|
const oldValue = recallState(state, store)
|
|
27
71
|
const newValue = getState__INTERNAL(state, store)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import HAMT from "hamt_plus"
|
|
2
|
+
|
|
3
|
+
import type { KeyedStateUpdate, TransactionUpdate, Store } from "."
|
|
4
|
+
import { IMPLICIT, withdraw } from "."
|
|
5
|
+
import { setState } from ".."
|
|
6
|
+
import type { AtomToken, TimelineOptions, TimelineToken, ƒn } from ".."
|
|
7
|
+
|
|
8
|
+
export type Timeline = {
|
|
9
|
+
key: string
|
|
10
|
+
type: `timeline`
|
|
11
|
+
next: () => void
|
|
12
|
+
prev: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type TimelineStateUpdate = KeyedStateUpdate<unknown> & {
|
|
16
|
+
type: `state_update`
|
|
17
|
+
}
|
|
18
|
+
export type TimelineTransactionUpdate = TransactionUpdate<ƒn> & {
|
|
19
|
+
type: `transaction_update`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type TimelineData = {
|
|
23
|
+
at: number
|
|
24
|
+
timeTraveling: boolean
|
|
25
|
+
history: (TimelineStateUpdate | TimelineTransactionUpdate)[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function timeline__INTERNAL(
|
|
29
|
+
options: TimelineOptions,
|
|
30
|
+
store: Store = IMPLICIT.STORE
|
|
31
|
+
): TimelineToken {
|
|
32
|
+
let incompleteTransactionKey: string | null = null
|
|
33
|
+
const timelineData: TimelineData = {
|
|
34
|
+
at: 0,
|
|
35
|
+
timeTraveling: false,
|
|
36
|
+
history: [],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const subscribeToAtom = (token: AtomToken<any>) => {
|
|
40
|
+
const state = withdraw(token, store)
|
|
41
|
+
state.subject.subscribe((update) => {
|
|
42
|
+
const storeCurrentTransactionKey =
|
|
43
|
+
store.transactionStatus.phase === `applying`
|
|
44
|
+
? store.transactionStatus.key
|
|
45
|
+
: null
|
|
46
|
+
store.config.logger?.info(
|
|
47
|
+
`⏳ timeline "${options.key}" saw atom "${token.key}" go (`,
|
|
48
|
+
update.oldValue,
|
|
49
|
+
`->`,
|
|
50
|
+
update.newValue,
|
|
51
|
+
storeCurrentTransactionKey
|
|
52
|
+
? `) in "${storeCurrentTransactionKey}"`
|
|
53
|
+
: `) independently`
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
storeCurrentTransactionKey &&
|
|
58
|
+
store.transactionStatus.phase === `applying`
|
|
59
|
+
) {
|
|
60
|
+
const currentTransaction = withdraw(
|
|
61
|
+
{ key: storeCurrentTransactionKey, type: `transaction` },
|
|
62
|
+
store
|
|
63
|
+
)
|
|
64
|
+
if (incompleteTransactionKey !== storeCurrentTransactionKey) {
|
|
65
|
+
if (incompleteTransactionKey) {
|
|
66
|
+
store.config.logger?.error(
|
|
67
|
+
`Timeline "${options.key}" was unable to resolve transaction "${incompleteTransactionKey}. This is probably a bug.`
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
incompleteTransactionKey = storeCurrentTransactionKey
|
|
71
|
+
const subscription = currentTransaction.subject.subscribe((update) => {
|
|
72
|
+
if (timelineData.timeTraveling === false) {
|
|
73
|
+
timelineData.history.push({
|
|
74
|
+
type: `transaction_update`,
|
|
75
|
+
...update,
|
|
76
|
+
atomUpdates: update.atomUpdates.filter((atomUpdate) =>
|
|
77
|
+
options.atoms.some((atom) => atom.key === atomUpdate.key)
|
|
78
|
+
),
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
timelineData.at = timelineData.history.length
|
|
82
|
+
subscription.unsubscribe()
|
|
83
|
+
incompleteTransactionKey = null
|
|
84
|
+
store.config.logger?.info(
|
|
85
|
+
`⌛ timeline "${options.key}" pushed a transaction_update from "${update.key}"`
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
if (timelineData.timeTraveling === false) {
|
|
91
|
+
timelineData.history.push({
|
|
92
|
+
type: `state_update`,
|
|
93
|
+
key: token.key,
|
|
94
|
+
oldValue: update.oldValue,
|
|
95
|
+
newValue: update.newValue,
|
|
96
|
+
})
|
|
97
|
+
store.config.logger?.info(
|
|
98
|
+
`⌛ timeline "${options.key}" pushed a state_update to "${token.key}"`
|
|
99
|
+
)
|
|
100
|
+
timelineData.at = timelineData.history.length
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const tokenOrFamily of options.atoms) {
|
|
107
|
+
if (tokenOrFamily.type === `atom_family`) {
|
|
108
|
+
const family = tokenOrFamily
|
|
109
|
+
family.subject.subscribe((token) => subscribeToAtom(token))
|
|
110
|
+
} else {
|
|
111
|
+
const token = tokenOrFamily
|
|
112
|
+
subscribeToAtom(token)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
store.timelineStore = HAMT.set(options.key, timelineData, store.timelineStore)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
key: options.key,
|
|
120
|
+
type: `timeline`,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const redo__INTERNAL = (
|
|
125
|
+
token: TimelineToken,
|
|
126
|
+
store: Store = IMPLICIT.STORE
|
|
127
|
+
): void => {
|
|
128
|
+
const timelineData = store.timelineStore.get(token.key)
|
|
129
|
+
if (!timelineData) {
|
|
130
|
+
store.config.logger?.error(
|
|
131
|
+
`Tried to redo on timeline "${token.key}" has not been initialized.`
|
|
132
|
+
)
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
if (timelineData.at === timelineData.history.length) {
|
|
136
|
+
store.config.logger?.warn(
|
|
137
|
+
`Tried to redo on timeline "${token.key}" but there is nothing to redo.`
|
|
138
|
+
)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
timelineData.timeTraveling = true
|
|
142
|
+
const update = timelineData.history[timelineData.at]
|
|
143
|
+
switch (update.type) {
|
|
144
|
+
case `state_update`: {
|
|
145
|
+
const { key, newValue } = update
|
|
146
|
+
setState({ key, type: `atom` }, newValue)
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
case `transaction_update`: {
|
|
150
|
+
for (const atomUpdate of update.atomUpdates) {
|
|
151
|
+
const { key, newValue } = atomUpdate
|
|
152
|
+
setState({ key, type: `atom` }, newValue)
|
|
153
|
+
}
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
++timelineData.at
|
|
158
|
+
timelineData.timeTraveling = false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const undo__INTERNAL = (
|
|
162
|
+
token: TimelineToken,
|
|
163
|
+
store: Store = IMPLICIT.STORE
|
|
164
|
+
): void => {
|
|
165
|
+
const timelineData = store.timelineStore.get(token.key)
|
|
166
|
+
if (!timelineData) {
|
|
167
|
+
store.config.logger?.error(
|
|
168
|
+
`Tried to undo on timeline "${token.key}" has not been initialized.`
|
|
169
|
+
)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
if (timelineData.at === 0) {
|
|
173
|
+
store.config.logger?.warn(
|
|
174
|
+
`Tried to undo on timeline "${token.key}" but there is nothing to undo.`
|
|
175
|
+
)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
timelineData.timeTraveling = true
|
|
179
|
+
--timelineData.at
|
|
180
|
+
const update = timelineData.history[timelineData.at]
|
|
181
|
+
switch (update.type) {
|
|
182
|
+
case `state_update`: {
|
|
183
|
+
const { key, oldValue } = update
|
|
184
|
+
setState({ key, type: `atom` }, oldValue)
|
|
185
|
+
break
|
|
186
|
+
}
|
|
187
|
+
case `transaction_update`: {
|
|
188
|
+
for (const atomUpdate of update.atomUpdates) {
|
|
189
|
+
const { key, oldValue } = atomUpdate
|
|
190
|
+
setState({ key, type: `atom` }, oldValue)
|
|
191
|
+
}
|
|
192
|
+
break
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
timelineData.timeTraveling = false
|
|
196
|
+
}
|
|
@@ -1,34 +1,175 @@
|
|
|
1
|
-
import
|
|
1
|
+
import HAMT from "hamt_plus"
|
|
2
|
+
import * as Rx from "rxjs"
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import type { Store, StoreCore } from "."
|
|
5
|
+
import { deposit, withdraw, IMPLICIT } from "."
|
|
6
|
+
import { getState, setState } from ".."
|
|
7
|
+
import type {
|
|
8
|
+
AtomToken,
|
|
9
|
+
StateUpdate,
|
|
10
|
+
Transaction,
|
|
11
|
+
TransactionOptions,
|
|
12
|
+
TransactionToken,
|
|
13
|
+
ƒn,
|
|
14
|
+
} from ".."
|
|
15
|
+
|
|
16
|
+
export const TRANSACTION_PHASES = [`idle`, `building`, `applying`] as const
|
|
17
|
+
export type TransactionPhase = (typeof TRANSACTION_PHASES)[number]
|
|
18
|
+
|
|
19
|
+
export type KeyedStateUpdate<T> = StateUpdate<T> & {
|
|
20
|
+
key: string
|
|
21
|
+
}
|
|
22
|
+
export type TransactionUpdate<ƒ extends ƒn> = {
|
|
23
|
+
key: string
|
|
24
|
+
atomUpdates: KeyedStateUpdate<unknown>[]
|
|
25
|
+
params: Parameters<ƒ>
|
|
26
|
+
output: ReturnType<ƒ>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type TransactionUpdateInProgress<ƒ extends ƒn> = TransactionUpdate<ƒ> & {
|
|
30
|
+
phase: `applying` | `building`
|
|
31
|
+
core: StoreCore
|
|
32
|
+
}
|
|
33
|
+
export type TransactionIdle = {
|
|
34
|
+
phase: `idle`
|
|
6
35
|
}
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
36
|
+
export type TransactionStatus<ƒ extends ƒn> =
|
|
37
|
+
| TransactionIdle
|
|
38
|
+
| TransactionUpdateInProgress<ƒ>
|
|
39
|
+
|
|
40
|
+
export const buildTransaction = (
|
|
41
|
+
key: string,
|
|
42
|
+
params: any[],
|
|
43
|
+
store: Store
|
|
44
|
+
): void => {
|
|
45
|
+
store.transactionStatus = {
|
|
46
|
+
key,
|
|
47
|
+
phase: `building`,
|
|
48
|
+
core: {
|
|
11
49
|
atoms: store.atoms,
|
|
50
|
+
atomsThatAreDefault: store.atomsThatAreDefault,
|
|
51
|
+
operation: { open: false },
|
|
12
52
|
readonlySelectors: store.readonlySelectors,
|
|
53
|
+
timelines: store.timelines,
|
|
54
|
+
timelineAtoms: store.timelineAtoms,
|
|
55
|
+
transactions: store.transactions,
|
|
56
|
+
selectorAtoms: store.selectorAtoms,
|
|
13
57
|
selectorGraph: store.selectorGraph,
|
|
14
58
|
selectors: store.selectors,
|
|
15
59
|
valueMap: store.valueMap,
|
|
16
60
|
},
|
|
61
|
+
atomUpdates: [],
|
|
62
|
+
params,
|
|
63
|
+
output: undefined,
|
|
64
|
+
}
|
|
65
|
+
store.config.logger?.info(`🛫`, `transaction "${key}" started`)
|
|
66
|
+
}
|
|
67
|
+
export const applyTransaction = <ƒ extends ƒn>(
|
|
68
|
+
output: ReturnType<ƒ>,
|
|
69
|
+
store: Store
|
|
70
|
+
): void => {
|
|
71
|
+
if (store.transactionStatus.phase !== `building`) {
|
|
72
|
+
store.config.logger?.warn(
|
|
73
|
+
`abortTransaction called outside of a transaction. This is probably a bug.`
|
|
74
|
+
)
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
store.config.logger?.info(
|
|
78
|
+
` ▶️ apply transaction "${store.transactionStatus.key}" (init)`
|
|
79
|
+
)
|
|
80
|
+
store.transactionStatus.phase = `applying`
|
|
81
|
+
store.transactionStatus.output = output
|
|
82
|
+
const { atomUpdates } = store.transactionStatus
|
|
83
|
+
for (const { key, oldValue, newValue } of atomUpdates) {
|
|
84
|
+
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
85
|
+
const state = withdraw(token, store)
|
|
86
|
+
setState(state, newValue, store)
|
|
87
|
+
}
|
|
88
|
+
const myTransaction = withdraw<ƒ>(
|
|
89
|
+
{ key: store.transactionStatus.key, type: `transaction` },
|
|
90
|
+
store
|
|
91
|
+
)
|
|
92
|
+
myTransaction.subject.next({
|
|
93
|
+
key: store.transactionStatus.key,
|
|
94
|
+
atomUpdates,
|
|
95
|
+
output,
|
|
96
|
+
params: store.transactionStatus.params as Parameters<ƒ>,
|
|
97
|
+
})
|
|
98
|
+
store.transactionStatus = { phase: `idle` }
|
|
99
|
+
store.config.logger?.info(`🛬`, `transaction done`)
|
|
100
|
+
}
|
|
101
|
+
export const undoTransactionUpdate = <ƒ extends ƒn>(
|
|
102
|
+
update: TransactionUpdate<ƒ>,
|
|
103
|
+
store: Store
|
|
104
|
+
): void => {
|
|
105
|
+
store.config.logger?.info(` ⏮ undo transaction "${update.key}" (undo)`)
|
|
106
|
+
for (const { key, oldValue, newValue } of update.atomUpdates) {
|
|
107
|
+
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
108
|
+
const state = withdraw(token, store)
|
|
109
|
+
setState(state, oldValue, store)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export const redoTransactionUpdate = <ƒ extends ƒn>(
|
|
113
|
+
update: TransactionUpdate<ƒ>,
|
|
114
|
+
store: Store
|
|
115
|
+
): void => {
|
|
116
|
+
store.config.logger?.info(` ⏭ redo transaction "${update.key}" (redo)`)
|
|
117
|
+
for (const { key, oldValue, newValue } of update.atomUpdates) {
|
|
118
|
+
const token: AtomToken<unknown> = { key, type: `atom` }
|
|
119
|
+
const state = withdraw(token, store)
|
|
120
|
+
setState(state, newValue, store)
|
|
17
121
|
}
|
|
18
|
-
store.config.logger?.info(`🛫`, `transaction start`)
|
|
19
122
|
}
|
|
123
|
+
|
|
20
124
|
export const abortTransaction = (store: Store): void => {
|
|
21
|
-
if (
|
|
125
|
+
if (store.transactionStatus.phase === `idle`) {
|
|
22
126
|
store.config.logger?.warn(
|
|
23
127
|
`abortTransaction called outside of a transaction. This is probably a bug.`
|
|
24
128
|
)
|
|
25
129
|
return
|
|
26
130
|
}
|
|
27
|
-
store.
|
|
28
|
-
store.readonlySelectors = store.transaction.prev.readonlySelectors
|
|
29
|
-
store.selectorGraph = store.transaction.prev.selectorGraph
|
|
30
|
-
store.selectors = store.transaction.prev.selectors
|
|
31
|
-
store.valueMap = store.transaction.prev.valueMap
|
|
32
|
-
store.transaction = { open: false }
|
|
131
|
+
store.transactionStatus = { phase: `idle` }
|
|
33
132
|
store.config.logger?.info(`🪂`, `transaction fail`)
|
|
34
133
|
}
|
|
134
|
+
|
|
135
|
+
export function transaction__INTERNAL<ƒ extends ƒn>(
|
|
136
|
+
options: TransactionOptions<ƒ>,
|
|
137
|
+
store: Store = IMPLICIT.STORE
|
|
138
|
+
): TransactionToken<ƒ> {
|
|
139
|
+
const newTransaction: Transaction<ƒ> = {
|
|
140
|
+
key: options.key,
|
|
141
|
+
type: `transaction`,
|
|
142
|
+
run: (...params: Parameters<ƒ>) => {
|
|
143
|
+
buildTransaction(options.key, params, store)
|
|
144
|
+
try {
|
|
145
|
+
const output = options.do(
|
|
146
|
+
{
|
|
147
|
+
get: (token) => getState(token, store),
|
|
148
|
+
set: (token, value) => setState(token, value, store),
|
|
149
|
+
},
|
|
150
|
+
...params
|
|
151
|
+
)
|
|
152
|
+
applyTransaction(output, store)
|
|
153
|
+
return output
|
|
154
|
+
} catch (thrown) {
|
|
155
|
+
abortTransaction(store)
|
|
156
|
+
store.config.logger?.error(`Transaction ${options.key} failed`, thrown)
|
|
157
|
+
throw thrown
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
subject: new Rx.Subject(),
|
|
161
|
+
}
|
|
162
|
+
const core = target(store)
|
|
163
|
+
core.transactions = HAMT.set(
|
|
164
|
+
newTransaction.key,
|
|
165
|
+
newTransaction,
|
|
166
|
+
core.transactions
|
|
167
|
+
)
|
|
168
|
+
const token = deposit(newTransaction)
|
|
169
|
+
return token
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const target = (store: Store = IMPLICIT.STORE): StoreCore =>
|
|
173
|
+
store.transactionStatus.phase === `building`
|
|
174
|
+
? store.transactionStatus.core
|
|
175
|
+
: store
|
package/src/react/index.ts
CHANGED
|
@@ -2,11 +2,10 @@ import type Preact from "preact/hooks"
|
|
|
2
2
|
|
|
3
3
|
import type React from "react"
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { subscribe, setState, __INTERNAL__ } from "atom.io"
|
|
6
|
+
import type { ReadonlyValueToken, StateToken } from "atom.io"
|
|
6
7
|
|
|
7
|
-
import type {
|
|
8
|
-
import { subscribe, setState, __INTERNAL__ } from ".."
|
|
9
|
-
import { withdraw } from "../internal"
|
|
8
|
+
import type { Modifier } from "~/packages/anvl/src/function"
|
|
10
9
|
|
|
11
10
|
export type AtomStoreReactConfig = {
|
|
12
11
|
useState: typeof Preact.useState | typeof React.useState
|
|
@@ -26,7 +25,7 @@ export const composeStoreHooks = ({
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
function useO<T>(token: ReadonlyValueToken<T> | StateToken<T>): T {
|
|
29
|
-
const state = withdraw(token, store)
|
|
28
|
+
const state = __INTERNAL__.withdraw(token, store)
|
|
30
29
|
const initialValue = __INTERNAL__.getState__INTERNAL(state, store)
|
|
31
30
|
const [current, dispatch] = useState(initialValue)
|
|
32
31
|
useEffect(() => {
|
|
@@ -40,7 +39,7 @@ export const composeStoreHooks = ({
|
|
|
40
39
|
store
|
|
41
40
|
)
|
|
42
41
|
return unsubscribe
|
|
43
|
-
}, [
|
|
42
|
+
}, [])
|
|
44
43
|
|
|
45
44
|
return current
|
|
46
45
|
}
|
package/src/selector.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as Rx from "rxjs"
|
|
1
|
+
import type * as Rx from "rxjs"
|
|
3
2
|
|
|
4
|
-
import { become } from "~/packages/anvl/src/function"
|
|
5
3
|
import type { Serializable } from "~/packages/anvl/src/json"
|
|
6
|
-
import { stringifyJson } from "~/packages/anvl/src/json"
|
|
7
4
|
|
|
8
5
|
import type { ReadonlyValueToken, SelectorToken } from "."
|
|
9
|
-
import
|
|
10
|
-
import { IMPLICIT, markDone, deposit, registerSelector } from "./internal"
|
|
6
|
+
import { selectorFamily__INTERNAL, selector__INTERNAL } from "./internal"
|
|
11
7
|
import type { ReadonlyTransactors, Transactors } from "./transaction"
|
|
12
8
|
|
|
13
9
|
export type SelectorOptions<T> = {
|
|
@@ -17,67 +13,14 @@ export type SelectorOptions<T> = {
|
|
|
17
13
|
}
|
|
18
14
|
export type ReadonlySelectorOptions<T> = Omit<SelectorOptions<T>, `set`>
|
|
19
15
|
|
|
16
|
+
export function selector<T>(options: SelectorOptions<T>): SelectorToken<T>
|
|
20
17
|
export function selector<T>(
|
|
21
|
-
options:
|
|
22
|
-
store?: Store
|
|
23
|
-
): SelectorToken<T>
|
|
24
|
-
export function selector<T>(
|
|
25
|
-
options: ReadonlySelectorOptions<T>,
|
|
26
|
-
store?: Store
|
|
18
|
+
options: ReadonlySelectorOptions<T>
|
|
27
19
|
): ReadonlyValueToken<T>
|
|
28
20
|
export function selector<T>(
|
|
29
|
-
options: ReadonlySelectorOptions<T> | SelectorOptions<T
|
|
30
|
-
store: Store = IMPLICIT.STORE
|
|
21
|
+
options: ReadonlySelectorOptions<T> | SelectorOptions<T>
|
|
31
22
|
): ReadonlyValueToken<T> | SelectorToken<T> {
|
|
32
|
-
|
|
33
|
-
throw new Error(`Key "${options.key}" already exists in the store.`)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
|
|
37
|
-
|
|
38
|
-
const { get, set } = registerSelector(options.key, store)
|
|
39
|
-
const getSelf = () => {
|
|
40
|
-
const value = options.get({ get })
|
|
41
|
-
store.valueMap = HAMT.set(options.key, value, store.valueMap)
|
|
42
|
-
return value
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!(`set` in options)) {
|
|
46
|
-
const readonlySelector = {
|
|
47
|
-
...options,
|
|
48
|
-
subject,
|
|
49
|
-
get: getSelf,
|
|
50
|
-
}
|
|
51
|
-
store.readonlySelectors = HAMT.set(
|
|
52
|
-
options.key,
|
|
53
|
-
readonlySelector,
|
|
54
|
-
store.readonlySelectors
|
|
55
|
-
)
|
|
56
|
-
const initialValue = getSelf()
|
|
57
|
-
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
58
|
-
return { ...readonlySelector, type: `readonly_selector` }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
62
|
-
store.config.logger?.info(` <- "${options.key}" became`, next)
|
|
63
|
-
const oldValue = getSelf()
|
|
64
|
-
const newValue = become(next)(oldValue)
|
|
65
|
-
store.valueMap = HAMT.set(options.key, newValue, store.valueMap)
|
|
66
|
-
markDone(options.key, store)
|
|
67
|
-
subject.next({ newValue, oldValue })
|
|
68
|
-
options.set({ get, set }, newValue)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const mySelector: Selector<T> = {
|
|
72
|
-
...options,
|
|
73
|
-
subject,
|
|
74
|
-
get: getSelf,
|
|
75
|
-
set: setSelf,
|
|
76
|
-
}
|
|
77
|
-
store.selectors = HAMT.set(options.key, mySelector, store.selectors)
|
|
78
|
-
const initialValue = getSelf()
|
|
79
|
-
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
80
|
-
return { ...mySelector, type: `selector` }
|
|
23
|
+
return selector__INTERNAL(options)
|
|
81
24
|
}
|
|
82
25
|
|
|
83
26
|
export type SelectorFamilyOptions<T, K extends Serializable> = {
|
|
@@ -90,43 +33,30 @@ export type ReadonlySelectorFamilyOptions<T, K extends Serializable> = Omit<
|
|
|
90
33
|
`set`
|
|
91
34
|
>
|
|
92
35
|
|
|
36
|
+
export type SelectorFamily<T, K extends Serializable = Serializable> = ((
|
|
37
|
+
key: K
|
|
38
|
+
) => SelectorToken<T>) & {
|
|
39
|
+
key: string
|
|
40
|
+
type: `selector_family`
|
|
41
|
+
subject: Rx.Subject<SelectorToken<T>>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type ReadonlySelectorFamily<T, K extends Serializable = Serializable> = ((
|
|
45
|
+
key: K
|
|
46
|
+
) => ReadonlyValueToken<T>) & {
|
|
47
|
+
key: string
|
|
48
|
+
type: `readonly_selector_family`
|
|
49
|
+
subject: Rx.Subject<ReadonlyValueToken<T>>
|
|
50
|
+
}
|
|
51
|
+
|
|
93
52
|
export function selectorFamily<T, K extends Serializable>(
|
|
94
|
-
options: SelectorFamilyOptions<T, K
|
|
95
|
-
|
|
96
|
-
): (key: K) => SelectorToken<T>
|
|
53
|
+
options: SelectorFamilyOptions<T, K>
|
|
54
|
+
): SelectorFamily<T, K>
|
|
97
55
|
export function selectorFamily<T, K extends Serializable>(
|
|
98
|
-
options: ReadonlySelectorFamilyOptions<T, K
|
|
99
|
-
|
|
100
|
-
): (key: K) => ReadonlyValueToken<T>
|
|
56
|
+
options: ReadonlySelectorFamilyOptions<T, K>
|
|
57
|
+
): ReadonlySelectorFamily<T, K>
|
|
101
58
|
export function selectorFamily<T, K extends Serializable>(
|
|
102
|
-
options: ReadonlySelectorFamilyOptions<T, K> | SelectorFamilyOptions<T, K
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return (key: K): ReadonlyValueToken<T> | SelectorToken<T> => {
|
|
106
|
-
const fullKey = `${options.key}__${stringifyJson(key)}`
|
|
107
|
-
const existing =
|
|
108
|
-
store.selectors.get(fullKey) ?? store.readonlySelectors.get(fullKey)
|
|
109
|
-
if (existing) {
|
|
110
|
-
return deposit(existing)
|
|
111
|
-
}
|
|
112
|
-
const readonlySelectorOptions: ReadonlySelectorOptions<T> = {
|
|
113
|
-
key: fullKey,
|
|
114
|
-
get: options.get(key),
|
|
115
|
-
}
|
|
116
|
-
if (!(`set` in options)) {
|
|
117
|
-
return selector<T>(
|
|
118
|
-
{
|
|
119
|
-
...readonlySelectorOptions,
|
|
120
|
-
},
|
|
121
|
-
store
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
return selector<T>(
|
|
125
|
-
{
|
|
126
|
-
...readonlySelectorOptions,
|
|
127
|
-
set: options.set(key),
|
|
128
|
-
},
|
|
129
|
-
store
|
|
130
|
-
)
|
|
131
|
-
}
|
|
59
|
+
options: ReadonlySelectorFamilyOptions<T, K> | SelectorFamilyOptions<T, K>
|
|
60
|
+
): ReadonlySelectorFamily<T, K> | SelectorFamily<T, K> {
|
|
61
|
+
return selectorFamily__INTERNAL(options)
|
|
132
62
|
}
|