atom.io 0.6.2 → 0.6.4

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.
Files changed (117) hide show
  1. package/dist/index.d.mts +27 -15
  2. package/dist/index.d.ts +27 -15
  3. package/dist/index.js +44 -24
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +44 -24
  6. package/dist/index.mjs.map +1 -1
  7. package/json/dist/index.d.mts +18 -0
  8. package/json/dist/index.d.ts +18 -0
  9. package/json/dist/index.js +51 -0
  10. package/json/dist/index.js.map +1 -0
  11. package/json/dist/index.mjs +15 -0
  12. package/json/dist/index.mjs.map +1 -0
  13. package/json/package.json +13 -13
  14. package/package.json +31 -12
  15. package/react/dist/index.d.mts +24 -0
  16. package/react/dist/index.d.ts +24 -0
  17. package/react/dist/index.js +87 -0
  18. package/react/dist/index.js.map +1 -0
  19. package/react/dist/index.mjs +45 -0
  20. package/react/dist/index.mjs.map +1 -0
  21. package/react/package.json +13 -13
  22. package/react-devtools/dist/index.css +26 -0
  23. package/react-devtools/dist/index.css.map +1 -0
  24. package/react-devtools/dist/index.d.mts +15 -0
  25. package/react-devtools/dist/index.d.ts +15 -0
  26. package/react-devtools/dist/index.js +2108 -0
  27. package/react-devtools/dist/index.js.map +1 -0
  28. package/react-devtools/dist/index.mjs +2080 -0
  29. package/react-devtools/dist/index.mjs.map +1 -0
  30. package/react-devtools/package.json +13 -13
  31. package/realtime/dist/index.d.mts +27 -0
  32. package/realtime/dist/index.d.ts +27 -0
  33. package/realtime/dist/index.js +191 -0
  34. package/realtime/dist/index.js.map +1 -0
  35. package/realtime/dist/index.mjs +152 -0
  36. package/realtime/dist/index.mjs.map +1 -0
  37. package/realtime/package.json +13 -13
  38. package/realtime-react/dist/index.d.mts +45 -0
  39. package/realtime-react/dist/index.d.ts +45 -0
  40. package/realtime-react/dist/index.js +217 -0
  41. package/realtime-react/dist/index.js.map +1 -0
  42. package/realtime-react/dist/index.mjs +172 -0
  43. package/realtime-react/dist/index.mjs.map +1 -0
  44. package/realtime-react/package.json +13 -13
  45. package/realtime-testing/dist/index.d.mts +49 -0
  46. package/realtime-testing/dist/index.d.ts +49 -0
  47. package/realtime-testing/dist/index.js +165 -0
  48. package/realtime-testing/dist/index.js.map +1 -0
  49. package/realtime-testing/dist/index.mjs +129 -0
  50. package/realtime-testing/dist/index.mjs.map +1 -0
  51. package/realtime-testing/package.json +15 -0
  52. package/src/atom.ts +16 -17
  53. package/src/index.ts +59 -59
  54. package/src/internal/atom-internal.ts +37 -37
  55. package/src/internal/families-internal.ts +115 -116
  56. package/src/internal/get.ts +83 -83
  57. package/src/internal/index.ts +1 -0
  58. package/src/internal/is-default.ts +17 -17
  59. package/src/internal/meta/attach-meta.ts +7 -7
  60. package/src/internal/meta/meta-state.ts +115 -115
  61. package/src/internal/operation.ts +93 -93
  62. package/src/internal/selector/create-read-write-selector.ts +47 -47
  63. package/src/internal/selector/create-readonly-selector.ts +38 -38
  64. package/src/internal/selector/lookup-selector-sources.ts +9 -9
  65. package/src/internal/selector/register-selector.ts +44 -44
  66. package/src/internal/selector/trace-selector-atoms.ts +30 -30
  67. package/src/internal/selector/update-selector-atoms.ts +25 -25
  68. package/src/internal/selector-internal.ts +38 -39
  69. package/src/internal/set.ts +78 -78
  70. package/src/internal/store.ts +119 -119
  71. package/src/internal/subject.ts +24 -0
  72. package/src/internal/subscribe-internal.ts +62 -62
  73. package/src/internal/time-travel-internal.ts +76 -76
  74. package/src/internal/timeline/add-atom-to-timeline.ts +158 -153
  75. package/src/internal/timeline-internal.ts +81 -82
  76. package/src/internal/transaction/abort-transaction.ts +8 -8
  77. package/src/internal/transaction/apply-transaction.ts +41 -41
  78. package/src/internal/transaction/build-transaction.ts +28 -28
  79. package/src/internal/transaction/index.ts +7 -7
  80. package/src/internal/transaction/redo-transaction.ts +13 -13
  81. package/src/internal/transaction/undo-transaction.ts +13 -13
  82. package/src/internal/transaction-internal.ts +49 -49
  83. package/src/json/select-json.ts +12 -12
  84. package/src/logger.ts +30 -30
  85. package/src/react/store-context.tsx +5 -6
  86. package/src/react/store-hooks.ts +19 -20
  87. package/src/react-devtools/AtomIODevtools.tsx +85 -85
  88. package/src/react-devtools/StateEditor.tsx +54 -55
  89. package/src/react-devtools/TokenList.tsx +49 -45
  90. package/src/react-explorer/AtomIOExplorer.tsx +198 -187
  91. package/src/react-explorer/explorer-effects.ts +11 -11
  92. package/src/react-explorer/explorer-states.ts +186 -193
  93. package/src/react-explorer/index.ts +11 -11
  94. package/src/react-explorer/space-states.ts +48 -50
  95. package/src/react-explorer/view-states.ts +25 -25
  96. package/src/realtime/hook-composition/expose-family.ts +81 -81
  97. package/src/realtime/hook-composition/expose-single.ts +26 -26
  98. package/src/realtime/hook-composition/expose-timeline.ts +60 -0
  99. package/src/realtime/hook-composition/index.ts +2 -2
  100. package/src/realtime/hook-composition/receive-state.ts +18 -18
  101. package/src/realtime/hook-composition/receive-transaction.ts +8 -8
  102. package/src/realtime-react/realtime-context.tsx +18 -19
  103. package/src/realtime-react/realtime-hooks.ts +17 -17
  104. package/src/realtime-react/realtime-state.ts +4 -4
  105. package/src/realtime-react/use-pull-family-member.ts +16 -17
  106. package/src/realtime-react/use-pull-family.ts +14 -15
  107. package/src/realtime-react/use-pull.ts +13 -14
  108. package/src/realtime-react/use-push.ts +16 -17
  109. package/src/realtime-react/use-server-action.ts +22 -23
  110. package/src/realtime-testing/index.ts +1 -0
  111. package/src/realtime-testing/setup-realtime-test.tsx +159 -0
  112. package/src/selector.ts +26 -27
  113. package/src/silo.ts +38 -38
  114. package/src/subscribe.ts +68 -68
  115. package/src/timeline.ts +13 -13
  116. package/src/transaction.ts +28 -28
  117. package/src/web-effects/storage.ts +17 -17
