atom.io 0.4.1 → 0.6.0

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