atom.io 0.5.0 → 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.
Files changed (87) hide show
  1. package/dist/index.d.mts +82 -66
  2. package/dist/index.d.ts +82 -66
  3. package/dist/index.js +482 -360
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +481 -360
  6. package/dist/index.mjs.map +1 -1
  7. package/json/dist/index.js.map +1 -1
  8. package/json/dist/index.mjs.map +1 -1
  9. package/package.json +12 -5
  10. package/react/dist/index.d.mts +18 -11
  11. package/react/dist/index.d.ts +18 -11
  12. package/react/dist/index.js +45 -21
  13. package/react/dist/index.js.map +1 -1
  14. package/react/dist/index.mjs +31 -21
  15. package/react/dist/index.mjs.map +1 -1
  16. package/react-devtools/dist/index.d.mts +4 -4
  17. package/react-devtools/dist/index.d.ts +4 -4
  18. package/react-devtools/dist/index.js.map +1 -1
  19. package/react-devtools/dist/index.mjs.map +1 -1
  20. package/realtime/dist/index.d.mts +3 -1
  21. package/realtime/dist/index.d.ts +3 -1
  22. package/realtime/dist/index.js +23 -0
  23. package/realtime/dist/index.js.map +1 -1
  24. package/realtime/dist/index.mjs +22 -0
  25. package/realtime/dist/index.mjs.map +1 -1
  26. package/realtime-react/dist/index.d.mts +45 -0
  27. package/realtime-react/dist/index.d.ts +45 -0
  28. package/realtime-react/dist/index.js +213 -0
  29. package/realtime-react/dist/index.js.map +1 -0
  30. package/realtime-react/dist/index.mjs +168 -0
  31. package/realtime-react/dist/index.mjs.map +1 -0
  32. package/realtime-react/package.json +15 -0
  33. package/src/index.ts +0 -6
  34. package/src/internal/get.ts +17 -3
  35. package/src/internal/index.ts +2 -0
  36. package/src/internal/meta/meta-state.ts +1 -1
  37. package/src/internal/operation.ts +3 -1
  38. package/src/internal/selector/create-read-write-selector.ts +62 -0
  39. package/src/internal/selector/create-readonly-selector.ts +52 -0
  40. package/src/internal/selector/index.ts +4 -0
  41. package/src/internal/selector/lookup-selector-sources.ts +16 -0
  42. package/src/internal/selector/register-selector.ts +57 -0
  43. package/src/internal/selector/trace-selector-atoms.ts +43 -0
  44. package/src/internal/selector/update-selector-atoms.ts +33 -0
  45. package/src/internal/selector-internal.ts +9 -207
  46. package/src/internal/store.ts +43 -16
  47. package/src/internal/subscribe-internal.ts +1 -1
  48. package/src/internal/time-travel-internal.ts +7 -7
  49. package/src/internal/timeline/add-atom-to-timeline.ts +164 -0
  50. package/src/internal/timeline/index.ts +1 -0
  51. package/src/internal/timeline-internal.ts +37 -156
  52. package/src/internal/transaction/abort-transaction.ts +12 -0
  53. package/src/internal/transaction/apply-transaction.ts +54 -0
  54. package/src/internal/transaction/build-transaction.ts +33 -0
  55. package/src/internal/transaction/index.ts +25 -0
  56. package/src/internal/transaction/redo-transaction.ts +23 -0
  57. package/src/internal/transaction/undo-transaction.ts +23 -0
  58. package/src/internal/transaction-internal.ts +14 -146
  59. package/src/react/index.ts +2 -46
  60. package/src/react/store-context.tsx +14 -0
  61. package/src/react/store-hooks.ts +48 -0
  62. package/src/react-devtools/AtomIODevtools.tsx +1 -1
  63. package/src/react-explorer/AtomIOExplorer.tsx +2 -2
  64. package/src/react-explorer/space-states.ts +2 -2
  65. package/src/realtime/README.md +33 -0
  66. package/src/realtime/hook-composition/index.ts +1 -0
  67. package/src/realtime/hook-composition/receive-state.ts +29 -0
  68. package/src/realtime/hook-composition/receive-transaction.ts +2 -3
  69. package/src/realtime-react/index.ts +3 -0
  70. package/src/realtime-react/realtime-context.tsx +31 -0
  71. package/src/realtime-react/realtime-hooks.ts +39 -0
  72. package/src/realtime-react/realtime-state.ts +10 -0
  73. package/src/realtime-react/use-pull-family-member.ts +27 -0
  74. package/src/realtime-react/use-pull-family.ts +25 -0
  75. package/src/realtime-react/use-pull.ts +23 -0
  76. package/src/realtime-react/use-push.ts +26 -0
  77. package/src/realtime-react/use-server-action.ts +34 -0
  78. package/src/silo.ts +12 -4
  79. package/src/subscribe.ts +30 -2
  80. package/src/timeline.ts +10 -0
  81. package/src/transaction.ts +15 -10
  82. package/src/realtime-client/hook-composition/compose-realtime-hooks.ts +0 -62
  83. package/src/realtime-client/hook-composition/realtime-client-family-member.ts +0 -28
  84. package/src/realtime-client/hook-composition/realtime-client-family.ts +0 -26
  85. package/src/realtime-client/hook-composition/realtime-client-single.ts +0 -24
  86. package/src/realtime-client/hook-composition/realtime-client-transaction.ts +0 -35
  87. 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
- target,
9
- cacheValue,
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
- const readonlySelector: ReadonlySelector<T> = {
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.subject.selectorCreation.next(token)
258
- return token
60
+ return createReadWriteSelector(options, family, store, core)
259
61
  }
@@ -13,7 +13,7 @@ import type {
13
13
  Selector,
14
14
  TransactionStatus,
15
15
  Timeline,
16
- TimelineData,
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
- timelineStore: Hamt<TimelineData, string>
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.timelineStore.get(token.key)
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.timelineStore.get(token.key)
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"