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,187 +1,66 @@
1
1
  import HAMT from "hamt_plus"
2
+ import * as Rx from "rxjs"
2
3
 
3
4
  import type { ƒn } from "~/packages/anvl/src/function"
4
5
 
5
- import type { KeyedStateUpdate, TransactionUpdate, Store } from "."
6
- import { target, IMPLICIT, withdraw } from "."
7
- import type { AtomToken, TimelineOptions, TimelineToken } from ".."
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 ".."
8
16
 
9
- export type Timeline = {
17
+ export type TimelineAtomUpdate = StateUpdate<unknown> & {
10
18
  key: string
11
- type: `timeline`
12
- next: () => void
13
- prev: () => void
14
- }
15
-
16
- export type TimelineAtomUpdate = KeyedStateUpdate<unknown> & {
17
19
  type: `atom_update`
20
+ timestamp: number
18
21
  }
19
22
  export type TimelineSelectorUpdate = {
20
23
  key: string
21
24
  type: `selector_update`
22
- atomUpdates: TimelineAtomUpdate[]
25
+ timestamp: number
26
+ atomUpdates: Omit<TimelineAtomUpdate, `timestamp`>[]
23
27
  }
24
28
  export type TimelineTransactionUpdate = TransactionUpdate<ƒn> & {
29
+ key: string
25
30
  type: `transaction_update`
31
+ timestamp: number
26
32
  }
27
33
 
28
- export type TimelineData = {
34
+ export type Timeline = {
35
+ key: string
29
36
  at: number
30
37
  timeTraveling: boolean
31
- history: (
32
- | TimelineAtomUpdate
33
- | TimelineSelectorUpdate
34
- | TimelineTransactionUpdate
35
- )[]
38
+ history: TimelineUpdate[]
36
39
  selectorTime: number | null
37
40
  transactionKey: string | null
41
+ install: (store: Store) => void
42
+ subject: Rx.Subject<
43
+ TimelineAtomUpdate | TimelineSelectorUpdate | TimelineTransactionUpdate
44
+ >
38
45
  }
39
46
 
40
47
  export function timeline__INTERNAL(
41
48
  options: TimelineOptions,
42
- store: Store = IMPLICIT.STORE
49
+ store: Store = IMPLICIT.STORE,
50
+ data: Timeline | null = null
43
51
  ): TimelineToken {
44
- const timelineData: TimelineData = {
52
+ const tl: Timeline = {
53
+ key: options.key,
45
54
  at: 0,
46
55
  timeTraveling: false,
47
- history: [],
48
56
  selectorTime: null,
49
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(),
50
62
  }
51
63
 
52
- const subscribeToAtom = (token: AtomToken<any>) => {
53
- const state = withdraw(token, store)
54
- if (state === null) {
55
- throw new Error(
56
- `Cannot subscribe to atom "${token.key}" because it has not been initialized in store "${store.config.name}"`
57
- )
58
- }
59
- state.subject.subscribe((update) => {
60
- const storeCurrentSelectorKey =
61
- store.operation.open && store.operation.token.type === `selector`
62
- ? store.operation.token.key
63
- : null
64
- const storeCurrentSelectorTime =
65
- store.operation.open && store.operation.token.type === `selector`
66
- ? store.operation.time
67
- : null
68
-
69
- const storeCurrentTransactionKey =
70
- store.transactionStatus.phase === `applying`
71
- ? store.transactionStatus.key
72
- : null
73
- store.config.logger?.info(
74
- `⏳ timeline "${options.key}" saw atom "${token.key}" go (`,
75
- update.oldValue,
76
- `->`,
77
- update.newValue,
78
- storeCurrentTransactionKey
79
- ? `) in transaction "${storeCurrentTransactionKey}"`
80
- : storeCurrentSelectorKey
81
- ? `) in selector "${storeCurrentSelectorKey}"`
82
- : `)`
83
- )
84
-
85
- if (
86
- storeCurrentTransactionKey &&
87
- store.transactionStatus.phase === `applying`
88
- ) {
89
- const currentTransaction = withdraw(
90
- { key: storeCurrentTransactionKey, type: `transaction` },
91
- store
92
- )
93
- if (currentTransaction === null) {
94
- throw new Error(
95
- `Transaction "${storeCurrentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${storeCurrentTransactionKey}".`
96
- )
97
- }
98
- if (timelineData.transactionKey !== storeCurrentTransactionKey) {
99
- if (timelineData.transactionKey) {
100
- store.config.logger?.error(
101
- `Timeline "${options.key}" was unable to resolve transaction "${timelineData.transactionKey}. This is probably a bug.`
102
- )
103
- }
104
- timelineData.transactionKey = storeCurrentTransactionKey
105
- const subscription = currentTransaction.subject.subscribe((update) => {
106
- if (timelineData.timeTraveling === false) {
107
- if (timelineData.at !== timelineData.history.length) {
108
- timelineData.history.splice(timelineData.at)
109
- }
110
- timelineData.history.push({
111
- type: `transaction_update`,
112
- ...update,
113
- atomUpdates: update.atomUpdates.filter((atomUpdate) =>
114
- options.atoms.some((atom) => atom.key === atomUpdate.key)
115
- ),
116
- })
117
- }
118
- timelineData.at = timelineData.history.length
119
- subscription.unsubscribe()
120
- timelineData.transactionKey = null
121
- store.config.logger?.info(
122
- `⌛ timeline "${options.key}" got a transaction_update "${update.key}"`
123
- )
124
- })
125
- }
126
- } else if (storeCurrentSelectorKey) {
127
- if (timelineData.timeTraveling === false) {
128
- if (storeCurrentSelectorTime !== timelineData.selectorTime) {
129
- const newSelectorUpdate: TimelineSelectorUpdate = {
130
- type: `selector_update`,
131
- key: storeCurrentSelectorKey,
132
- atomUpdates: [],
133
- }
134
- newSelectorUpdate.atomUpdates.push({
135
- key: token.key,
136
- type: `atom_update`,
137
- ...update,
138
- })
139
- if (timelineData.at !== timelineData.history.length) {
140
- timelineData.history.splice(timelineData.at)
141
- }
142
- timelineData.history.push(newSelectorUpdate)
143
-
144
- store.config.logger?.info(
145
- `⌛ timeline "${options.key}" got a selector_update "${storeCurrentSelectorKey}" with`,
146
- newSelectorUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
147
- )
148
- timelineData.at = timelineData.history.length
149
- timelineData.selectorTime = storeCurrentSelectorTime
150
- } else {
151
- const latestUpdate = timelineData.history.at(-1)
152
- if (latestUpdate?.type === `selector_update`) {
153
- latestUpdate.atomUpdates.push({
154
- key: token.key,
155
- type: `atom_update`,
156
- ...update,
157
- })
158
- store.config.logger?.info(
159
- ` ⌛ timeline "${options.key}" set selector_update "${storeCurrentSelectorKey}" to`,
160
- latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key)
161
- )
162
- }
163
- }
164
- }
165
- } else {
166
- if (timelineData.timeTraveling === false) {
167
- timelineData.selectorTime = null
168
- if (timelineData.at !== timelineData.history.length) {
169
- timelineData.history.splice(timelineData.at)
170
- }
171
- timelineData.history.push({
172
- type: `atom_update`,
173
- key: token.key,
174
- oldValue: update.oldValue,
175
- newValue: update.newValue,
176
- })
177
- store.config.logger?.info(
178
- `⌛ timeline "${options.key}" got a state_update to "${token.key}"`
179
- )
180
- timelineData.at = timelineData.history.length
181
- }
182
- }
183
- })
184
- }
185
64
  const core = target(store)
186
65
  for (const tokenOrFamily of options.atoms) {
187
66
  const timelineKey = core.timelineAtoms.getRelatedId(tokenOrFamily.key)
@@ -193,7 +72,9 @@ export function timeline__INTERNAL(
193
72
  }
194
73
  if (tokenOrFamily.type === `atom_family`) {
195
74
  const family = tokenOrFamily
196
- family.subject.subscribe((token) => subscribeToAtom(token))
75
+ family.subject.subscribe((token) =>
76
+ addAtomToTimeline(token, options.atoms, tl, store)
77
+ )
197
78
  } else {
198
79
  const token = tokenOrFamily
199
80
  if (`family` in token && token.family) {
@@ -207,7 +88,7 @@ export function timeline__INTERNAL(
207
88
  continue
208
89
  }
209
90
  }
210
- subscribeToAtom(token)
91
+ addAtomToTimeline(token, options.atoms, tl, store)
211
92
  }
212
93
  core.timelineAtoms = core.timelineAtoms.set({
213
94
  atomKey: tokenOrFamily.key,
@@ -215,7 +96,7 @@ export function timeline__INTERNAL(
215
96
  })
216
97
  }
217
98
 
218
- store.timelineStore = HAMT.set(options.key, timelineData, store.timelineStore)
99
+ store.timelines = HAMT.set(options.key, tl, store.timelines)
219
100
  const token: TimelineToken = {
220
101
  key: options.key,
221
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
+ }
@@ -4,155 +4,22 @@ import * as Rx from "rxjs"
4
4
  import type { ƒn } from "~/packages/anvl/src/function"
5
5
 
6
6
  import type { Store, StoreCore } from "."
7
- import { deposit, withdraw, IMPLICIT } from "."
7
+ import {
8
+ abortTransaction,
9
+ applyTransaction,
10
+ buildTransaction,
11
+ deposit,
12
+ IMPLICIT,
13
+ } from "."
14
+ import type { TransactionOptions, TransactionToken, TransactionUpdate } from ".."
8
15
  import { getState, setState } from ".."
9
- import type {
10
- AtomToken,
11
- StateUpdate,
12
- Transaction,
13
- TransactionOptions,
14
- TransactionToken,
15
- } from ".."
16
16
 
17
- export const TRANSACTION_PHASES = [`idle`, `building`, `applying`] as const
18
- export type TransactionPhase = (typeof TRANSACTION_PHASES)[number]
19
-
20
- export type KeyedStateUpdate<T> = StateUpdate<T> & {
21
- key: string
22
- }
23
- export type TransactionUpdate<ƒ extends ƒn> = {
17
+ export type Transaction<ƒ extends ƒn> = {
24
18
  key: string
25
- atomUpdates: KeyedStateUpdate<unknown>[]
26
- params: Parameters<ƒ>
27
- output: ReturnType<ƒ>
28
- }
29
-
30
- export type TransactionUpdateInProgress<ƒ extends ƒn> = TransactionUpdate<ƒ> & {
31
- phase: `applying` | `building`
32
- core: StoreCore
33
- }
34
- export type TransactionIdle = {
35
- phase: `idle`
36
- }
37
- export type TransactionStatus<ƒ extends ƒn> =
38
- | TransactionIdle
39
- | TransactionUpdateInProgress<ƒ>
40
-
41
- export const buildTransaction = (
42
- key: string,
43
- params: any[],
44
- store: Store
45
- ): void => {
46
- store.transactionStatus = {
47
- key,
48
- phase: `building`,
49
- core: {
50
- atoms: store.atoms,
51
- atomsThatAreDefault: store.atomsThatAreDefault,
52
- operation: { open: false },
53
- readonlySelectors: store.readonlySelectors,
54
- timelines: store.timelines,
55
- timelineAtoms: store.timelineAtoms,
56
- transactions: store.transactions,
57
- selectorAtoms: store.selectorAtoms,
58
- selectorGraph: store.selectorGraph,
59
- selectors: store.selectors,
60
- valueMap: store.valueMap,
61
- },
62
- atomUpdates: [],
63
- params,
64
- output: undefined,
65
- }
66
- store.config.logger?.info(`🛫`, `transaction "${key}" started`)
67
- }
68
- export const applyTransaction = <ƒ extends ƒn>(
69
- output: ReturnType<ƒ>,
70
- store: Store
71
- ): void => {
72
- if (store.transactionStatus.phase !== `building`) {
73
- store.config.logger?.warn(
74
- `abortTransaction called outside of a transaction. This is probably a bug.`
75
- )
76
- return
77
- }
78
- store.config.logger?.info(
79
- `🛃 apply transaction "${store.transactionStatus.key}"`
80
- )
81
- store.transactionStatus.phase = `applying`
82
- store.transactionStatus.output = output
83
- const { atomUpdates } = store.transactionStatus
84
-
85
- for (const { key, newValue } of atomUpdates) {
86
- const token: AtomToken<unknown> = { key, type: `atom` }
87
- if (!HAMT.has(token.key, store.valueMap)) {
88
- const newAtom = HAMT.get(token.key, store.transactionStatus.core.atoms)
89
- store.atoms = HAMT.set(newAtom.key, newAtom, store.atoms)
90
- store.valueMap = HAMT.set(newAtom.key, newAtom.default, store.valueMap)
91
- store.config.logger?.info(`🔧`, `add atom "${newAtom.key}"`)
92
- }
93
- setState(token, newValue, store)
94
- }
95
- const myTransaction = withdraw<ƒ>(
96
- { key: store.transactionStatus.key, type: `transaction` },
97
- store
98
- )
99
- if (myTransaction === null) {
100
- throw new Error(
101
- `Transaction "${store.transactionStatus.key}" not found. Absurd. How is this running?`
102
- )
103
- }
104
- myTransaction.subject.next({
105
- key: store.transactionStatus.key,
106
- atomUpdates,
107
- output,
108
- params: store.transactionStatus.params as Parameters<ƒ>,
109
- })
110
- store.transactionStatus = { phase: `idle` }
111
- store.config.logger?.info(`🛬`, `transaction done`)
112
- }
113
- export const undoTransactionUpdate = <ƒ extends ƒn>(
114
- update: TransactionUpdate<ƒ>,
115
- store: Store
116
- ): void => {
117
- store.config.logger?.info(` ⏮ undo transaction "${update.key}" (undo)`)
118
- for (const { key, oldValue } of update.atomUpdates) {
119
- const token: AtomToken<unknown> = { key, type: `atom` }
120
- const state = withdraw(token, store)
121
- if (state === null) {
122
- throw new Error(
123
- `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
124
- )
125
- }
126
-
127
- setState(state, oldValue, store)
128
- }
129
- }
130
- export const redoTransactionUpdate = <ƒ extends ƒn>(
131
- update: TransactionUpdate<ƒ>,
132
- store: Store
133
- ): void => {
134
- store.config.logger?.info(` ⏭ redo transaction "${update.key}" (redo)`)
135
- for (const { key, newValue } of update.atomUpdates) {
136
- const token: AtomToken<unknown> = { key, type: `atom` }
137
- const state = withdraw(token, store)
138
- if (state === null) {
139
- throw new Error(
140
- `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
141
- )
142
- }
143
- setState(state, newValue, store)
144
- }
145
- }
146
-
147
- export const abortTransaction = (store: Store): void => {
148
- if (store.transactionStatus.phase === `idle`) {
149
- store.config.logger?.warn(
150
- `abortTransaction called outside of a transaction. This is probably a bug.`
151
- )
152
- return
153
- }
154
- store.transactionStatus = { phase: `idle` }
155
- store.config.logger?.info(`🪂`, `transaction fail`)
19
+ type: `transaction`
20
+ install: (store: Store) => void
21
+ subject: Rx.Subject<TransactionUpdate<ƒ>>
22
+ run: (...parameters: Parameters<ƒ>) => ReturnType<ƒ>
156
23
  }
157
24
 
158
25
  export function transaction__INTERNAL<ƒ extends ƒn>(
@@ -180,6 +47,7 @@ export function transaction__INTERNAL<ƒ extends ƒn>(
180
47
  throw thrown
181
48
  }
182
49
  },
50
+ install: (store) => transaction__INTERNAL(options, store),
183
51
  subject: new Rx.Subject(),
184
52
  }
185
53
  const core = target(store)
@@ -1,46 +1,2 @@
1
- import { useSyncExternalStore } from "react"
2
-
3
- import { subscribe, setState, __INTERNAL__, getState } from "atom.io"
4
- import type { ReadonlySelectorToken, StateToken } from "atom.io"
5
-
6
- import type { Modifier } from "~/packages/anvl/src/function"
7
-
8
- export type StoreHooks = {
9
- useI: <T>(token: StateToken<T>) => (next: Modifier<T> | T) => void
10
- useO: <T>(token: ReadonlySelectorToken<T> | StateToken<T>) => T
11
- useIO: <T>(token: StateToken<T>) => [T, (next: Modifier<T> | T) => void]
12
- }
13
-
14
- export const composeStoreHooks = (
15
- store: __INTERNAL__.Store = __INTERNAL__.IMPLICIT.STORE
16
- ): StoreHooks => {
17
- function useI<T>(token: StateToken<T>): (next: Modifier<T> | T) => void {
18
- const updateState = (next: Modifier<T> | T) => setState(token, next, store)
19
- return updateState
20
- }
21
-
22
- function useO<T>(token: ReadonlySelectorToken<T> | StateToken<T>): T {
23
- return useSyncExternalStore<T>(
24
- (observe) => subscribe(token, observe, store),
25
- () => getState(token, store)
26
- )
27
- }
28
-
29
- function useIO<T>(token: StateToken<T>): [T, (next: Modifier<T> | T) => void] {
30
- return [useO(token), useI(token)]
31
- }
32
-
33
- return { useI, useO, useIO }
34
- }
35
-
36
- export const { useI, useO, useIO } = composeStoreHooks()
37
-
38
- export function useStore<T>(
39
- token: StateToken<T>
40
- ): [T, (next: Modifier<T> | T) => void]
41
- export function useStore<T>(token: ReadonlySelectorToken<T>): T
42
- export function useStore<T>(
43
- token: ReadonlySelectorToken<T> | StateToken<T>
44
- ): T | [T, (next: Modifier<T> | T) => void] {
45
- return token.type === `readonly_selector` ? useO(token) : useIO(token)
46
- }
1
+ export * from "./store-context"
2
+ export * from "./store-hooks"
@@ -0,0 +1,14 @@
1
+ import * as React from "react"
2
+
3
+ import * as AtomIO from "atom.io"
4
+
5
+ export const StoreContext = React.createContext<AtomIO.Store>(
6
+ AtomIO.__INTERNAL__.IMPLICIT.STORE
7
+ )
8
+
9
+ export const StoreProvider: React.FC<{
10
+ children: React.ReactNode
11
+ store?: AtomIO.Store
12
+ }> = ({ children, store = AtomIO.__INTERNAL__.IMPLICIT.STORE }) => (
13
+ <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
14
+ )