@@ -1,82 +1,82 @@
1
1
  import type { Atom, ReadonlySelector, Selector, Store } from "."
2
2
  import {
3
- getState__INTERNAL,
4
- withdraw,
5
- recallState,
6
- traceAllSelectorAtoms,
3
+ getState__INTERNAL,
4
+ withdraw,
5
+ recallState,
6
+ traceAllSelectorAtoms,
7
7
  } from "."
8
8
  import type { StateUpdate } from ".."
9
9
 
10
10
  export const prepareUpdate = <T>(
11
- state: Atom<T> | ReadonlySelector<T> | Selector<T>,
12
- store: Store
11
+ state: Atom<T> | ReadonlySelector<T> | Selector<T>,
12
+ store: Store,
13
13
  ): StateUpdate<T> => {
14
- const oldValue = recallState(state, store)
15
- const newValue = getState__INTERNAL(state, store)
16
- return { newValue, oldValue }
14
+ const oldValue = recallState(state, store)
15
+ const newValue = getState__INTERNAL(state, store)
16
+ return { newValue, oldValue }
17
17
  }
18
18
 
19
19
  export const stowUpdate = <T>(
20
- state: Atom<T>,
21
- update: StateUpdate<T>,
22
- store: Store
20
+ state: Atom<T>,
21
+ update: StateUpdate<T>,
22
+ store: Store,
23
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, `)`)
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
34
  }
35
35
 
36
36
  export const emitUpdate = <T>(
37
- state: Atom<T> | ReadonlySelector<T> | Selector<T>,
38
- update: StateUpdate<T>,
39
- store: Store
37
+ state: Atom<T> | ReadonlySelector<T> | Selector<T>,
38
+ update: StateUpdate<T>,
39
+ store: Store,
40
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)
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
51
  }
52
52
 
