atom.io 0.5.0 → 0.6.1
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 +14 -14
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs +14 -14
- 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,34 +1,23 @@
|
|
|
1
1
|
import HAMT from "hamt_plus"
|
|
2
|
-
import * as Rx from "rxjs"
|
|
3
|
-
|
|
4
|
-
import { become } from "~/packages/anvl/src/function"
|
|
2
|
+
import type * as Rx from "rxjs"
|
|
5
3
|
|
|
6
4
|
import type { Store } from "."
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
markDone,
|
|
11
|
-
lookup,
|
|
12
|
-
IMPLICIT,
|
|
13
|
-
getState__INTERNAL,
|
|
14
|
-
setState__INTERNAL,
|
|
15
|
-
withdraw,
|
|
16
|
-
} from "."
|
|
5
|
+
import { target, IMPLICIT } from "."
|
|
6
|
+
import { createReadWriteSelector } from "./selector/create-read-write-selector"
|
|
7
|
+
import { createReadonlySelector } from "./selector/create-readonly-selector"
|
|
17
8
|
import type {
|
|
18
|
-
AtomToken,
|
|
19
9
|
FamilyMetadata,
|
|
20
10
|
ReadonlySelectorOptions,
|
|
21
11
|
ReadonlySelectorToken,
|
|
22
12
|
SelectorOptions,
|
|
23
13
|
SelectorToken,
|
|
24
|
-
StateToken,
|
|
25
14
|
} from ".."
|
|
26
|
-
import type { Transactors } from "../transaction"
|
|
27
15
|
|
|
28
16
|
export type Selector<T> = {
|
|
29
17
|
key: string
|
|
30
18
|
type: `selector`
|
|
31
19
|
family?: FamilyMetadata
|
|
20
|
+
install: (store: Store) => void
|
|
32
21
|
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
33
22
|
get: () => T
|
|
34
23
|
set: (newValue: T | ((oldValue: T) => T)) => void
|
|
@@ -37,143 +26,11 @@ export type ReadonlySelector<T> = {
|
|
|
37
26
|
key: string
|
|
38
27
|
type: `readonly_selector`
|
|
39
28
|
family?: FamilyMetadata
|
|
29
|
+
install: (store: Store) => void
|
|
40
30
|
subject: Rx.Subject<{ newValue: T; oldValue: T }>
|
|
41
31
|
get: () => T
|
|
42
32
|
}
|
|
43
33
|
|
|
44
|
-
export const lookupSelectorSources = (
|
|
45
|
-
key: string,
|
|
46
|
-
store: Store
|
|
47
|
-
): (
|
|
48
|
-
| AtomToken<unknown>
|
|
49
|
-
| ReadonlySelectorToken<unknown>
|
|
50
|
-
| SelectorToken<unknown>
|
|
51
|
-
)[] =>
|
|
52
|
-
target(store)
|
|
53
|
-
.selectorGraph.getRelations(key)
|
|
54
|
-
.filter(({ source }) => source !== key)
|
|
55
|
-
.map(({ source }) => lookup(source, store))
|
|
56
|
-
|
|
57
|
-
export const traceSelectorAtoms = (
|
|
58
|
-
selectorKey: string,
|
|
59
|
-
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
60
|
-
store: Store
|
|
61
|
-
): AtomToken<unknown>[] => {
|
|
62
|
-
const roots: AtomToken<unknown>[] = []
|
|
63
|
-
|
|
64
|
-
const sources = lookupSelectorSources(dependency.key, store)
|
|
65
|
-
let depth = 0
|
|
66
|
-
while (sources.length > 0) {
|
|
67
|
-
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
68
|
-
const source = sources.shift()!
|
|
69
|
-
++depth
|
|
70
|
-
if (depth > 999) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Maximum selector dependency depth exceeded in selector "${selectorKey}".`
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (source.type !== `atom`) {
|
|
77
|
-
sources.push(...lookupSelectorSources(source.key, store))
|
|
78
|
-
} else {
|
|
79
|
-
roots.push(source)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return roots
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const traceAllSelectorAtoms = (
|
|
87
|
-
selectorKey: string,
|
|
88
|
-
store: Store
|
|
89
|
-
): AtomToken<unknown>[] => {
|
|
90
|
-
const sources = lookupSelectorSources(selectorKey, store)
|
|
91
|
-
return sources.flatMap((source) =>
|
|
92
|
-
source.type === `atom`
|
|
93
|
-
? source
|
|
94
|
-
: traceSelectorAtoms(selectorKey, source, store)
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const updateSelectorAtoms = (
|
|
99
|
-
selectorKey: string,
|
|
100
|
-
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
101
|
-
store: Store
|
|
102
|
-
): void => {
|
|
103
|
-
const core = target(store)
|
|
104
|
-
if (dependency.type === `atom`) {
|
|
105
|
-
core.selectorAtoms = core.selectorAtoms.set({
|
|
106
|
-
selectorKey,
|
|
107
|
-
atomKey: dependency.key,
|
|
108
|
-
})
|
|
109
|
-
store.config.logger?.info(
|
|
110
|
-
` || adding root for "${selectorKey}": ${dependency.key}`
|
|
111
|
-
)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
const roots = traceSelectorAtoms(selectorKey, dependency, store)
|
|
115
|
-
store.config.logger?.info(
|
|
116
|
-
` || adding roots for "${selectorKey}":`,
|
|
117
|
-
roots.map((r) => r.key)
|
|
118
|
-
)
|
|
119
|
-
for (const root of roots) {
|
|
120
|
-
core.selectorAtoms = core.selectorAtoms.set({
|
|
121
|
-
selectorKey,
|
|
122
|
-
atomKey: root.key,
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export const registerSelector = (
|
|
128
|
-
selectorKey: string,
|
|
129
|
-
store: Store = IMPLICIT.STORE
|
|
130
|
-
): Transactors => ({
|
|
131
|
-
get: (dependency) => {
|
|
132
|
-
const core = target(store)
|
|
133
|
-
const alreadyRegistered = core.selectorGraph
|
|
134
|
-
.getRelations(selectorKey)
|
|
135
|
-
.some(({ source }) => source === dependency.key)
|
|
136
|
-
|
|
137
|
-
const dependencyState = withdraw(dependency, store)
|
|
138
|
-
if (dependencyState === null) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
const dependencyValue = getState__INTERNAL(dependencyState, store)
|
|
144
|
-
|
|
145
|
-
if (alreadyRegistered) {
|
|
146
|
-
store.config.logger?.info(
|
|
147
|
-
` || ${selectorKey} <- ${dependency.key} =`,
|
|
148
|
-
dependencyValue
|
|
149
|
-
)
|
|
150
|
-
} else {
|
|
151
|
-
store.config.logger?.info(
|
|
152
|
-
`🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
|
|
153
|
-
dependencyValue,
|
|
154
|
-
`)`
|
|
155
|
-
)
|
|
156
|
-
core.selectorGraph = core.selectorGraph.set(
|
|
157
|
-
{ from: dependency.key, to: selectorKey },
|
|
158
|
-
{
|
|
159
|
-
source: dependency.key,
|
|
160
|
-
}
|
|
161
|
-
)
|
|
162
|
-
}
|
|
163
|
-
updateSelectorAtoms(selectorKey, dependency, store)
|
|
164
|
-
return dependencyValue
|
|
165
|
-
},
|
|
166
|
-
set: (stateToken, newValue) => {
|
|
167
|
-
const state = withdraw(stateToken, store)
|
|
168
|
-
if (state === null) {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
setState__INTERNAL(state, newValue, store)
|
|
174
|
-
},
|
|
175
|
-
})
|
|
176
|
-
|
|
177
34
|
export function selector__INTERNAL<T>(
|
|
178
35
|
options: SelectorOptions<T>,
|
|
179
36
|
family?: FamilyMetadata,
|
|
@@ -190,70 +47,15 @@ export function selector__INTERNAL<T>(
|
|
|
190
47
|
store: Store = IMPLICIT.STORE
|
|
191
48
|
): ReadonlySelectorToken<T> | SelectorToken<T> {
|
|
192
49
|
const core = target(store)
|
|
50
|
+
|
|
193
51
|
if (HAMT.has(options.key, core.selectors)) {
|
|
194
52
|
store.config.logger?.error(
|
|
195
53
|
`Key "${options.key}" already exists in the store.`
|
|
196
54
|
)
|
|
197
55
|
}
|
|
198
56
|
|
|
199
|
-
const subject = new Rx.Subject<{ newValue: T; oldValue: T }>()
|
|
200
|
-
|
|
201
|
-
const { get, set } = registerSelector(options.key, store)
|
|
202
|
-
const getSelf = () => {
|
|
203
|
-
const value = options.get({ get })
|
|
204
|
-
cacheValue(options.key, value, store)
|
|
205
|
-
return value
|
|
206
|
-
}
|
|
207
57
|
if (!(`set` in options)) {
|
|
208
|
-
|
|
209
|
-
...options,
|
|
210
|
-
subject,
|
|
211
|
-
get: getSelf,
|
|
212
|
-
type: `readonly_selector`,
|
|
213
|
-
...(family && { family }),
|
|
214
|
-
}
|
|
215
|
-
core.readonlySelectors = HAMT.set(
|
|
216
|
-
options.key,
|
|
217
|
-
readonlySelector,
|
|
218
|
-
core.readonlySelectors
|
|
219
|
-
)
|
|
220
|
-
const initialValue = getSelf()
|
|
221
|
-
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
222
|
-
const token: ReadonlySelectorToken<T> = {
|
|
223
|
-
key: options.key,
|
|
224
|
-
type: `readonly_selector`,
|
|
225
|
-
family,
|
|
226
|
-
}
|
|
227
|
-
store.subject.selectorCreation.next(token)
|
|
228
|
-
return token
|
|
229
|
-
}
|
|
230
|
-
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
231
|
-
store.config.logger?.info(` <- "${options.key}" became`, next)
|
|
232
|
-
const oldValue = getSelf()
|
|
233
|
-
const newValue = become(next)(oldValue)
|
|
234
|
-
cacheValue(options.key, newValue, store)
|
|
235
|
-
markDone(options.key, store)
|
|
236
|
-
if (store.transactionStatus.phase === `idle`) {
|
|
237
|
-
subject.next({ newValue, oldValue })
|
|
238
|
-
}
|
|
239
|
-
options.set({ get, set }, newValue)
|
|
240
|
-
}
|
|
241
|
-
const mySelector: Selector<T> = {
|
|
242
|
-
...options,
|
|
243
|
-
subject,
|
|
244
|
-
get: getSelf,
|
|
245
|
-
set: setSelf,
|
|
246
|
-
type: `selector`,
|
|
247
|
-
...(family && { family }),
|
|
248
|
-
}
|
|
249
|
-
core.selectors = HAMT.set(options.key, mySelector, core.selectors)
|
|
250
|
-
const initialValue = getSelf()
|
|
251
|
-
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
252
|
-
const token: SelectorToken<T> = {
|
|
253
|
-
key: options.key,
|
|
254
|
-
type: `selector`,
|
|
255
|
-
family,
|
|
58
|
+
return createReadonlySelector(options, family, store, core)
|
|
256
59
|
}
|
|
257
|
-
store
|
|
258
|
-
return token
|
|
60
|
+
return createReadWriteSelector(options, family, store, core)
|
|
259
61
|
}
|
package/src/internal/store.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
Selector,
|
|
14
14
|
TransactionStatus,
|
|
15
15
|
Timeline,
|
|
16
|
-
|
|
16
|
+
Transaction,
|
|
17
17
|
} from "."
|
|
18
18
|
import type {
|
|
19
19
|
AtomToken,
|
|
@@ -21,7 +21,6 @@ import type {
|
|
|
21
21
|
ReadonlySelectorToken,
|
|
22
22
|
SelectorToken,
|
|
23
23
|
TimelineToken,
|
|
24
|
-
Transaction,
|
|
25
24
|
TransactionToken,
|
|
26
25
|
} from ".."
|
|
27
26
|
|
|
@@ -47,9 +46,8 @@ export interface Store {
|
|
|
47
46
|
selectorAtoms: Join<null, `selectorKey`, `atomKey`>
|
|
48
47
|
selectorGraph: Join<{ source: string }>
|
|
49
48
|
selectors: Hamt<Selector<any>, string>
|
|
50
|
-
timelines: Hamt<Timeline, string>
|
|
51
49
|
timelineAtoms: Join<null, `timelineKey`, `atomKey`>
|
|
52
|
-
|
|
50
|
+
timelines: Hamt<Timeline, string>
|
|
53
51
|
transactions: Hamt<Transaction<any>, string>
|
|
54
52
|
valueMap: Hamt<any, string>
|
|
55
53
|
|
|
@@ -71,46 +69,75 @@ export interface Store {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
export const createStore = (name: string): Store =>
|
|
75
|
-
|
|
72
|
+
export const createStore = (name: string, store: Store | null = null): Store => {
|
|
73
|
+
const copiedStore = {
|
|
74
|
+
...(store ??
|
|
75
|
+
(() => ({
|
|
76
|
+
atomsThatAreDefault: new Set(),
|
|
77
|
+
selectorAtoms: new Join({ relationType: `n:n` })
|
|
78
|
+
.from(`selectorKey`)
|
|
79
|
+
.to(`atomKey`),
|
|
80
|
+
selectorGraph: new Join({ relationType: `n:n` }),
|
|
81
|
+
valueMap: HAMT.make<any, string>(),
|
|
82
|
+
}))()),
|
|
83
|
+
|
|
76
84
|
atoms: HAMT.make<Atom<any>, string>(),
|
|
77
|
-
atomsThatAreDefault: new Set(),
|
|
78
85
|
readonlySelectors: HAMT.make<ReadonlySelector<any>, string>(),
|
|
79
|
-
selectorAtoms: new Join({ relationType: `n:n` })
|
|
80
|
-
.from(`selectorKey`)
|
|
81
|
-
.to(`atomKey`),
|
|
82
|
-
selectorGraph: new Join({ relationType: `n:n` }),
|
|
83
86
|
selectors: HAMT.make<Selector<any>, string>(),
|
|
87
|
+
transactions: HAMT.make<Transaction<any>, string>(),
|
|
84
88
|
timelines: HAMT.make<Timeline, string>(),
|
|
89
|
+
|
|
85
90
|
timelineAtoms: new Join({ relationType: `1:n` })
|
|
86
91
|
.from(`timelineKey`)
|
|
87
92
|
.to(`atomKey`),
|
|
88
|
-
timelineStore: HAMT.make<TimelineData, string>(),
|
|
89
|
-
transactions: HAMT.make<Transaction<any>, string>(),
|
|
90
|
-
valueMap: HAMT.make<any, string>(),
|
|
91
93
|
|
|
92
94
|
subject: {
|
|
93
95
|
atomCreation: new Rx.Subject(),
|
|
94
96
|
selectorCreation: new Rx.Subject(),
|
|
95
97
|
transactionCreation: new Rx.Subject(),
|
|
96
98
|
timelineCreation: new Rx.Subject(),
|
|
99
|
+
...store?.subject,
|
|
97
100
|
},
|
|
98
101
|
|
|
99
102
|
operation: {
|
|
100
103
|
open: false,
|
|
104
|
+
...store?.operation,
|
|
101
105
|
},
|
|
102
106
|
transactionStatus: {
|
|
103
107
|
phase: `idle`,
|
|
108
|
+
...store?.transactionStatus,
|
|
104
109
|
},
|
|
105
110
|
config: {
|
|
106
|
-
name,
|
|
107
111
|
logger: {
|
|
108
112
|
...console,
|
|
109
113
|
info: doNothing,
|
|
114
|
+
...store?.config?.logger,
|
|
110
115
|
},
|
|
111
116
|
logger__INTERNAL: console,
|
|
117
|
+
...store?.config,
|
|
118
|
+
name,
|
|
112
119
|
},
|
|
113
|
-
} satisfies Store
|
|
120
|
+
} satisfies Store
|
|
121
|
+
|
|
122
|
+
store?.atoms.forEach((atom) => {
|
|
123
|
+
const copiedAtom = { ...atom, subject: new Rx.Subject() } satisfies Atom<any>
|
|
124
|
+
copiedStore.atoms = HAMT.set(atom.key, copiedAtom, copiedStore.atoms)
|
|
125
|
+
})
|
|
126
|
+
store?.readonlySelectors.forEach((selector) => {
|
|
127
|
+
selector.install(copiedStore)
|
|
128
|
+
})
|
|
129
|
+
store?.selectors.forEach((selector) => {
|
|
130
|
+
selector.install(copiedStore)
|
|
131
|
+
})
|
|
132
|
+
store?.transactions.forEach((tx) => {
|
|
133
|
+
tx.install(copiedStore)
|
|
134
|
+
})
|
|
135
|
+
store?.timelines.forEach((timeline) => {
|
|
136
|
+
timeline.install(copiedStore)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
return copiedStore
|
|
140
|
+
}
|
|
114
141
|
|
|
115
142
|
export const IMPLICIT = {
|
|
116
143
|
STORE_INTERNAL: undefined as Store | undefined,
|
|
@@ -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>(
|
|
@@ -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"
|