atom.io 0.6.9 → 0.8.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 (170) hide show
  1. package/README.md +21 -2
  2. package/dist/index.d.mts +34 -421
  3. package/dist/index.d.ts +34 -421
  4. package/dist/index.js +248 -23
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +209 -4
  7. package/dist/index.mjs.map +1 -1
  8. package/internal/dist/index.d.mts +364 -0
  9. package/internal/dist/index.d.ts +364 -0
  10. package/internal/dist/index.js +1906 -0
  11. package/internal/dist/index.js.map +1 -0
  12. package/internal/dist/index.mjs +1830 -0
  13. package/internal/dist/index.mjs.map +1 -0
  14. package/internal/package.json +15 -0
  15. package/internal/src/atom/create-atom.ts +75 -0
  16. package/internal/src/atom/delete-atom.ts +10 -0
  17. package/internal/src/atom/index.ts +3 -0
  18. package/internal/src/atom/is-default.ts +37 -0
  19. package/internal/src/caching.ts +38 -0
  20. package/internal/src/families/create-atom-family.ts +59 -0
  21. package/internal/src/families/create-readonly-selector-family.ts +45 -0
  22. package/internal/src/families/create-selector-family.ts +67 -0
  23. package/internal/src/families/index.ts +3 -0
  24. package/internal/src/future.ts +39 -0
  25. package/internal/src/get-state-internal.ts +23 -0
  26. package/internal/src/index.ts +14 -0
  27. package/internal/src/mutable/create-mutable-atom-family.ts +25 -0
  28. package/internal/src/mutable/create-mutable-atom.ts +49 -0
  29. package/internal/src/mutable/get-json-token.ts +22 -0
  30. package/internal/src/mutable/get-update-token.ts +20 -0
  31. package/internal/src/mutable/index.ts +17 -0
  32. package/internal/src/mutable/is-atom-token-mutable.ts +7 -0
  33. package/internal/src/mutable/tracker-family.ts +61 -0
  34. package/internal/src/mutable/tracker.ts +164 -0
  35. package/internal/src/mutable/transceiver.ts +110 -0
  36. package/internal/src/operation.ts +68 -0
  37. package/internal/src/selector/create-read-write-selector.ts +65 -0
  38. package/internal/src/selector/create-readonly-selector.ts +49 -0
  39. package/internal/src/selector/create-selector.ts +65 -0
  40. package/internal/src/selector/index.ts +5 -0
  41. package/internal/src/selector/lookup-selector-sources.ts +20 -0
  42. package/internal/src/selector/register-selector.ts +61 -0
  43. package/internal/src/selector/trace-selector-atoms.ts +45 -0
  44. package/internal/src/selector/update-selector-atoms.ts +34 -0
  45. package/internal/src/set-state/become.ts +10 -0
  46. package/internal/src/set-state/copy-mutable-if-needed.ts +23 -0
  47. package/internal/src/set-state/copy-mutable-in-transaction.ts +59 -0
  48. package/internal/src/set-state/copy-mutable-into-new-store.ts +34 -0
  49. package/internal/src/set-state/emit-update.ts +23 -0
  50. package/internal/src/set-state/evict-downstream.ts +39 -0
  51. package/internal/src/set-state/index.ts +2 -0
  52. package/internal/src/set-state/set-atom-state.ts +38 -0
  53. package/internal/src/set-state/set-selector-state.ts +19 -0
  54. package/internal/src/set-state/set-state-internal.ts +18 -0
  55. package/internal/src/set-state/stow-update.ts +42 -0
  56. package/internal/src/store/deposit.ts +43 -0
  57. package/internal/src/store/index.ts +5 -0
  58. package/internal/src/store/lookup.ts +26 -0
  59. package/internal/src/store/store.ts +154 -0
  60. package/internal/src/store/withdraw-new-family-member.ts +53 -0
  61. package/internal/src/store/withdraw.ts +113 -0
  62. package/internal/src/subject.ts +21 -0
  63. package/internal/src/subscribe/index.ts +1 -0
  64. package/internal/src/subscribe/recall-state.ts +19 -0
  65. package/internal/src/subscribe/subscribe-to-root-atoms.ts +47 -0
  66. package/internal/src/timeline/add-atom-to-timeline.ts +189 -0
  67. package/internal/src/timeline/index.ts +3 -0
  68. package/internal/src/timeline/time-travel-internal.ts +91 -0
  69. package/internal/src/timeline/timeline-internal.ts +115 -0
  70. package/internal/src/transaction/abort-transaction.ts +12 -0
  71. package/internal/src/transaction/apply-transaction.ts +64 -0
  72. package/internal/src/transaction/build-transaction.ts +39 -0
  73. package/internal/src/transaction/index.ts +26 -0
  74. package/internal/src/transaction/redo-transaction.ts +22 -0
  75. package/internal/src/transaction/transaction-internal.ts +64 -0
  76. package/internal/src/transaction/undo-transaction.ts +22 -0
  77. package/introspection/dist/index.d.mts +3 -197
  78. package/introspection/dist/index.d.ts +3 -197
  79. package/introspection/dist/index.js +329 -4
  80. package/introspection/dist/index.js.map +1 -1
  81. package/introspection/dist/index.mjs +310 -4
  82. package/introspection/dist/index.mjs.map +1 -1
  83. package/introspection/src/attach-atom-index.ts +84 -0
  84. package/introspection/src/attach-introspection-states.ts +38 -0
  85. package/introspection/src/attach-selector-index.ts +90 -0
  86. package/introspection/src/attach-timeline-family.ts +59 -0
  87. package/introspection/src/attach-timeline-index.ts +38 -0
  88. package/introspection/src/attach-transaction-index.ts +40 -0
  89. package/introspection/src/attach-transaction-logs.ts +43 -0
  90. package/introspection/src/index.ts +20 -0
  91. package/json/dist/index.d.mts +10 -2
  92. package/json/dist/index.d.ts +10 -2
  93. package/json/dist/index.js +83 -26
  94. package/json/dist/index.js.map +1 -1
  95. package/json/dist/index.mjs +74 -3
  96. package/json/dist/index.mjs.map +1 -1
  97. package/json/src/index.ts +5 -0
  98. package/json/src/select-json-family.ts +35 -0
  99. package/json/src/select-json.ts +22 -0
  100. package/package.json +103 -63
  101. package/react/dist/index.d.mts +9 -17
  102. package/react/dist/index.d.ts +9 -17
  103. package/react/dist/index.js +44 -27
  104. package/react/dist/index.js.map +1 -1
  105. package/react/dist/index.mjs +24 -4
  106. package/react/dist/index.mjs.map +1 -1
  107. package/react/src/index.ts +2 -0
  108. package/react/src/store-context.tsx +12 -0
  109. package/react/src/store-hooks.ts +36 -0
  110. package/react-devtools/dist/index.css +50 -1
  111. package/react-devtools/dist/index.css.map +1 -1
  112. package/react-devtools/dist/index.d.mts +104 -71
  113. package/react-devtools/dist/index.d.ts +104 -71
  114. package/react-devtools/dist/index.js +2821 -45
  115. package/react-devtools/dist/index.js.map +1 -1
  116. package/react-devtools/dist/index.mjs +2790 -11
  117. package/react-devtools/dist/index.mjs.map +1 -1
  118. package/react-devtools/src/AtomIODevtools.tsx +109 -0
  119. package/react-devtools/src/Button.tsx +23 -0
  120. package/react-devtools/src/StateEditor.tsx +75 -0
  121. package/react-devtools/src/StateIndex.tsx +159 -0
  122. package/react-devtools/src/TimelineIndex.tsx +88 -0
  123. package/react-devtools/src/TransactionIndex.tsx +70 -0
  124. package/react-devtools/src/Updates.tsx +150 -0
  125. package/react-devtools/src/devtools.scss +310 -0
  126. package/react-devtools/src/index.ts +72 -0
  127. package/realtime-react/dist/index.d.mts +8 -22
  128. package/realtime-react/dist/index.d.ts +8 -22
  129. package/realtime-react/dist/index.js +87 -32
  130. package/realtime-react/dist/index.js.map +1 -1
  131. package/realtime-react/dist/index.mjs +62 -6
  132. package/realtime-react/dist/index.mjs.map +1 -1
  133. package/realtime-react/src/index.ts +7 -0
  134. package/realtime-react/src/realtime-context.tsx +29 -0
  135. package/realtime-react/src/use-pull-family-member.ts +15 -0
  136. package/realtime-react/src/use-pull-mutable-family-member.ts +20 -0
  137. package/realtime-react/src/use-pull-mutable.ts +17 -0
  138. package/realtime-react/src/use-pull.ts +15 -0
  139. package/realtime-react/src/use-push.ts +19 -0
  140. package/realtime-react/src/use-server-action.ts +18 -0
  141. package/realtime-testing/dist/index.d.mts +49 -0
  142. package/realtime-testing/dist/index.d.ts +49 -0
  143. package/realtime-testing/dist/index.js +147 -0
  144. package/realtime-testing/dist/index.js.map +1 -0
  145. package/realtime-testing/dist/index.mjs +116 -0
  146. package/realtime-testing/dist/index.mjs.map +1 -0
  147. package/realtime-testing/src/index.ts +1 -0
  148. package/realtime-testing/src/setup-realtime-test.tsx +161 -0
  149. package/src/atom.ts +64 -9
  150. package/src/index.ts +29 -31
  151. package/src/logger.ts +3 -3
  152. package/src/selector.ts +3 -3
  153. package/src/silo.ts +29 -20
  154. package/src/subscribe.ts +3 -3
  155. package/src/timeline.ts +2 -2
  156. package/transceivers/set-rtx/dist/index.d.mts +39 -0
  157. package/transceivers/set-rtx/dist/index.d.ts +39 -0
  158. package/transceivers/set-rtx/dist/index.js +213 -0
  159. package/transceivers/set-rtx/dist/index.js.map +1 -0
  160. package/transceivers/set-rtx/dist/index.mjs +211 -0
  161. package/transceivers/set-rtx/dist/index.mjs.map +1 -0
  162. package/{realtime → transceivers/set-rtx}/package.json +1 -1
  163. package/transceivers/set-rtx/src/index.ts +1 -0
  164. package/transceivers/set-rtx/src/set-rtx.ts +242 -0
  165. package/realtime/dist/index.d.mts +0 -23
  166. package/realtime/dist/index.d.ts +0 -23
  167. package/realtime/dist/index.js +0 -32
  168. package/realtime/dist/index.js.map +0 -1
  169. package/realtime/dist/index.mjs +0 -7
  170. package/realtime/dist/index.mjs.map +0 -1