53
53
  export const subscribeToRootAtoms = <T>(
54
- state: ReadonlySelector<T> | Selector<T>,
55
- store: Store
54
+ state: ReadonlySelector<T> | Selector<T>,
55
+ store: Store,
56
56
  ): { unsubscribe: () => void }[] | null => {
57
- const dependencySubscriptions =
58
- `default` in state
59
- ? null
60
- : traceAllSelectorAtoms(state.key, store).map((atomToken) => {
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
- }
67
- return atom.subject.subscribe((atomChange) => {
68
- store.config.logger?.info(
69
- `📢 selector "${state.key}" saw root "${atomToken.key}" go (`,
70
- atomChange.oldValue,
71
- `->`,
72
- atomChange.newValue,
73
- `)`
74
- )
75
- const oldValue = recallState(state, store)
76
- const newValue = getState__INTERNAL(state, store)
77
- store.config.logger?.info(` <- ${state.key} became`, newValue)
78
- state.subject.next({ newValue, oldValue })
79
- })
80
- })
81
- return dependencySubscriptions
57
+ const dependencySubscriptions =
58
+ `default` in state
59
+ ? null
60
+ : traceAllSelectorAtoms(state.key, store).map((atomToken) => {
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
+ }
67
+ return atom.subject.subscribe((atomChange) => {
68
+ store.config.logger?.info(
69
+ `📢 selector "${state.key}" saw root "${atomToken.key}" go (`,
70
+ atomChange.oldValue,
71
+ `->`,
72
+ atomChange.newValue,
73
+ `)`,
74
+ )
75
+ const oldValue = recallState(state, store)
76
+ const newValue = getState__INTERNAL(state, store)
77
+ store.config.logger?.info(` <- ${state.key} became`, newValue)
78
+ state.subject.next({ newValue, oldValue })
79
+ })
80
+ })
81
+ return dependencySubscriptions
82
82
  }
@@ -4,86 +4,86 @@ import type { TimelineToken } from ".."
4
4
  import { setState } from ".."
5
5
 
6
6
  export const redo__INTERNAL = (
7
- token: TimelineToken,
8
- store: Store = IMPLICIT.STORE
7
+ token: TimelineToken,
8
+ store: Store = IMPLICIT.STORE,
9
9
  ): void => {
10
- store.config.logger?.info(`⏩ redo "${token.key}"`)
11
- const timelineData = store.timelines.get(token.key)
12
- if (!timelineData) {
13
- store.config.logger?.error(
14
- `Failed to redo on timeline "${token.key}". This timeline has not been initialized.`
15
- )
16
- return
17
- }
18
- if (timelineData.at === timelineData.history.length) {
19
- store.config.logger?.warn(
20
- `Failed to redo at the end of timeline "${token.key}". There is nothing to redo.`
21
- )
22
- return
23
- }
24
- timelineData.timeTraveling = true
25
- const update = timelineData.history[timelineData.at]
26
- switch (update.type) {
27
- case `atom_update`: {
28
- const { key, newValue } = update
29
- setState({ key, type: `atom` }, newValue, store)
30
- break
31
- }
32
- case `selector_update`:
33
- case `transaction_update`: {
34
- for (const atomUpdate of update.atomUpdates) {
35
- const { key, newValue } = atomUpdate
36
- setState({ key, type: `atom` }, newValue, store)
37
- }
38
- break
39
- }
40
- }
41
- ++timelineData.at
42
- timelineData.timeTraveling = false
43
- store.config.logger?.info(
44
- `⏹️ "${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
45
- )
10
+ store.config.logger?.info(`⏩ redo "${token.key}"`)
11
+ const timelineData = store.timelines.get(token.key)
12
+ if (!timelineData) {
13
+ store.config.logger?.error(
14
+ `Failed to redo on timeline "${token.key}". This timeline has not been initialized.`,
15
+ )
16
+ return
17
+ }
18
+ if (timelineData.at === timelineData.history.length) {
19
+ store.config.logger?.warn(
20
+ `Failed to redo at the end of timeline "${token.key}". There is nothing to redo.`,
21
+ )
22
+ return
23
+ }
24
+ timelineData.timeTraveling = true
25
+ const update = timelineData.history[timelineData.at]
26
+ switch (update.type) {
27
+ case `atom_update`: {
28
+ const { key, newValue } = update
29
+ setState({ key, type: `atom` }, newValue, store)
30
+ break
31
+ }
32
+ case `selector_update`:
33
+ case `transaction_update`: {
34
+ for (const atomUpdate of update.atomUpdates) {
35
+ const { key, newValue } = atomUpdate
36
+ setState({ key, type: `atom` }, newValue, store)
37
+ }
38
+ break
39
+ }
40
+ }
41
+ ++timelineData.at
42
+ timelineData.timeTraveling = false
43
+ store.config.logger?.info(
44
+ `⏹️ "${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`,
45
+ )
46
46
  }
