atom.io 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -10
- package/dist/index.d.mts +614 -0
- package/dist/index.d.ts +130 -77
- package/dist/index.js +584 -347
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +582 -347
- package/dist/index.mjs.map +1 -1
- package/json/dist/index.d.mts +18 -0
- package/json/dist/index.d.ts +18 -0
- package/json/dist/index.js +51 -0
- package/json/dist/index.js.map +1 -0
- package/json/dist/index.mjs +15 -0
- package/json/dist/index.mjs.map +1 -0
- package/json/package.json +15 -0
- package/package.json +43 -9
- package/react/dist/index.d.mts +24 -0
- package/react/dist/index.d.ts +18 -11
- package/react/dist/index.js +45 -21
- package/react/dist/index.js.map +1 -1
- package/react/dist/index.mjs +31 -21
- package/react/dist/index.mjs.map +1 -1
- package/react-devtools/dist/index.d.mts +15 -0
- package/react-devtools/dist/index.d.ts +4 -4
- package/react-devtools/dist/index.js +1 -1
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs +1 -1
- package/react-devtools/dist/index.mjs.map +1 -1
- package/realtime/dist/index.d.mts +27 -0
- package/realtime/dist/index.d.ts +27 -0
- package/realtime/dist/index.js +191 -0
- package/realtime/dist/index.js.map +1 -0
- package/realtime/dist/index.mjs +152 -0
- package/realtime/dist/index.mjs.map +1 -0
- package/realtime/package.json +15 -0
- package/realtime-react/dist/index.d.mts +45 -0
- package/realtime-react/dist/index.d.ts +45 -0
- package/realtime-react/dist/index.js +213 -0
- package/realtime-react/dist/index.js.map +1 -0
- package/realtime-react/dist/index.mjs +168 -0
- package/realtime-react/dist/index.mjs.map +1 -0
- package/realtime-react/package.json +15 -0
- package/src/index.ts +21 -5
- package/src/internal/atom-internal.ts +1 -1
- package/src/internal/families-internal.ts +3 -3
- package/src/internal/get.ts +39 -15
- package/src/internal/index.ts +2 -0
- package/src/internal/meta/meta-state.ts +1 -1
- package/src/internal/operation.ts +3 -1
- package/src/internal/selector/create-read-write-selector.ts +62 -0
- package/src/internal/selector/create-readonly-selector.ts +52 -0
- package/src/internal/selector/index.ts +4 -0
- package/src/internal/selector/lookup-selector-sources.ts +16 -0
- package/src/internal/selector/register-selector.ts +57 -0
- package/src/internal/selector/trace-selector-atoms.ts +43 -0
- package/src/internal/selector/update-selector-atoms.ts +33 -0
- package/src/internal/selector-internal.ts +9 -197
- package/src/internal/set.ts +1 -1
- package/src/internal/store.ts +44 -17
- package/src/internal/subscribe-internal.ts +6 -1
- package/src/internal/time-travel-internal.ts +7 -7
- package/src/internal/timeline/add-atom-to-timeline.ts +164 -0
- package/src/internal/timeline/index.ts +1 -0
- package/src/internal/timeline-internal.ts +39 -146
- package/src/internal/transaction/abort-transaction.ts +12 -0
- package/src/internal/transaction/apply-transaction.ts +54 -0
- package/src/internal/transaction/build-transaction.ts +33 -0
- package/src/internal/transaction/index.ts +25 -0
- package/src/internal/transaction/redo-transaction.ts +23 -0
- package/src/internal/transaction/undo-transaction.ts +23 -0
- package/src/internal/transaction-internal.ts +16 -133
- package/src/json/index.ts +1 -0
- package/src/json/select-json.ts +18 -0
- package/src/react/index.ts +2 -46
- package/src/react/store-context.tsx +14 -0
- package/src/react/store-hooks.ts +48 -0
- package/src/react-devtools/AtomIODevtools.tsx +1 -1
- package/src/react-explorer/AtomIOExplorer.tsx +2 -2
- package/src/react-explorer/explorer-states.ts +5 -5
- package/src/react-explorer/index.ts +1 -1
- package/src/react-explorer/space-states.ts +8 -9
- package/src/realtime/README.md +33 -0
- package/src/realtime/hook-composition/expose-family.ts +101 -0
- package/src/realtime/hook-composition/expose-single.ts +38 -0
- package/src/realtime/hook-composition/index.ts +12 -0
- package/src/realtime/hook-composition/receive-state.ts +29 -0
- package/src/realtime/hook-composition/receive-transaction.ts +18 -0
- package/src/realtime/index.ts +1 -0
- package/src/realtime-react/index.ts +3 -0
- package/src/realtime-react/realtime-context.tsx +31 -0
- package/src/realtime-react/realtime-hooks.ts +39 -0
- package/src/realtime-react/realtime-state.ts +10 -0
- package/src/realtime-react/use-pull-family-member.ts +27 -0
- package/src/realtime-react/use-pull-family.ts +25 -0
- package/src/realtime-react/use-pull.ts +23 -0
- package/src/realtime-react/use-push.ts +26 -0
- package/src/realtime-react/use-server-action.ts +34 -0
- package/src/selector.ts +9 -6
- package/src/silo.ts +53 -0
- package/src/subscribe.ts +42 -2
- package/src/timeline.ts +10 -0
- package/src/transaction.ts +24 -12
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type { Atom, ReadonlySelector, Selector, Store } from "."
|
|
1
2
|
import {
|
|
2
3
|
getState__INTERNAL,
|
|
3
4
|
withdraw,
|
|
4
5
|
recallState,
|
|
5
6
|
traceAllSelectorAtoms,
|
|
6
7
|
} from "."
|
|
7
|
-
import type { Atom, ReadonlySelector, Selector, Store } from "."
|
|
8
8
|
import type { StateUpdate } from ".."
|
|
9
9
|
|
|
10
10
|
export const prepareUpdate = <T>(
|
|
@@ -59,6 +59,11 @@ export const subscribeToRootAtoms = <T>(
|
|
|
59
59
|
? null
|
|
60
60
|
: traceAllSelectorAtoms(state.key, store).map((atomToken) => {
|
|
61
61
|
const atom = withdraw(atomToken, store)
|
|
62
|
+
if (atom === null) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Atom "${atomToken.key}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`
|
|
65
|
+
)
|
|
66
|
+
}
|
|
62
67
|
return atom.subject.subscribe((atomChange) => {
|
|
63
68
|
store.config.logger?.info(
|
|
64
69
|
`📢 selector "${state.key}" saw root "${atomToken.key}" go (`,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { Store } from "."
|
|
2
2
|
import { IMPLICIT } from "."
|
|
3
|
-
import { setState } from ".."
|
|
4
3
|
import type { TimelineToken } from ".."
|
|
4
|
+
import { setState } from ".."
|
|
5
5
|
|
|
6
6
|
export const redo__INTERNAL = (
|
|
7
7
|
token: TimelineToken,
|
|
8
8
|
store: Store = IMPLICIT.STORE
|
|
9
9
|
): void => {
|
|
10
10
|
store.config.logger?.info(`⏩ redo "${token.key}"`)
|
|
11
|
-
const timelineData = store.
|
|
11
|
+
const timelineData = store.timelines.get(token.key)
|
|
12
12
|
if (!timelineData) {
|
|
13
13
|
store.config.logger?.error(
|
|
14
14
|
`Failed to redo on timeline "${token.key}". This timeline has not been initialized.`
|
|
@@ -26,14 +26,14 @@ export const redo__INTERNAL = (
|
|
|
26
26
|
switch (update.type) {
|
|
27
27
|
case `atom_update`: {
|
|
28
28
|
const { key, newValue } = update
|
|
29
|
-
setState({ key, type: `atom` }, newValue)
|
|
29
|
+
setState({ key, type: `atom` }, newValue, store)
|
|
30
30
|
break
|
|
31
31
|
}
|
|
32
32
|
case `selector_update`:
|
|
33
33
|
case `transaction_update`: {
|
|
34
34
|
for (const atomUpdate of update.atomUpdates) {
|
|
35
35
|
const { key, newValue } = atomUpdate
|
|
36
|
-
setState({ key, type: `atom` }, newValue)
|
|
36
|
+
setState({ key, type: `atom` }, newValue, store)
|
|
37
37
|
}
|
|
38
38
|
break
|
|
39
39
|
}
|
|
@@ -50,7 +50,7 @@ export const undo__INTERNAL = (
|
|
|
50
50
|
store: Store = IMPLICIT.STORE
|
|
51
51
|
): void => {
|
|
52
52
|
store.config.logger?.info(`⏪ undo "${token.key}"`)
|
|
53
|
-
const timelineData = store.
|
|
53
|
+
const timelineData = store.timelines.get(token.key)
|
|
54
54
|
if (!timelineData) {
|
|
55
55
|
store.config.logger?.error(
|
|
56
56
|
`Failed to undo on timeline "${token.key}". This timeline has not been initialized.`
|
|
@@ -70,14 +70,14 @@ export const undo__INTERNAL = (
|
|
|
70
70
|
switch (update.type) {
|
|
71
71
|
case `atom_update`: {
|
|
72
72
|
const { key, oldValue } = update
|
|
73
|
-
setState({ key, type: `atom` }, oldValue)
|
|
73
|
+
setState({ key, type: `atom` }, oldValue, store)
|
|
74
74
|
break
|
|
75
75
|
}
|
|
76
76
|
case `selector_update`:
|
|
77
77
|
case `transaction_update`: {
|
|
78
78
|
for (const atomUpdate of update.atomUpdates) {
|
|
79
79
|
const { key, oldValue } = atomUpdate
|
|
80
|
-
setState({ key, type: `atom` }, oldValue)
|
|
80
|
+
setState({ key, type: `atom` }, oldValue, store)
|
|
81
81
|
}
|
|
82
82
|
break
|
|
83
83
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { IMPLICIT, withdraw } from ".."
|
|
2
|
+
import type {
|
|
3
|
+
TimelineSelectorUpdate,
|
|
4
|
+
Timeline,
|
|
5
|
+
Store,
|
|
6
|
+
TimelineTransactionUpdate,
|
|
7
|
+
TimelineAtomUpdate,
|
|
8
|
+
} from ".."
|
|
9
|
+
import type { AtomFamily, AtomToken, TimelineUpdate } from "../.."
|
|
10
|
+
|
|
11
|
+
export const addAtomToTimeline = (
|
|
12
|
+
atomToken: AtomToken<any>,
|
|
13
|
+
atoms: (AtomFamily<any> | 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((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 === false) {
|
|
54
|
+
if (tl.selectorTime && tl.selectorTime !== currentSelectorTime) {
|
|
55
|
+
const mostRecentUpdate: TimelineUpdate = tl.history.at(-1)!
|
|
56
|
+
if (mostRecentUpdate.type === `selector_update`) {
|
|
57
|
+
tl.subject.next(mostRecentUpdate)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (
|
|
61
|
+
currentTransactionKey &&
|
|
62
|
+
store.transactionStatus.phase === `applying`
|
|
63
|
+
) {
|
|
64
|
+
const currentTransaction = withdraw(
|
|
65
|
+
{ key: currentTransactionKey, type: `transaction` },
|
|
66
|
+
store
|
|
67
|
+
)
|
|
68
|
+
if (currentTransaction === null) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Transaction "${currentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${currentTransactionKey}".`
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
if (tl.transactionKey !== currentTransactionKey) {
|
|
74
|
+
if (tl.transactionKey) {
|
|
75
|
+
store.config.logger?.error(
|
|
76
|
+
`Timeline "${tl.key}" was unable to resolve transaction "${tl.transactionKey}. This is probably a bug.`
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
tl.transactionKey = currentTransactionKey
|
|
80
|
+
const subscription = currentTransaction.subject.subscribe((update) => {
|
|
81
|
+
if (tl.timeTraveling === false && currentTransactionTime) {
|
|
82
|
+
if (tl.at !== tl.history.length) {
|
|
83
|
+
tl.history.splice(tl.at)
|
|
84
|
+
}
|
|
85
|
+
const timelineTransactionUpdate: TimelineTransactionUpdate = {
|
|
86
|
+
type: `transaction_update`,
|
|
87
|
+
timestamp: currentTransactionTime,
|
|
88
|
+
...update,
|
|
89
|
+
atomUpdates: update.atomUpdates.filter((atomUpdate) =>
|
|
90
|
+
atoms.some((atom) => atom.key === atomUpdate.key)
|
|
91
|
+
),
|
|
92
|
+
}
|
|
93
|
+
tl.history.push(timelineTransactionUpdate)
|
|
94
|
+
tl.subject.next(timelineTransactionUpdate)
|
|
95
|
+
}
|
|
96
|
+
tl.at = tl.history.length
|
|
97
|
+
subscription.unsubscribe()
|
|
98
|
+
tl.transactionKey = null
|
|
99
|
+
store.config.logger?.info(
|
|
100
|
+
`⌛ timeline "${tl.key}" got a transaction_update "${update.key}"`
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
} else if (currentSelectorKey && currentSelectorTime) {
|
|
105
|
+
if (currentSelectorTime !== tl.selectorTime) {
|
|
106
|
+
const newSelectorUpdate: TimelineSelectorUpdate = {
|
|
107
|
+
type: `selector_update`,
|
|
108
|
+
timestamp: currentSelectorTime,
|
|
109
|
+
key: currentSelectorKey,
|
|
110
|
+
atomUpdates: [],
|
|
111
|
+
}
|
|
112
|
+
newSelectorUpdate.atomUpdates.push({
|
|
113
|
+
key: atom.key,
|
|
114
|
+
type: `atom_update`,
|
|
115
|
+
...update,
|
|
116
|
+
})
|
|
117
|
+
if (tl.at !== tl.history.length) {
|
|
118
|
+
tl.history.splice(tl.at)
|
|
119
|
+
}
|
|
120
|
+
tl.history.push(newSelectorUpdate)
|
|
121
|
+
|
|
122
|
+
store.config.logger?.info(
|
|
123
|
+
`⌛ timeline "${tl.key}" got a selector_update "${currentSelectorKey}" with`,
|
|
124
|
+
newSelectorUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
|
|
125
|
+
)
|
|
126
|
+
tl.at = tl.history.length
|
|
127
|
+
tl.selectorTime = currentSelectorTime
|
|
128
|
+
} else {
|
|
129
|
+
const latestUpdate = tl.history.at(-1)
|
|
130
|
+
if (latestUpdate?.type === `selector_update`) {
|
|
131
|
+
latestUpdate.atomUpdates.push({
|
|
132
|
+
key: atom.key,
|
|
133
|
+
type: `atom_update`,
|
|
134
|
+
...update,
|
|
135
|
+
})
|
|
136
|
+
store.config.logger?.info(
|
|
137
|
+
` ⌛ timeline "${tl.key}" set selector_update "${currentSelectorKey}" to`,
|
|
138
|
+
latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key)
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
const timestamp = Date.now()
|
|
144
|
+
tl.selectorTime = null
|
|
145
|
+
if (tl.at !== tl.history.length) {
|
|
146
|
+
tl.history.splice(tl.at)
|
|
147
|
+
}
|
|
148
|
+
const atomUpdate: TimelineAtomUpdate = {
|
|
149
|
+
type: `atom_update`,
|
|
150
|
+
timestamp,
|
|
151
|
+
key: atom.key,
|
|
152
|
+
oldValue: update.oldValue,
|
|
153
|
+
newValue: update.newValue,
|
|
154
|
+
}
|
|
155
|
+
tl.history.push(atomUpdate)
|
|
156
|
+
tl.subject.next(atomUpdate)
|
|
157
|
+
store.config.logger?.info(
|
|
158
|
+
`⌛ timeline "${tl.key}" got a state_update to "${atom.key}"`
|
|
159
|
+
)
|
|
160
|
+
tl.at = tl.history.length
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./add-atom-to-timeline"
|
|
@@ -1,175 +1,66 @@
|
|
|
1
1
|
import HAMT from "hamt_plus"
|
|
2
|
+
import * as Rx from "rxjs"
|
|
2
3
|
|
|
3
|
-
import type {
|
|
4
|
-
import { target, IMPLICIT, withdraw } from "."
|
|
5
|
-
import type { AtomToken, TimelineOptions, TimelineToken, ƒn } from ".."
|
|
4
|
+
import type { ƒn } from "~/packages/anvl/src/function"
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 ".."
|
|
13
16
|
|
|
14
|
-
export type TimelineAtomUpdate =
|
|
17
|
+
export type TimelineAtomUpdate = StateUpdate<unknown> & {
|
|
18
|
+
key: string
|
|
15
19
|
type: `atom_update`
|
|
20
|
+
timestamp: number
|
|
16
21
|
}
|
|
17
22
|
export type TimelineSelectorUpdate = {
|
|
18
23
|
key: string
|
|
19
24
|
type: `selector_update`
|
|
20
|
-
|
|
25
|
+
timestamp: number
|
|
26
|
+
atomUpdates: Omit<TimelineAtomUpdate, `timestamp`>[]
|
|
21
27
|
}
|
|
22
28
|
export type TimelineTransactionUpdate = TransactionUpdate<ƒn> & {
|
|
29
|
+
key: string
|
|
23
30
|
type: `transaction_update`
|
|
31
|
+
timestamp: number
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
export type
|
|
34
|
+
export type Timeline = {
|
|
35
|
+
key: string
|
|
27
36
|
at: number
|
|
28
37
|
timeTraveling: boolean
|
|
29
|
-
history:
|
|
30
|
-
| TimelineAtomUpdate
|
|
31
|
-
| TimelineSelectorUpdate
|
|
32
|
-
| TimelineTransactionUpdate
|
|
33
|
-
)[]
|
|
38
|
+
history: TimelineUpdate[]
|
|
34
39
|
selectorTime: number | null
|
|
35
40
|
transactionKey: string | null
|
|
41
|
+
install: (store: Store) => void
|
|
42
|
+
subject: Rx.Subject<
|
|
43
|
+
TimelineAtomUpdate | TimelineSelectorUpdate | TimelineTransactionUpdate
|
|
44
|
+
>
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
export function timeline__INTERNAL(
|
|
39
48
|
options: TimelineOptions,
|
|
40
|
-
store: Store = IMPLICIT.STORE
|
|
49
|
+
store: Store = IMPLICIT.STORE,
|
|
50
|
+
data: Timeline | null = null
|
|
41
51
|
): TimelineToken {
|
|
42
|
-
const
|
|
52
|
+
const tl: Timeline = {
|
|
53
|
+
key: options.key,
|
|
43
54
|
at: 0,
|
|
44
55
|
timeTraveling: false,
|
|
45
|
-
history: [],
|
|
46
56
|
selectorTime: null,
|
|
47
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(),
|
|
48
62
|
}
|
|
49
63
|
|
|
50
|
-
const subscribeToAtom = (token: AtomToken<any>) => {
|
|
51
|
-
const state = withdraw(token, store)
|
|
52
|
-
state.subject.subscribe((update) => {
|
|
53
|
-
const storeCurrentSelectorKey =
|
|
54
|
-
store.operation.open && store.operation.token.type === `selector`
|
|
55
|
-
? store.operation.token.key
|
|
56
|
-
: null
|
|
57
|
-
const storeCurrentSelectorTime =
|
|
58
|
-
store.operation.open && store.operation.token.type === `selector`
|
|
59
|
-
? store.operation.time
|
|
60
|
-
: null
|
|
61
|
-
|
|
62
|
-
const storeCurrentTransactionKey =
|
|
63
|
-
store.transactionStatus.phase === `applying`
|
|
64
|
-
? store.transactionStatus.key
|
|
65
|
-
: null
|
|
66
|
-
store.config.logger?.info(
|
|
67
|
-
`⏳ timeline "${options.key}" saw atom "${token.key}" go (`,
|
|
68
|
-
update.oldValue,
|
|
69
|
-
`->`,
|
|
70
|
-
update.newValue,
|
|
71
|
-
storeCurrentTransactionKey
|
|
72
|
-
? `) in transaction "${storeCurrentTransactionKey}"`
|
|
73
|
-
: storeCurrentSelectorKey
|
|
74
|
-
? `) in selector "${storeCurrentSelectorKey}"`
|
|
75
|
-
: `)`
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
storeCurrentTransactionKey &&
|
|
80
|
-
store.transactionStatus.phase === `applying`
|
|
81
|
-
) {
|
|
82
|
-
const currentTransaction = withdraw(
|
|
83
|
-
{ key: storeCurrentTransactionKey, type: `transaction` },
|
|
84
|
-
store
|
|
85
|
-
)
|
|
86
|
-
if (timelineData.transactionKey !== storeCurrentTransactionKey) {
|
|
87
|
-
if (timelineData.transactionKey) {
|
|
88
|
-
store.config.logger?.error(
|
|
89
|
-
`Timeline "${options.key}" was unable to resolve transaction "${timelineData.transactionKey}. This is probably a bug.`
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
timelineData.transactionKey = storeCurrentTransactionKey
|
|
93
|
-
const subscription = currentTransaction.subject.subscribe((update) => {
|
|
94
|
-
if (timelineData.timeTraveling === false) {
|
|
95
|
-
if (timelineData.at !== timelineData.history.length) {
|
|
96
|
-
timelineData.history.splice(timelineData.at)
|
|
97
|
-
}
|
|
98
|
-
timelineData.history.push({
|
|
99
|
-
type: `transaction_update`,
|
|
100
|
-
...update,
|
|
101
|
-
atomUpdates: update.atomUpdates.filter((atomUpdate) =>
|
|
102
|
-
options.atoms.some((atom) => atom.key === atomUpdate.key)
|
|
103
|
-
),
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
timelineData.at = timelineData.history.length
|
|
107
|
-
subscription.unsubscribe()
|
|
108
|
-
timelineData.transactionKey = null
|
|
109
|
-
store.config.logger?.info(
|
|
110
|
-
`⌛ timeline "${options.key}" got a transaction_update "${update.key}"`
|
|
111
|
-
)
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
} else if (storeCurrentSelectorKey) {
|
|
115
|
-
if (timelineData.timeTraveling === false) {
|
|
116
|
-
if (storeCurrentSelectorTime !== timelineData.selectorTime) {
|
|
117
|
-
const newSelectorUpdate: TimelineSelectorUpdate = {
|
|
118
|
-
type: `selector_update`,
|
|
119
|
-
key: storeCurrentSelectorKey,
|
|
120
|
-
atomUpdates: [],
|
|
121
|
-
}
|
|
122
|
-
newSelectorUpdate.atomUpdates.push({
|
|
123
|
-
key: token.key,
|
|
124
|
-
type: `atom_update`,
|
|
125
|
-
...update,
|
|
126
|
-
})
|
|
127
|
-
if (timelineData.at !== timelineData.history.length) {
|
|
128
|
-
timelineData.history.splice(timelineData.at)
|
|
129
|
-
}
|
|
130
|
-
timelineData.history.push(newSelectorUpdate)
|
|
131
|
-
|
|
132
|
-
store.config.logger?.info(
|
|
133
|
-
`⌛ timeline "${options.key}" got a selector_update "${storeCurrentSelectorKey}" with`,
|
|
134
|
-
newSelectorUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
|
|
135
|
-
)
|
|
136
|
-
timelineData.at = timelineData.history.length
|
|
137
|
-
timelineData.selectorTime = storeCurrentSelectorTime
|
|
138
|
-
} else {
|
|
139
|
-
const latestUpdate = timelineData.history.at(-1)
|
|
140
|
-
if (latestUpdate?.type === `selector_update`) {
|
|
141
|
-
latestUpdate.atomUpdates.push({
|
|
142
|
-
key: token.key,
|
|
143
|
-
type: `atom_update`,
|
|
144
|
-
...update,
|
|
145
|
-
})
|
|
146
|
-
store.config.logger?.info(
|
|
147
|
-
` ⌛ timeline "${options.key}" set selector_update "${storeCurrentSelectorKey}" to`,
|
|
148
|
-
latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key)
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
if (timelineData.timeTraveling === false) {
|
|
155
|
-
timelineData.selectorTime = null
|
|
156
|
-
if (timelineData.at !== timelineData.history.length) {
|
|
157
|
-
timelineData.history.splice(timelineData.at)
|
|
158
|
-
}
|
|
159
|
-
timelineData.history.push({
|
|
160
|
-
type: `atom_update`,
|
|
161
|
-
key: token.key,
|
|
162
|
-
oldValue: update.oldValue,
|
|
163
|
-
newValue: update.newValue,
|
|
164
|
-
})
|
|
165
|
-
store.config.logger?.info(
|
|
166
|
-
`⌛ timeline "${options.key}" got a state_update to "${token.key}"`
|
|
167
|
-
)
|
|
168
|
-
timelineData.at = timelineData.history.length
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
64
|
const core = target(store)
|
|
174
65
|
for (const tokenOrFamily of options.atoms) {
|
|
175
66
|
const timelineKey = core.timelineAtoms.getRelatedId(tokenOrFamily.key)
|
|
@@ -181,7 +72,9 @@ export function timeline__INTERNAL(
|
|
|
181
72
|
}
|
|
182
73
|
if (tokenOrFamily.type === `atom_family`) {
|
|
183
74
|
const family = tokenOrFamily
|
|
184
|
-
family.subject.subscribe((token) =>
|
|
75
|
+
family.subject.subscribe((token) =>
|
|
76
|
+
addAtomToTimeline(token, options.atoms, tl, store)
|
|
77
|
+
)
|
|
185
78
|
} else {
|
|
186
79
|
const token = tokenOrFamily
|
|
187
80
|
if (`family` in token && token.family) {
|
|
@@ -195,7 +88,7 @@ export function timeline__INTERNAL(
|
|
|
195
88
|
continue
|
|
196
89
|
}
|
|
197
90
|
}
|
|
198
|
-
|
|
91
|
+
addAtomToTimeline(token, options.atoms, tl, store)
|
|
199
92
|
}
|
|
200
93
|
core.timelineAtoms = core.timelineAtoms.set({
|
|
201
94
|
atomKey: tokenOrFamily.key,
|
|
@@ -203,7 +96,7 @@ export function timeline__INTERNAL(
|
|
|
203
96
|
})
|
|
204
97
|
}
|
|
205
98
|
|
|
206
|
-
store.
|
|
99
|
+
store.timelines = HAMT.set(options.key, tl, store.timelines)
|
|
207
100
|
const token: TimelineToken = {
|
|
208
101
|
key: options.key,
|
|
209
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
|
+
}
|