atom.io 0.39.1 → 0.40.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/internal/index.d.ts +1 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +95 -73
- package/dist/internal/index.js.map +1 -1
- package/dist/main/index.d.ts +8 -7
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js.map +1 -1
- package/dist/react-devtools/index.js +8 -5
- package/dist/react-devtools/index.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/events/ingest-selector-update.ts +13 -4
- package/src/internal/get-state/reduce-reference.ts +15 -2
- package/src/internal/operation.ts +3 -1
- package/src/internal/set-state/dispatch-state-update.ts +13 -11
- package/src/internal/timeline/create-timeline.ts +133 -98
- package/src/internal/timeline/time-travel.ts +42 -31
- package/src/internal/transaction/apply-transaction.ts +2 -2
- package/src/internal/transaction/build-transaction.ts +1 -1
- package/src/main/events.ts +8 -2
- package/src/main/logger.ts +1 -1
- package/src/main/timeline.ts +1 -7
- package/src/react-devtools/Updates.tsx +14 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReadableToken } from "atom.io"
|
|
1
|
+
import type { AtomUpdateEvent, ReadableToken, StateCreationEvent } from "atom.io"
|
|
2
2
|
|
|
3
3
|
import type { Store } from "./store"
|
|
4
4
|
import { isChildStore } from "./transaction/is-root-store"
|
|
@@ -14,6 +14,7 @@ export type OpenOperation<R extends ReadableToken<any> = ReadableToken<any>> = {
|
|
|
14
14
|
done: Set<string>
|
|
15
15
|
prev: Map<string, any>
|
|
16
16
|
timestamp: number
|
|
17
|
+
subEvents: (AtomUpdateEvent<any> | StateCreationEvent<any>)[]
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function openOperation(
|
|
@@ -36,6 +37,7 @@ export function openOperation(
|
|
|
36
37
|
prev: new Map(),
|
|
37
38
|
timestamp: Date.now(),
|
|
38
39
|
token,
|
|
40
|
+
subEvents: [],
|
|
39
41
|
}
|
|
40
42
|
store.logger.info(
|
|
41
43
|
`⭕`,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AtomUpdateEvent,
|
|
3
|
+
StateCreationEvent,
|
|
4
|
+
StateUpdate,
|
|
5
|
+
TimelineEvent,
|
|
6
|
+
} from "atom.io"
|
|
2
7
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
newest,
|
|
6
|
-
type Subject,
|
|
7
|
-
type WritableFamily,
|
|
8
|
-
type WritableState,
|
|
9
|
-
} from ".."
|
|
8
|
+
import type { MutableAtom, Subject, WritableFamily, WritableState } from ".."
|
|
9
|
+
import { newest } from ".."
|
|
10
10
|
import { hasRole } from "../atom"
|
|
11
11
|
import { readOrComputeValue } from "../get-state"
|
|
12
12
|
import type { Transceiver } from "../mutable"
|
|
@@ -27,13 +27,15 @@ export function dispatchOrDeferStateUpdate<T>(
|
|
|
27
27
|
const token = deposit(state)
|
|
28
28
|
if (stateIsNewlyCreated && family) {
|
|
29
29
|
state.subject.next({ newValue })
|
|
30
|
-
const stateCreationEvent: StateCreationEvent<any> = {
|
|
30
|
+
const stateCreationEvent: StateCreationEvent<any> & TimelineEvent<any> = {
|
|
31
|
+
write: true,
|
|
31
32
|
type: `state_creation`,
|
|
32
33
|
subType: `writable`,
|
|
33
34
|
token,
|
|
34
35
|
timestamp: Date.now(),
|
|
35
36
|
value: newValue,
|
|
36
37
|
}
|
|
38
|
+
target.operation.subEvents.push(stateCreationEvent)
|
|
37
39
|
const familySubject = family.subject as Subject<StateCreationEvent<any>>
|
|
38
40
|
familySubject.next(stateCreationEvent)
|
|
39
41
|
const innerTarget = newest(target)
|
|
@@ -75,7 +77,7 @@ export function dispatchOrDeferStateUpdate<T>(
|
|
|
75
77
|
`is now (`,
|
|
76
78
|
newValue,
|
|
77
79
|
`) subscribers:`,
|
|
78
|
-
subject.subscribers,
|
|
80
|
+
subject.subscribers.keys(),
|
|
79
81
|
)
|
|
80
82
|
break
|
|
81
83
|
case `atom`:
|
|
@@ -90,7 +92,7 @@ export function dispatchOrDeferStateUpdate<T>(
|
|
|
90
92
|
`->`,
|
|
91
93
|
newValue,
|
|
92
94
|
`) subscribers:`,
|
|
93
|
-
subject.subscribers,
|
|
95
|
+
subject.subscribers.keys(),
|
|
94
96
|
)
|
|
95
97
|
}
|
|
96
98
|
subject.next(update)
|
|
@@ -4,13 +4,16 @@ import type {
|
|
|
4
4
|
AtomUpdateEvent,
|
|
5
5
|
StateCreationEvent,
|
|
6
6
|
StateDisposalEvent,
|
|
7
|
+
StateUpdate,
|
|
7
8
|
TimelineEvent,
|
|
8
9
|
TimelineManageable,
|
|
9
10
|
TimelineOptions,
|
|
11
|
+
TimelineSelectorUpdateEvent,
|
|
10
12
|
TimelineToken,
|
|
11
13
|
TransactionOutcomeEvent,
|
|
12
14
|
TransactionSubEvent,
|
|
13
15
|
TransactionToken,
|
|
16
|
+
WritablePureSelectorToken,
|
|
14
17
|
} from "atom.io"
|
|
15
18
|
|
|
16
19
|
import { reduceReference } from "../get-state/reduce-reference"
|
|
@@ -24,10 +27,6 @@ export type Timeline<ManagedAtom extends TimelineManageable> = {
|
|
|
24
27
|
type: `timeline`
|
|
25
28
|
key: string
|
|
26
29
|
at: number
|
|
27
|
-
shouldCapture?: (
|
|
28
|
-
update: TimelineEvent<ManagedAtom>,
|
|
29
|
-
timeline: Timeline<ManagedAtom>,
|
|
30
|
-
) => boolean
|
|
31
30
|
timeTraveling: `into_future` | `into_past` | null
|
|
32
31
|
history: TimelineEvent<ManagedAtom>[]
|
|
33
32
|
selectorTime: number | null
|
|
@@ -46,7 +45,6 @@ export function createTimeline<ManagedAtom extends TimelineManageable>(
|
|
|
46
45
|
type: `timeline`,
|
|
47
46
|
key: options.key,
|
|
48
47
|
at: 0,
|
|
49
|
-
|
|
50
48
|
timeTraveling: null,
|
|
51
49
|
selectorTime: null,
|
|
52
50
|
transactionKey: null,
|
|
@@ -56,9 +54,7 @@ export function createTimeline<ManagedAtom extends TimelineManageable>(
|
|
|
56
54
|
subject: new Subject(),
|
|
57
55
|
subscriptions: new Map(),
|
|
58
56
|
}
|
|
59
|
-
|
|
60
|
-
tl.shouldCapture = options.shouldCapture
|
|
61
|
-
}
|
|
57
|
+
|
|
62
58
|
const timelineKey = options.key
|
|
63
59
|
const target = newest(store)
|
|
64
60
|
for (const initialTopic of options.scope) {
|
|
@@ -182,93 +178,32 @@ function addAtomToTimeline(
|
|
|
182
178
|
if (txUpdateInProgress) {
|
|
183
179
|
joinTransaction(store, tl, txUpdateInProgress)
|
|
184
180
|
} else if (currentSelectorToken && currentSelectorTime) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
atomUpdates: [],
|
|
194
|
-
}
|
|
195
|
-
latestUpdate.atomUpdates.push({
|
|
196
|
-
type: `atom_update`,
|
|
197
|
-
token: atomToken,
|
|
198
|
-
update,
|
|
199
|
-
timestamp: Date.now(), // 👺 use store operation
|
|
200
|
-
})
|
|
201
|
-
if (tl.at !== tl.history.length) {
|
|
202
|
-
tl.history.splice(tl.at)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
tl.history.push(latestUpdate)
|
|
206
|
-
|
|
207
|
-
store.logger.info(
|
|
208
|
-
`⌛`,
|
|
209
|
-
`timeline`,
|
|
210
|
-
tl.key,
|
|
211
|
-
`got a selector_update "${currentSelectorToken.key}" with`,
|
|
212
|
-
latestUpdate.atomUpdates.map(
|
|
213
|
-
(atomUpdate) => atomUpdate.token.key,
|
|
214
|
-
),
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
tl.at = tl.history.length
|
|
218
|
-
tl.selectorTime = currentSelectorTime
|
|
219
|
-
} else {
|
|
220
|
-
if (latestUpdate?.type === `selector_update`) {
|
|
221
|
-
latestUpdate.atomUpdates.push({
|
|
222
|
-
type: `atom_update`,
|
|
223
|
-
token: atomToken,
|
|
224
|
-
update,
|
|
225
|
-
timestamp: Date.now(), // 👺 use store operation
|
|
226
|
-
})
|
|
227
|
-
store.logger.info(
|
|
228
|
-
`⌛`,
|
|
229
|
-
`timeline`,
|
|
230
|
-
tl.key,
|
|
231
|
-
`set selector_update "${currentSelectorToken.key}" to`,
|
|
232
|
-
latestUpdate?.atomUpdates.map(
|
|
233
|
-
(atomUpdate) => atomUpdate.token.key,
|
|
234
|
-
),
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if (latestUpdate) {
|
|
239
|
-
const willCaptureSelectorUpdate =
|
|
240
|
-
tl.shouldCapture?.(latestUpdate, tl) ?? true
|
|
241
|
-
if (willCaptureSelectorUpdate) {
|
|
242
|
-
tl.subject.next(latestUpdate)
|
|
243
|
-
} else {
|
|
244
|
-
tl.history.pop()
|
|
245
|
-
tl.at = tl.history.length
|
|
246
|
-
}
|
|
247
|
-
}
|
|
181
|
+
buildSelectorUpdate(
|
|
182
|
+
store,
|
|
183
|
+
tl,
|
|
184
|
+
atomToken,
|
|
185
|
+
update,
|
|
186
|
+
currentSelectorToken,
|
|
187
|
+
currentSelectorTime,
|
|
188
|
+
)
|
|
248
189
|
} else {
|
|
249
190
|
const timestamp = Date.now()
|
|
250
191
|
tl.selectorTime = null
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const atomUpdate: AtomUpdateEvent<any> = {
|
|
192
|
+
|
|
193
|
+
const atomUpdate: AtomUpdateEvent<any> & TimelineEvent<any> = {
|
|
194
|
+
write: true,
|
|
255
195
|
type: `atom_update`,
|
|
256
196
|
token: deposit(atom),
|
|
257
197
|
update,
|
|
258
198
|
timestamp,
|
|
259
199
|
}
|
|
260
|
-
const willCapture = tl.shouldCapture?.(atomUpdate, tl) ?? true
|
|
261
200
|
store.logger.info(
|
|
262
201
|
`⌛`,
|
|
263
202
|
`timeline`,
|
|
264
203
|
tl.key,
|
|
265
204
|
`got an atom_update to "${atom.key}"`,
|
|
266
205
|
)
|
|
267
|
-
|
|
268
|
-
tl.history.push(atomUpdate)
|
|
269
|
-
tl.at = tl.history.length
|
|
270
|
-
tl.subject.next(atomUpdate)
|
|
271
|
-
}
|
|
206
|
+
addToHistory(tl, atomUpdate)
|
|
272
207
|
}
|
|
273
208
|
}
|
|
274
209
|
},
|
|
@@ -322,10 +257,6 @@ function joinTransaction(
|
|
|
322
257
|
unsubscribe()
|
|
323
258
|
tl.transactionKey = null
|
|
324
259
|
if (tl.timeTraveling === null && currentTxInstanceId) {
|
|
325
|
-
if (tl.at !== tl.history.length) {
|
|
326
|
-
tl.history.splice(tl.at)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
260
|
// biome-ignore lint/style/noNonNullAssertion: we are in the context of this timeline
|
|
330
261
|
const timelineTopics = store.timelineTopics.getRelatedKeys(tl.key)!
|
|
331
262
|
|
|
@@ -334,25 +265,99 @@ function joinTransaction(
|
|
|
334
265
|
timelineTopics,
|
|
335
266
|
)
|
|
336
267
|
|
|
337
|
-
const timelineTransactionUpdate:
|
|
338
|
-
TransactionToken<any
|
|
339
|
-
|
|
268
|
+
const timelineTransactionUpdate: TimelineEvent<any> &
|
|
269
|
+
TransactionOutcomeEvent<TransactionToken<any>> = {
|
|
270
|
+
write: true,
|
|
340
271
|
...transactionUpdate,
|
|
341
272
|
subEvents: subEventsFiltered,
|
|
342
273
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (willCapture) {
|
|
346
|
-
tl.history.push(timelineTransactionUpdate)
|
|
347
|
-
tl.at = tl.history.length
|
|
348
|
-
tl.subject.next(timelineTransactionUpdate)
|
|
349
|
-
}
|
|
274
|
+
|
|
275
|
+
addToHistory(tl, timelineTransactionUpdate)
|
|
350
276
|
}
|
|
351
277
|
},
|
|
352
278
|
)
|
|
353
279
|
}
|
|
354
280
|
}
|
|
355
281
|
|
|
282
|
+
function buildSelectorUpdate(
|
|
283
|
+
store: Store,
|
|
284
|
+
tl: Timeline<any>,
|
|
285
|
+
atomToken: AtomToken<any>,
|
|
286
|
+
eventOrUpdate: StateCreationEvent<any> | StateUpdate<any>,
|
|
287
|
+
currentSelectorToken: WritablePureSelectorToken<any>,
|
|
288
|
+
currentSelectorTime: number,
|
|
289
|
+
) {
|
|
290
|
+
let latestUpdate: TimelineEvent<any> | undefined = tl.history.at(-1)
|
|
291
|
+
if (currentSelectorTime !== tl.selectorTime) {
|
|
292
|
+
const selectorUpdate: TimelineEvent<any> & TimelineSelectorUpdateEvent<any> =
|
|
293
|
+
(latestUpdate = {
|
|
294
|
+
write: true,
|
|
295
|
+
type: `selector_update`,
|
|
296
|
+
timestamp: currentSelectorTime,
|
|
297
|
+
token: currentSelectorToken,
|
|
298
|
+
subEvents: [],
|
|
299
|
+
})
|
|
300
|
+
if (`type` in eventOrUpdate) {
|
|
301
|
+
latestUpdate.subEvents.push(eventOrUpdate)
|
|
302
|
+
} else {
|
|
303
|
+
latestUpdate.subEvents.push({
|
|
304
|
+
type: `atom_update`,
|
|
305
|
+
token: atomToken,
|
|
306
|
+
update: eventOrUpdate,
|
|
307
|
+
timestamp: Date.now(), // 👺 use store operation
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
addToHistory(tl, latestUpdate)
|
|
312
|
+
tl.selectorTime = currentSelectorTime
|
|
313
|
+
|
|
314
|
+
store.logger.info(
|
|
315
|
+
`⌛`,
|
|
316
|
+
`timeline`,
|
|
317
|
+
tl.key,
|
|
318
|
+
`got a selector_update "${currentSelectorToken.key}" with`,
|
|
319
|
+
latestUpdate.subEvents.map((event) => event.token.key),
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
const operation = store.operation
|
|
323
|
+
const unsub = store.on.operationClose.subscribe(
|
|
324
|
+
`timeline:${tl.key} (needs to gather nested selector creations)`,
|
|
325
|
+
() => {
|
|
326
|
+
unsub()
|
|
327
|
+
if (operation.open) {
|
|
328
|
+
selectorUpdate.subEvents = [
|
|
329
|
+
...operation.subEvents,
|
|
330
|
+
...selectorUpdate.subEvents,
|
|
331
|
+
]
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
)
|
|
335
|
+
} else {
|
|
336
|
+
if (latestUpdate?.type === `selector_update`) {
|
|
337
|
+
if (`type` in eventOrUpdate) {
|
|
338
|
+
latestUpdate.subEvents.push(eventOrUpdate)
|
|
339
|
+
} else {
|
|
340
|
+
latestUpdate.subEvents.push({
|
|
341
|
+
type: `atom_update`,
|
|
342
|
+
token: atomToken,
|
|
343
|
+
update: eventOrUpdate,
|
|
344
|
+
timestamp: Date.now(), // 👺 use store operation
|
|
345
|
+
})
|
|
346
|
+
}
|
|
347
|
+
store.logger.info(
|
|
348
|
+
`⌛`,
|
|
349
|
+
`timeline`,
|
|
350
|
+
tl.key,
|
|
351
|
+
`set selector_update "${currentSelectorToken.key}" to`,
|
|
352
|
+
latestUpdate?.subEvents.map((event) => event.token.key),
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (latestUpdate) {
|
|
357
|
+
tl.subject.next(latestUpdate)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
356
361
|
function filterTransactionSubEvents(
|
|
357
362
|
updates: TransactionSubEvent[],
|
|
358
363
|
timelineTopics: Set<string>,
|
|
@@ -402,6 +407,16 @@ function handleStateLifecycleEvent(
|
|
|
402
407
|
event: StateCreationEvent<any> | StateDisposalEvent<any>,
|
|
403
408
|
tl: Timeline<any>,
|
|
404
409
|
): void {
|
|
410
|
+
const currentSelectorToken =
|
|
411
|
+
store.operation.open &&
|
|
412
|
+
store.operation.token.type === `writable_pure_selector`
|
|
413
|
+
? store.operation.token
|
|
414
|
+
: null
|
|
415
|
+
const currentSelectorTime =
|
|
416
|
+
store.operation.open &&
|
|
417
|
+
store.operation.token.type === `writable_pure_selector`
|
|
418
|
+
? store.operation.timestamp
|
|
419
|
+
: null
|
|
405
420
|
if (!tl.timeTraveling) {
|
|
406
421
|
const target = newest(store)
|
|
407
422
|
if (isChildStore(target)) {
|
|
@@ -410,10 +425,21 @@ function handleStateLifecycleEvent(
|
|
|
410
425
|
const txUpdateInProgress = target.on.transactionApplying.state
|
|
411
426
|
if (txUpdateInProgress) {
|
|
412
427
|
joinTransaction(store, tl, txUpdateInProgress.update)
|
|
428
|
+
} else if (
|
|
429
|
+
currentSelectorToken &&
|
|
430
|
+
currentSelectorTime &&
|
|
431
|
+
event.type === `state_creation`
|
|
432
|
+
) {
|
|
433
|
+
buildSelectorUpdate(
|
|
434
|
+
store,
|
|
435
|
+
tl,
|
|
436
|
+
event.token,
|
|
437
|
+
event,
|
|
438
|
+
currentSelectorToken,
|
|
439
|
+
currentSelectorTime,
|
|
440
|
+
)
|
|
413
441
|
} else {
|
|
414
|
-
tl
|
|
415
|
-
tl.at = tl.history.length
|
|
416
|
-
tl.subject.next(event)
|
|
442
|
+
addToHistory(tl, event)
|
|
417
443
|
}
|
|
418
444
|
}
|
|
419
445
|
}
|
|
@@ -427,3 +453,12 @@ function handleStateLifecycleEvent(
|
|
|
427
453
|
break
|
|
428
454
|
}
|
|
429
455
|
}
|
|
456
|
+
|
|
457
|
+
function addToHistory(tl: Timeline<any>, event: TimelineEvent<any>): void {
|
|
458
|
+
if (tl.at !== tl.history.length) {
|
|
459
|
+
tl.history.splice(tl.at)
|
|
460
|
+
}
|
|
461
|
+
tl.history.push(event)
|
|
462
|
+
tl.at = tl.history.length
|
|
463
|
+
tl.subject.next(event)
|
|
464
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TimelineToken } from "atom.io"
|
|
1
|
+
import type { TimelineEvent, TimelineToken } from "atom.io"
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
ingestAtomUpdateEvent,
|
|
@@ -30,6 +30,7 @@ export const timeTravel = (
|
|
|
30
30
|
)
|
|
31
31
|
return
|
|
32
32
|
}
|
|
33
|
+
|
|
33
34
|
if (
|
|
34
35
|
(action === `redo` && timelineData.at === timelineData.history.length) ||
|
|
35
36
|
(action === `undo` && timelineData.at === 0)
|
|
@@ -46,46 +47,56 @@ export const timeTravel = (
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
timelineData.timeTraveling = action === `redo` ? `into_future` : `into_past`
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
let nextIndex = timelineData.at
|
|
51
|
+
let events: TimelineEvent<any>[]
|
|
52
|
+
switch (action) {
|
|
53
|
+
case `undo`:
|
|
54
|
+
--nextIndex
|
|
55
|
+
while (nextIndex !== 0 && timelineData.history[nextIndex].write !== true) {
|
|
56
|
+
--nextIndex
|
|
57
|
+
}
|
|
58
|
+
events = timelineData.history.slice(nextIndex, timelineData.at).reverse()
|
|
59
|
+
|
|
60
|
+
break
|
|
61
|
+
case `redo`:
|
|
62
|
+
++nextIndex
|
|
63
|
+
events = timelineData.history.slice(timelineData.at, nextIndex)
|
|
51
64
|
}
|
|
65
|
+
timelineData.at = nextIndex
|
|
52
66
|
|
|
53
|
-
const event = timelineData.history[timelineData.at]
|
|
54
67
|
const applying = action === `redo` ? `newValue` : `oldValue`
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
for (const event of events) {
|
|
70
|
+
switch (event.type) {
|
|
71
|
+
case `atom_update`: {
|
|
72
|
+
ingestAtomUpdateEvent(store, event, applying)
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
case `selector_update`: {
|
|
76
|
+
ingestSelectorUpdateEvent(store, event, applying)
|
|
77
|
+
break
|
|
78
|
+
}
|
|
79
|
+
case `transaction_outcome`: {
|
|
80
|
+
ingestTransactionOutcomeEvent(store, event, applying)
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
case `state_creation`: {
|
|
84
|
+
ingestCreationEvent(store, event, applying)
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
case `state_disposal`: {
|
|
88
|
+
ingestDisposalEvent(store, event, applying)
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
case `molecule_creation`:
|
|
92
|
+
case `molecule_disposal`:
|
|
72
93
|
}
|
|
73
|
-
case `state_disposal`: {
|
|
74
|
-
ingestDisposalEvent(store, event, applying)
|
|
75
|
-
break
|
|
76
|
-
}
|
|
77
|
-
case `molecule_creation`:
|
|
78
|
-
case `molecule_disposal`:
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (action === `redo`) {
|
|
82
|
-
++timelineData.at
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
timelineData.subject.next(action)
|
|
86
97
|
timelineData.timeTraveling = null
|
|
87
98
|
store.logger.info(
|
|
88
|
-
|
|
99
|
+
`⏸️`,
|
|
89
100
|
`timeline`,
|
|
90
101
|
token.key,
|
|
91
102
|
`"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`,
|
|
@@ -34,7 +34,7 @@ export function applyTransaction<F extends Fn>(
|
|
|
34
34
|
`🛄`,
|
|
35
35
|
`transaction`,
|
|
36
36
|
child.transactionMeta.update.token.key,
|
|
37
|
-
`
|
|
37
|
+
`applying ${updates.length} subEvents:`,
|
|
38
38
|
updates,
|
|
39
39
|
)
|
|
40
40
|
|
|
@@ -55,7 +55,7 @@ export function applyTransaction<F extends Fn>(
|
|
|
55
55
|
`🛬`,
|
|
56
56
|
`transaction`,
|
|
57
57
|
child.transactionMeta.update.token.key,
|
|
58
|
-
`
|
|
58
|
+
`applied`,
|
|
59
59
|
)
|
|
60
60
|
} else if (isChildStore(parent)) {
|
|
61
61
|
parent.transactionMeta.update.subEvents.push(child.transactionMeta.update)
|
package/src/main/events.ts
CHANGED
|
@@ -23,10 +23,13 @@ export type AtomUpdateEvent<A extends AtomToken<any>> = {
|
|
|
23
23
|
timestamp: number
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export type SelectorUpdateSubEvent<A extends AtomToken<any>> =
|
|
27
|
+
| AtomUpdateEvent<A>
|
|
28
|
+
| StateCreationEvent<any>
|
|
26
29
|
export type TimelineSelectorUpdateEvent<A extends TimelineManageable> = {
|
|
27
30
|
type: `selector_update`
|
|
28
31
|
token: SelectorToken<any>
|
|
29
|
-
|
|
32
|
+
subEvents: SelectorUpdateSubEvent<AtomOnly<A>>[]
|
|
30
33
|
timestamp: number
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -110,7 +113,9 @@ export type TransactionOutcomeEvent<T extends TransactionToken<any>> = {
|
|
|
110
113
|
output: ReturnType<TokenType<T>>
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
export type TimelineEvent<ManagedAtom extends TimelineManageable> =
|
|
116
|
+
export type TimelineEvent<ManagedAtom extends TimelineManageable> = {
|
|
117
|
+
write?: true
|
|
118
|
+
} & (
|
|
114
119
|
| AtomUpdateEvent<AtomOnly<ManagedAtom>>
|
|
115
120
|
| MoleculeCreationEvent
|
|
116
121
|
| MoleculeDisposalEvent
|
|
@@ -118,3 +123,4 @@ export type TimelineEvent<ManagedAtom extends TimelineManageable> =
|
|
|
118
123
|
| StateDisposalEvent<AtomOnly<ManagedAtom>>
|
|
119
124
|
| TimelineSelectorUpdateEvent<ManagedAtom>
|
|
120
125
|
| TransactionOutcomeEvent<TransactionToken<any>>
|
|
126
|
+
)
|
package/src/main/logger.ts
CHANGED
|
@@ -37,7 +37,7 @@ const LOGGER_ICON_DICTIONARY = {
|
|
|
37
37
|
"⏭️": `Transaction redo`,
|
|
38
38
|
"⏮️": `Transaction undo`,
|
|
39
39
|
"⏳": `Timeline event partially captured`,
|
|
40
|
-
"
|
|
40
|
+
"⏸️": `Time-travel complete`,
|
|
41
41
|
// Problems
|
|
42
42
|
"💣": `Dangerous action likely to cause bad errors down the line`,
|
|
43
43
|
"❗": `Dangerous action unless in development mode`,
|
package/src/main/timeline.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { Timeline } from "atom.io/internal"
|
|
2
1
|
import { createTimeline, IMPLICIT, timeTravel } from "atom.io/internal"
|
|
3
2
|
|
|
4
|
-
import type { AtomFamilyToken, AtomToken,
|
|
3
|
+
import type { AtomFamilyToken, AtomToken, TimelineToken } from "."
|
|
5
4
|
|
|
6
5
|
export type TimelineManageable = AtomFamilyToken<any, any> | AtomToken<any>
|
|
7
6
|
export type AtomOnly<M extends TimelineManageable> = M extends AtomFamilyToken<
|
|
@@ -33,11 +32,6 @@ export type TimelineOptions<ManagedAtom extends TimelineManageable> = {
|
|
|
33
32
|
key: string
|
|
34
33
|
/** The managed atoms (and families of atoms) to record */
|
|
35
34
|
scope: ManagedAtom[]
|
|
36
|
-
/** A function that determines whether a given update should be recorded */
|
|
37
|
-
shouldCapture?: (
|
|
38
|
-
update: TimelineEvent<ManagedAtom>,
|
|
39
|
-
timeline: Timeline<TimelineManageable>,
|
|
40
|
-
) => boolean
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
/**
|
|
@@ -192,18 +192,23 @@ export const TimelineUpdateFC: React.FC<{
|
|
|
192
192
|
}
|
|
193
193
|
})
|
|
194
194
|
) : timelineUpdate.type === `selector_update` ? (
|
|
195
|
-
timelineUpdate.
|
|
195
|
+
timelineUpdate.subEvents
|
|
196
196
|
.filter(
|
|
197
197
|
(atomUpdateEvent) => !atomUpdateEvent.token.key.startsWith(`👁🗨`),
|
|
198
198
|
)
|
|
199
|
-
.map((
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
199
|
+
.map((event, index) => {
|
|
200
|
+
switch (event.type) {
|
|
201
|
+
case `atom_update`:
|
|
202
|
+
return (
|
|
203
|
+
<article.AtomUpdate
|
|
204
|
+
key={`${timelineUpdate.token.key}:${index}:${event.token.key}`}
|
|
205
|
+
serialNumber={index}
|
|
206
|
+
atomUpdate={event}
|
|
207
|
+
/>
|
|
208
|
+
)
|
|
209
|
+
case `state_creation`:
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
207
212
|
})
|
|
208
213
|
) : timelineUpdate.type === `atom_update` ? (
|
|
209
214
|
<article.AtomUpdate
|