@@ -0,0 +1,18 @@
1
+ import type { Atom } from "../atom"
2
+ import type { Selector } from "../selector"
3
+ import type { Store } from "../store"
4
+ import { IMPLICIT } from "../store"
5
+ import { setAtomState } from "./set-atom-state"
6
+ import { setSelectorState } from "./set-selector-state"
7
+
8
+ export const setState__INTERNAL = <T>(
9
+ state: Atom<T> | Selector<T>,
10
+ value: T | ((oldValue: T) => T),
11
+ store: Store = IMPLICIT.STORE,
12
+ ): void => {
13
+ if (`set` in state) {
14
+ setSelectorState(state, value, store)
15
+ } else {
16
+ setAtomState(state, value, store)
17
+ }
18
+ }
@@ -0,0 +1,42 @@
1
+ import type { KeyedStateUpdate, StateUpdate } from "atom.io"
2
+
3
+ import type { Atom } from "../atom"
4
+ import { isTransceiver } from "../mutable"
5
+ import type { Store } from "../store"
6
+
7
+ function shouldUpdateBeStowed(key: string, update: StateUpdate<any>): boolean {
8
+ // do not stow updates that aren't json, unless they're not equal by reference
9
+ if (isTransceiver(update.newValue)) {
10
+ return false
11
+ }
12
+ // do not stow updates where the key contains 👁‍🗨
13
+ if (key.includes(`👁‍🗨`)) {
14
+ return false
15
+ }
16
+ return true
17
+ }
18
+
19
+ export const stowUpdate = <T>(
20
+ state: Atom<T>,
21
+ update: StateUpdate<T>,
22
+ store: Store,
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
+ const shouldStow = shouldUpdateBeStowed(key, update)
33
+ if (!shouldStow) {
34
+ return
35
+ }
36
+ const atomUpdate: KeyedStateUpdate<T> = { key, ...update }
37
+ if (state.family) {
38
+ atomUpdate.family = state.family
39
+ }
40
+ store.transactionStatus.atomUpdates.push(atomUpdate)
41
+ logger?.info(`📝 ${key} stowed (`, update.oldValue, `->`, update.newValue, `)`)
42
+ }
@@ -0,0 +1,43 @@
1
+ import type {
2
+ AtomToken,
3
+ ReadonlySelectorToken,
4
+ SelectorToken,
5
+ StateToken,
6
+ TransactionToken,
7
+ ƒn,
8
+ } from "atom.io"
9
+
10
+ import type { Atom } from "../atom"
11
+ import type { ReadonlySelector, Selector } from "../selector"
12
+ import type { Transaction } from "../transaction"
13
+
14
+ export function deposit<T>(state: Atom<T>): AtomToken<T>
15
+ export function deposit<T>(state: Selector<T>): SelectorToken<T>
16
+ export function deposit<T>(state: Atom<T> | Selector<T>): StateToken<T>
17
+ export function deposit<T>(state: ReadonlySelector<T>): ReadonlySelectorToken<T>
18
+ export function deposit<T>(
19
+ state: Transaction<T extends ƒn ? T : never>,
20
+ ): TransactionToken<T>
21
+ export function deposit<T>(
22
+ state: Atom<T> | ReadonlySelector<T> | Selector<T>,
23
+ ): ReadonlySelectorToken<T> | StateToken<T>
24
+ export function deposit<T>(
25
+ state:
26
+ | Atom<T>
27
+ | ReadonlySelector<T>
28
+ | Selector<T>
29
+ | Transaction<T extends ƒn ? T : never>,
30
+ ):
31
+ | AtomToken<T>
32
+ | ReadonlySelectorToken<T>
33
+ | SelectorToken<T>
34
+ | TransactionToken<T> {
35
+ const token = {
36
+ key: state.key,
37
+ type: state.type,
38
+ } as any
39
+ if (`family` in state) {
40
+ token.family = state.family
41
+ }
42
+ return token
43
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./deposit"
2
+ export * from "./lookup"
3
+ export * from "./store"
4
+ export * from "./withdraw"
5
+ export * from "./withdraw-new-family-member"
@@ -0,0 +1,26 @@
1
+ import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "atom.io"
2
+
3
+ import { target } from "../transaction"
4
+ import type { Store } from "./store"
5
+
6
+ export function lookup(
7
+ key: string,
8
+ store: Store,
9
+ ): AtomToken<unknown> | ReadonlySelectorToken<unknown> | SelectorToken<unknown> {
10
+ const core = target(store)
11
+ let type: string = core.atoms.has(key)
12
+ ? `atom`
13
+ : core.selectors.has(key)
14
+ ? `selector`
15
+ : core.readonlySelectors.has(key)
16
+ ? `readonly_selector`
17
+ : ``
18
+ if (!type) {
19
+ const errorId = Math.random().toString(36)
20
+ type = `🚨 This state could not be found by lookup! Check the console for "${errorId}"`
21
+ store.config.logger?.error(
22
+ `${errorId}: Key "${key}" does not exist in the store.`,
23
+ )
24
+ }
25
+ return { key, type } as any
26
+ }
@@ -0,0 +1,154 @@
1
+ import type {
2
+ AtomFamily,
3
+ AtomToken,
4
+ Logger,
5
+ ReadonlySelectorFamily,
6
+ ReadonlySelectorToken,
7
+ SelectorFamily,
8
+ SelectorToken,
9
+ TimelineToken,
10
+ TransactionToken,
11
+ ƒn,
12
+ } from "atom.io"
13
+
14
+ import { Junction } from "~/packages/rel8/junction/src"
15
+
16
+ import type { Atom } from "../atom"
17
+ import type { MutableAtom, Tracker, Transceiver } from "../mutable"
18
+ import type { OperationProgress } from "../operation"
19
+ import type { ReadonlySelector, Selector } from "../selector"
20
+ import { Subject } from "../subject"
21
+ import type { Timeline } from "../timeline"
22
+ import type { Transaction, TransactionStatus } from "../transaction"
23
+
24
+ export type StoreCore = Pick<
25
+ Store,
26
+ | `atoms`
27
+ | `atomsThatAreDefault`
28
+ | `families`
29
+ | `operation`
30
+ | `readonlySelectors`
31
+ | `selectorAtoms`
32
+ | `selectorGraph`
33
+ | `selectors`
34
+ | `timelineAtoms`
35
+ | `timelines`
36
+ | `trackers`
37
+ | `transactions`
38
+ | `valueMap`
39
+ >
40
+
41
+ export class Store {
42
+ public valueMap = new Map<string, any>()
43
+
44
+ public atoms = new Map<string, Atom<any> | MutableAtom<any>>()
45
+ public selectors = new Map<string, Selector<any>>()
46
+ public readonlySelectors = new Map<string, ReadonlySelector<any>>()
47
+
48
+ public trackers = new Map<string, Tracker<Transceiver<any>>>()
49
+ public families = new Map<
50
+ string,
51
+ | AtomFamily<any, any>
52
+ | ReadonlySelectorFamily<any, any>
53
+ | SelectorFamily<any, any>
54
+ >()
55
+
56
+ public timelines = new Map<string, Timeline>()
57
+ public transactions = new Map<string, Transaction<ƒn>>()
58
+
59
+ public atomsThatAreDefault = new Set<string>()
60
+
61
+ public timelineAtoms = new Junction({
62
+ between: [`timelineKey`, `atomKey`],
63
+ cardinality: `1:n`,
64
+ })
65
+ public selectorAtoms = new Junction({
66
+ between: [`selectorKey`, `atomKey`],
67
+ cardinality: `n:n`,
68
+ })
69
+ public selectorGraph = new Junction<
70
+ `upstreamSelectorKey`,
71
+ `downstreamSelectorKey`,
72
+ { source: string }
73
+ >(
74
+ {
75
+ between: [`upstreamSelectorKey`, `downstreamSelectorKey`],
76
+ cardinality: `n:n`,
77
+ },
78
+ {
79
+ makeContentKey: (...keys) => keys.sort().join(`:`),
80
+ },
81
+ )
82
+
83
+ public subject = {
84
+ atomCreation: new Subject<AtomToken<unknown>>(),
85
+ selectorCreation: new Subject<
86
+ ReadonlySelectorToken<unknown> | SelectorToken<unknown>
87
+ >(),
88
+ transactionCreation: new Subject<TransactionToken<ƒn>>(),
89
+ timelineCreation: new Subject<TimelineToken>(),
90
+ operationStatus: new Subject<OperationProgress>(),
91
+ }
92
+ public operation: OperationProgress = { open: false }
93
+ public transactionStatus: TransactionStatus<ƒn> = { phase: `idle` }
94
+
95
+ public config: {
96
+ name: string
97
+ logger: Logger | null
98
+ logger__INTERNAL: Logger
99
+ } = {
100
+ name: `IMPLICIT_STORE`,
101
+ logger: { ...console, info: () => undefined },
102
+ logger__INTERNAL: console,
103
+ }
104
+
105
+ public constructor(name: string, store: Store | null = null) {
106
+ if (store !== null) {
107
+ this.valueMap = new Map(store?.valueMap)
108
+
109
+ this.operation = { ...store?.operation }
110
+ this.transactionStatus = { ...store?.transactionStatus }
111
+ this.config = {
112
+ ...store?.config,
113
+ logger__INTERNAL: console,
114
+ logger: {
115
+ ...console,
116
+ info: () => undefined,
117
+ ...store?.config?.logger,
118
+ },
119
+ name,
120
+ }
121
+ }
122
+
123
+ store?.atoms.forEach((atom) => {
124
+ atom.install(this)
125
+ })
126
+ store?.readonlySelectors.forEach((selector) => {
127
+ selector.install(this)
128
+ })
129
+ store?.selectors.forEach((selector) => {
130
+ selector.install(this)
131
+ })
132
+ store?.transactions.forEach((tx) => {
133
+ tx.install(this)
134
+ })
135
+ store?.timelines.forEach((timeline) => {
136
+ timeline.install(this)
137
+ })
138
+ }
139
+ }
140
+
141
+ export const IMPLICIT = {
142
+ STORE_INTERNAL: undefined as Store | undefined,
143
+ get STORE(): Store {
144
+ return (
145
+ this.STORE_INTERNAL ?? (this.STORE_INTERNAL = new Store(`IMPLICIT_STORE`))
146
+ )
147
+ },
148
+ }
149
+
150
+ export const clearStore = (store: Store = IMPLICIT.STORE): void => {
151
+ const { config } = store
152
+ Object.assign(store, new Store(config.name))
153
+ store.config = config
154
+ }
@@ -0,0 +1,53 @@
1
+ import type {
2
+ AtomToken,
3
+ ReadonlySelectorToken,
4
+ SelectorToken,
5
+ StateToken,
6
+ } from "atom.io"
7
+ import type { Atom, ReadonlySelector, Selector, Store } from ".."
8
+ import { withdraw } from ".."
9
+ import { target } from "../transaction"
10
+
11
+ export function withdrawNewFamilyMember<T>(
12
+ token: AtomToken<T>,
13
+ store: Store,
14
+ ): Atom<T> | null
15
+ export function withdrawNewFamilyMember<T>(
16
+ token: SelectorToken<T>,
17
+ store: Store,
18
+ ): Selector<T> | null
19
+ export function withdrawNewFamilyMember<T>(
20
+ token: ReadonlySelectorToken<T>,
21
+ store: Store,
22
+ ): ReadonlySelector<T> | null
23
+ export function withdrawNewFamilyMember<T>(
24
+ token: StateToken<T>,
25
+ store: Store,
26
+ ): Atom<T> | Selector<T> | null
27
+ export function withdrawNewFamilyMember<T>(
28
+ token: ReadonlySelectorToken<T> | StateToken<T>,
29
+ store: Store,
30
+ ): Atom<T> | ReadonlySelector<T> | Selector<T> | null
31
+ export function withdrawNewFamilyMember<T>(
32
+ token:
33
+ | AtomToken<T>
34
+ | ReadonlySelectorToken<T>
35
+ | SelectorToken<T>
36
+ | StateToken<T>,
37
+ store: Store,
38
+ ): Atom<T> | ReadonlySelector<T> | Selector<T> | null {
39
+ store.config.logger?.info(
40
+ `👪 creating new family member "${token.key}" in store "${store.config.name}"`,
41
+ )
42
+ if (token.family) {
43
+ const core = target(store)
44
+ const family = core.families.get(token.family.key)
45
+ if (family) {
46
+ const jsonSubKey = JSON.parse(token.family.subKey)
47
+ family(jsonSubKey)
48
+ const state = withdraw(token, store)
49
+ return state
50
+ }
51
+ }
52
+ return null
53
+ }
@@ -0,0 +1,113 @@
1
+ import type {
2
+ AtomToken,
3
+ ReadonlySelectorToken,
4
+ SelectorToken,
5
+ StateToken,
6
+ TimelineToken,
7
+ TransactionToken,
8
+ ƒn,
9
+ } from "atom.io"
10
+
11
+ import type { Atom } from "../atom"
12
+ import type { ReadonlySelector, Selector } from "../selector"
13
+ import { addAtomToTimeline } from "../timeline"
14
+ import type { Timeline } from "../timeline"
15
+ import type { Transaction } from "../transaction"
16
+ import { target } from "../transaction"
17
+ import type { Store } from "./store"
18
+
19
+ export function withdraw<T>(token: AtomToken<T>, store: Store): Atom<T> | null
20
+ export function withdraw<T>(
21
+ token: SelectorToken<T>,
22
+ store: Store,
23
+ ): Selector<T> | null
24
+ export function withdraw<T>(
25
+ token: StateToken<T>,
26
+ store: Store,
27
+ ): Atom<T> | Selector<T> | null
28
+ export function withdraw<T>(
29
+ token: ReadonlySelectorToken<T>,
30
+ store: Store,
31
+ ): ReadonlySelector<T> | null
32
+ export function withdraw<T>(
33
+ token: TransactionToken<T>,
34
+ store: Store,
35
+ ): Transaction<T extends ƒn ? T : never> | null
36
+ export function withdraw<T>(
37
+ token: ReadonlySelectorToken<T> | StateToken<T>,
38
+ store: Store,
39
+ ): Atom<T> | ReadonlySelector<T> | Selector<T> | null
40
+ export function withdraw<T>(token: TimelineToken, store: Store): Timeline | null
41
+ export function withdraw<T>(
42
+ token:
43
+ | ReadonlySelectorToken<T>
44
+ | StateToken<T>
45
+ | TimelineToken
46
+ | TransactionToken<T>,
47
+ store: Store,
48
+ ):
49
+ | Atom<T>
50
+ | ReadonlySelector<T>
51
+ | Selector<T>
52
+ | Timeline
53
+ | Transaction<T extends ƒn ? T : never>
54
+ | null {
55
+ let core = target(store)
56
+ let state =
57
+ core.atoms.get(token.key) ??
58
+ core.selectors.get(token.key) ??
59
+ core.readonlySelectors.get(token.key) ??
60
+ core.transactions.get(token.key) ??
61
+ core.timelines.get(token.key)
62
+ if (state) {
63
+ return state
64
+ }
65
+ if (store.transactionStatus.phase === `applying`) {
66
+ core = store.transactionStatus.core
67
+ state =
68
+ core.atoms.get(token.key) ??
69
+ core.selectors.get(token.key) ??
70
+ core.readonlySelectors.get(token.key) ??
71
+ core.transactions.get(token.key) ??
72
+ core.timelines.get(token.key)
73
+
74
+ if (state) {
75
+ store.config.logger?.info(`🛠️ add ${token.type} "${token.key}"`)
76
+ switch (state.type) {
77
+ case `atom`: {
78
+ store.atoms.set(token.key, state)
79
+ store.valueMap.set(token.key, state.default)
80
+ const stateKey = state.key
81
+ const familyKey = state.family?.key
82
+ let timelineKey = core.timelineAtoms.getRelatedKey(stateKey)
83
+ if (timelineKey === undefined && typeof familyKey === `string`) {
84
+ timelineKey = core.timelineAtoms.getRelatedKey(familyKey)
85
+ }
86
+ const timeline =
87
+ typeof timelineKey === `string`
88
+ ? store.timelines.get(timelineKey)
89
+ : undefined
90
+
91
+ if (timeline) {
92
+ addAtomToTimeline(state, timeline, store)
93
+ }
94
+ break
95
+ }
96
+ case `selector`:
97
+ core.selectors.set(token.key, state)
98
+ break
99
+ case `readonly_selector`:
100
+ core.readonlySelectors.set(token.key, state)
101
+ break
102
+ case `transaction`:
103
+ core.transactions.set(token.key, state)
104
+ break
105
+ case `timeline`:
106
+ core.timelines.set(token.key, state)
107
+ break
108
+ }
109
+ return state
110
+ }
111
+ }
112
+ return null
113
+ }
@@ -0,0 +1,21 @@
1
+ export class Subject<T> {
2
+ public Subscriber: (value: T) => void
3
+
4
+ public subscribers: Map<string, this[`Subscriber`]> = new Map()
5
+
6
+ public subscribe(key: string, subscriber: this[`Subscriber`]): () => void {
7
+ this.subscribers.set(key, subscriber)
8
+ const unsubscribe = () => this.unsubscribe(key)
9
+ return unsubscribe
10
+ }
11
+
12
+ private unsubscribe(key: string) {
13
+ this.subscribers.delete(key)
14
+ }
15
+
16
+ public next(value: T): void {
17
+ for (const subscriber of this.subscribers.values()) {
18
+ subscriber(value)
19
+ }
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ export * from "./subscribe-to-root-atoms"
@@ -0,0 +1,19 @@
1
+ import type { Atom } from "../atom"
2
+ import type { ReadonlySelector, Selector } from "../selector"
3
+ import type { Store } from "../store"
4
+ import { IMPLICIT } from "../store"
5
+ import { target } from "../transaction"
6
+
7
+ export const recallState = <T>(
8
+ state: Atom<T> | ReadonlySelector<T> | Selector<T>,
9
+ store: Store = IMPLICIT.STORE,
10
+ ): T => {
11
+ const core = target(store)
12
+ if (!core.operation.open) {
13
+ store.config.logger?.warn(
14
+ `recall called outside of an operation. This is probably a bug.`,
15
+ )
16
+ return core.valueMap.get(state.key)
17
+ }
18
+ return core.operation.prev.get(state.key)
19
+ }
@@ -0,0 +1,47 @@
1
+ import { getState__INTERNAL } from "../get-state-internal"
2
+ import type { ReadonlySelector, Selector } from "../selector"
3
+ import { traceAllSelectorAtoms } from "../selector"
4
+ import type { Store } from "../store"
5
+ import { withdraw } from "../store"
6
+ import { recallState } from "./recall-state"
7
+
8
+ export const subscribeToRootAtoms = <T>(
9
+ state: ReadonlySelector<T> | Selector<T>,
10
+ store: Store,
11
+ ): (() => void)[] | null => {
12
+ const dependencySubscriptions =
13
+ `default` in state
14
+ ? null
15
+ : traceAllSelectorAtoms(state.key, store).map((atomToken) => {
16
+ const atom = withdraw(atomToken, store)
17
+ if (atom === null) {
18
+ throw new Error(
19
+ `Atom "${atomToken.key}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`,
20
+ )
21
+ }
22
+ return atom.subject.subscribe(
23
+ `${state.type}:${state.key}`,
24
+ (atomChange) => {
25
+ store.config.logger?.info(
26
+ `📢 selector "${state.key}" saw root "${atomToken.key}" go (`,
27
+ atomChange.oldValue,
28
+ `->`,
29
+ atomChange.newValue,
30
+ `)`,
31
+ )
32
+ const oldValue = recallState(state, store)
33
+ // ❗ this retrieves a stale cached value when applying a transaction on the server
34
+ const newValue = getState__INTERNAL(state, store)
35
+ store.config.logger?.info(
36
+ ` <- "${state.key}" went (`,
37
+ oldValue,
38
+ `->`,
39
+ newValue,
40
+ `)`,
41
+ )
42
+ state.subject.next({ newValue, oldValue })
43
+ },
44
+ )
45
+ })
46
+ return dependencySubscriptions
47
+ }