47
47
 
48
48
  export const undo__INTERNAL = (
49
- token: TimelineToken,
50
- store: Store = IMPLICIT.STORE
49
+ token: TimelineToken,
50
+ store: Store = IMPLICIT.STORE,
51
51
  ): void => {
52
- store.config.logger?.info(`⏪ undo "${token.key}"`)
53
- const timelineData = store.timelines.get(token.key)
54
- if (!timelineData) {
55
- store.config.logger?.error(
56
- `Failed to undo on timeline "${token.key}". This timeline has not been initialized.`
57
- )
58
- return
59
- }
60
- if (timelineData.at === 0) {
61
- store.config.logger?.warn(
62
- `Failed to undo at the beginning of timeline "${token.key}". There is nothing to undo.`
63
- )
64
- return
65
- }
66
- timelineData.timeTraveling = true
52
+ store.config.logger?.info(`⏪ undo "${token.key}"`)
53
+ const timelineData = store.timelines.get(token.key)
54
+ if (!timelineData) {
55
+ store.config.logger?.error(
56
+ `Failed to undo on timeline "${token.key}". This timeline has not been initialized.`,
57
+ )
58
+ return
59
+ }
60
+ if (timelineData.at === 0) {
61
+ store.config.logger?.warn(
62
+ `Failed to undo at the beginning of timeline "${token.key}". There is nothing to undo.`,
63
+ )
64
+ return
65
+ }
66
+ timelineData.timeTraveling = true
67
67
 
68
- --timelineData.at
69
- const update = timelineData.history[timelineData.at]
70
- switch (update.type) {
71
- case `atom_update`: {
72
- const { key, oldValue } = update
73
- setState({ key, type: `atom` }, oldValue, store)
74
- break
75
- }
76
- case `selector_update`:
77
- case `transaction_update`: {
78
- for (const atomUpdate of update.atomUpdates) {
79
- const { key, oldValue } = atomUpdate
80
- setState({ key, type: `atom` }, oldValue, store)
81
- }
82
- break
83
- }
84
- }
85
- timelineData.timeTraveling = false
86
- store.config.logger?.info(
87
- `⏹️ "${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
88
- )
68
+ --timelineData.at
69
+ const update = timelineData.history[timelineData.at]
70
+ switch (update.type) {
71
+ case `atom_update`: {
72
+ const { key, oldValue } = update
73
+ setState({ key, type: `atom` }, oldValue, store)
74
+ break
75
+ }
76
+ case `selector_update`:
77
+ case `transaction_update`: {
78
+ for (const atomUpdate of update.atomUpdates) {
79
+ const { key, oldValue } = atomUpdate
80
+ setState({ key, type: `atom` }, oldValue, store)
81
+ }
82
+ break
83
+ }
84
+ }
85
+ timelineData.timeTraveling = false
86
+ store.config.logger?.info(
87
+ `⏹️ "${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`,
88
+ )
89
89
  }
@@ -1,164 +1,169 @@
1
1
  import { IMPLICIT, withdraw } from ".."
2
2
  import type {
3
- TimelineSelectorUpdate,
4
- Timeline,
5
- Store,
6
- TimelineTransactionUpdate,
7
- TimelineAtomUpdate,
3
+ TimelineSelectorUpdate,
4
+ Timeline,
5
+ Store,
6
+ TimelineTransactionUpdate,
7
+ TimelineAtomUpdate,
8
8
  } from ".."
9
9
  import type { AtomFamily, AtomToken, TimelineUpdate } from "../.."
10
10
 
11
11
  export const addAtomToTimeline = (
12
- atomToken: AtomToken<any>,
13
- atoms: (AtomFamily<any> | AtomToken<any>)[],
14
- tl: Timeline,
15
- store: Store = IMPLICIT.STORE
12
+ atomToken: AtomToken<any>,
13
+ atoms: (AtomFamily<any> | AtomToken<any>)[],
14
+ tl: Timeline,
15
+ store: Store = IMPLICIT.STORE,
16
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
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
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
- )
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
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)
53
+ if (tl.timeTraveling === false) {
54
+ if (tl.selectorTime && tl.selectorTime !== currentSelectorTime) {
55
+ const mostRecentUpdate: TimelineUpdate | undefined = tl.history.at(-1)
56
+ if (mostRecentUpdate === undefined) {
57
+ throw new Error(
58
+ `Timeline "${tl.key}" has a selectorTime, but no history. This is most likely a bug in AtomIO.`,
59
+ )
60
+ }
61
+ if (mostRecentUpdate.type === `selector_update`) {
62
+ tl.subject.next(mostRecentUpdate)
63
+ }
64
+ }
65
+ if (
66
+ currentTransactionKey &&
67
+ store.transactionStatus.phase === `applying`
68
+ ) {
69
+ const currentTransaction = withdraw(
70
+ { key: currentTransactionKey, type: `transaction` },
71
+ store,
72
+ )
73
+ if (currentTransaction === null) {
74
+ throw new Error(
75
+ `Transaction "${currentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${currentTransactionKey}".`,
76
+ )
77
+ }
78
+ if (tl.transactionKey !== currentTransactionKey) {
79
+ if (tl.transactionKey) {
80
+ store.config.logger?.error(
81
+ `Timeline "${tl.key}" was unable to resolve transaction "${tl.transactionKey}. This is probably a bug.`,
82
+ )
83
+ }
84
+ tl.transactionKey = currentTransactionKey
85
+ const subscription = currentTransaction.subject.subscribe((update) => {
86
+ if (tl.timeTraveling === false && currentTransactionTime) {
87
+ if (tl.at !== tl.history.length) {
88
+ tl.history.splice(tl.at)
89
+ }
90
+ const timelineTransactionUpdate: TimelineTransactionUpdate = {
91
+ type: `transaction_update`,
92
+ timestamp: currentTransactionTime,
93
+ ...update,
94
+ atomUpdates: update.atomUpdates.filter((atomUpdate) =>
95
+ atoms.some((atom) => atom.key === atomUpdate.key),
96
+ ),
97
+ }
98
+ tl.history.push(timelineTransactionUpdate)
99
+ tl.subject.next(timelineTransactionUpdate)
100
+ }
101
+ tl.at = tl.history.length
102
+ subscription.unsubscribe()
103
+ tl.transactionKey = null
104
+ store.config.logger?.info(
105
+ `⌛ timeline "${tl.key}" got a transaction_update "${update.key}"`,
106
+ )
107
+ })
108
+ }
109
+ } else if (currentSelectorKey && currentSelectorTime) {
110
+ if (currentSelectorTime !== tl.selectorTime) {
111
+ const newSelectorUpdate: TimelineSelectorUpdate = {
112
+ type: `selector_update`,
113
+ timestamp: currentSelectorTime,
114
+ key: currentSelectorKey,
115
+ atomUpdates: [],
116
+ }
117
+ newSelectorUpdate.atomUpdates.push({
118
+ key: atom.key,
119
+ type: `atom_update`,
120
+ ...update,
121
+ })
122
+ if (tl.at !== tl.history.length) {
123
+ tl.history.splice(tl.at)
124
+ }
125
+ tl.history.push(newSelectorUpdate)
121
126
 
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
- })
127
+ store.config.logger?.info(
128
+ `⌛ timeline "${tl.key}" got a selector_update "${currentSelectorKey}" with`,
129
+ newSelectorUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key),
130
+ )
131
+ tl.at = tl.history.length
132
+ tl.selectorTime = currentSelectorTime
133
+ } else {
134
+ const latestUpdate = tl.history.at(-1)
135
+ if (latestUpdate?.type === `selector_update`) {
136
+ latestUpdate.atomUpdates.push({
137
+ key: atom.key,
138
+ type: `atom_update`,
139
+ ...update,
140
+ })
141
+ store.config.logger?.info(
142
+ ` ⌛ timeline "${tl.key}" set selector_update "${currentSelectorKey}" to`,
143
+ latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key),
144
+ )
145
+ }
146
+ }
147
+ } else {
148
+ const timestamp = Date.now()
149
+ tl.selectorTime = null
150
+ if (tl.at !== tl.history.length) {
151
+ tl.history.splice(tl.at)
152
+ }
153
+ const atomUpdate: TimelineAtomUpdate = {
154
+ type: `atom_update`,
155
+ timestamp,
156
+ key: atom.key,
157
+ oldValue: update.oldValue,
158
+ newValue: update.newValue,
159
+ }
160
+ tl.history.push(atomUpdate)
161
+ tl.subject.next(atomUpdate)
162
+ store.config.logger?.info(
163
+ `⌛ timeline "${tl.key}" got a state_update to "${atom.key}"`,
164
+ )
165
+ tl.at = tl.history.length
166
+ }
167
+ }
168
+ })
164
169
  }