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,68 @@
1
+ import type { StateToken } from "atom.io"
2
+
3
+ import type { Store } from "./store"
4
+ import { IMPLICIT } from "./store"
5
+ import { target } from "./transaction"
6
+
7
+ export type OperationProgress =
8
+ | {
9
+ open: false
10
+ }
11
+ | {
12
+ open: true
13
+ done: Set<string>
14
+ prev: Map<string, any>
15
+ time: number
16
+ token: StateToken<any>
17
+ }
18
+
19
+ export const openOperation = (token: StateToken<any>, store: Store): void => {
20
+ const core = target(store)
21
+ if (core.operation.open) {
22
+ store.config.logger?.error(
23
+ `❌ failed to setState to "${token.key}" during a setState for "${core.operation.token.key}"`,
24
+ )
25
+ throw Symbol(`violation`)
26
+ }
27
+ core.operation = {
28
+ open: true,
29
+ done: new Set(),
30
+ prev: new Map(store.valueMap),
31
+ time: Date.now(),
32
+ token,
33
+ }
34
+ store.config.logger?.info(
35
+ `⭕ operation start from "${token.key}" in store "${store.config.name}"${
36
+ store.transactionStatus.phase === `idle`
37
+ ? ``
38
+ : ` ${store.transactionStatus.phase} "${store.transactionStatus.key}"`
39
+ }`,
40
+ )
41
+ }
42
+ export const closeOperation = (store: Store): void => {
43
+ const core = target(store)
44
+ core.operation = { open: false }
45
+ store.config.logger?.info(`🔴 operation done`)
46
+ store.subject.operationStatus.next(core.operation)
47
+ }
48
+
49
+ export const isDone = (key: string, store: Store = IMPLICIT.STORE): boolean => {
50
+ const core = target(store)
51
+ if (!core.operation.open) {
52
+ store.config.logger?.warn(
53
+ `isDone called outside of an operation. This is probably a bug.`,
54
+ )
55
+ return true
56
+ }
57
+ return core.operation.done.has(key)
58
+ }
59
+ export const markDone = (key: string, store: Store = IMPLICIT.STORE): void => {
60
+ const core = target(store)
61
+ if (!core.operation.open) {
62
+ store.config.logger?.warn(
63
+ `markDone called outside of an operation. This is probably a bug.`,
64
+ )
65
+ return
66
+ }
67
+ core.operation.done.add(key)
68
+ }
@@ -0,0 +1,65 @@
1
+ import type { FamilyMetadata, SelectorOptions, SelectorToken } from "atom.io"
2
+
3
+ import { cacheValue } from "../caching"
4
+ import { markDone } from "../operation"
5
+ import { become } from "../set-state/become"
6
+ import type { Store, StoreCore } from "../store"
7
+ import { Subject } from "../subject"
8
+ import type { Selector } from "./create-selector"
9
+ import { createSelector } from "./create-selector"
10
+ import { registerSelector } from "./register-selector"
11
+
12
+ export const createReadWriteSelector = <T>(
13
+ options: SelectorOptions<T>,
14
+ family: FamilyMetadata | undefined,
15
+ store: Store,
16
+ core: StoreCore,
17
+ ): SelectorToken<T> => {
18
+ const subject = new Subject<{ newValue: T; oldValue: T }>()
19
+
20
+ const { get, set } = registerSelector(options.key, store)
21
+ const getSelf = () => {
22
+ const value = options.get({ get })
23
+ cacheValue(options.key, value, subject, store)
24
+ return value
25
+ }
26
+
27
+ const setSelf = (next: T | ((oldValue: T) => T)): void => {
28
+ const oldValue = getSelf()
29
+ store.config.logger?.info(
30
+ ` <- "${options.key}" went (`,
31
+ oldValue,
32
+ `->`,
33
+ next,
34
+ `)`,
35
+ )
36
+ const newValue = become(next)(oldValue)
37
+ cacheValue(options.key, newValue, subject, store)
38
+ markDone(options.key, store)
39
+ if (store.transactionStatus.phase === `idle`) {
40
+ subject.next({ newValue, oldValue })
41
+ }
42
+ options.set({ get, set }, newValue)
43
+ }
44
+ const mySelector: Selector<T> = {
45
+ ...options,
46
+ subject,
47
+ install: (s: Store) => createSelector(options, family, s),
48
+ get: getSelf,
49
+ set: setSelf,
50
+ type: `selector`,
51
+ ...(family && { family }),
52
+ }
53
+ core.selectors.set(options.key, mySelector)
54
+ const initialValue = getSelf()
55
+ store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
56
+ const token: SelectorToken<T> = {
57
+ key: options.key,
58
+ type: `selector`,
59
+ }
60
+ if (family) {
61
+ token.family = family
62
+ }
63
+ store.subject.selectorCreation.next(token)
64
+ return token
65
+ }
@@ -0,0 +1,49 @@
1
+ import type {
2
+ FamilyMetadata,
3
+ ReadonlySelectorOptions,
4
+ ReadonlySelectorToken,
5
+ } from "atom.io"
6
+
7
+ import { cacheValue } from "../caching"
8
+ import type { Store, StoreCore } from "../store"
9
+ import { Subject } from "../subject"
10
+ import type { ReadonlySelector } from "./create-selector"
11
+ import { createSelector } from "./create-selector"
12
+ import { registerSelector } from "./register-selector"
13
+
14
+ export const createReadonlySelector = <T>(
15
+ options: ReadonlySelectorOptions<T>,
16
+ family: FamilyMetadata | undefined,
17
+ store: Store,
18
+ core: StoreCore,
19
+ ): ReadonlySelectorToken<T> => {
20
+ const subject = new Subject<{ newValue: T; oldValue: T }>()
21
+
22
+ const { get } = registerSelector(options.key, store)
23
+ const getSelf = () => {
24
+ const value = options.get({ get })
25
+ cacheValue(options.key, value, subject, store)
26
+ return value
27
+ }
28
+
29
+ const readonlySelector: ReadonlySelector<T> = {
30
+ ...options,
31
+ subject,
32
+ install: (s: Store) => createSelector(options, family, s),
33
+ get: getSelf,
34
+ type: `readonly_selector`,
35
+ ...(family && { family }),
36
+ }
37
+ core.readonlySelectors.set(options.key, readonlySelector)
38
+ const initialValue = getSelf()
39
+ store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
40
+ const token: ReadonlySelectorToken<T> = {
41
+ key: options.key,
42
+ type: `readonly_selector`,
43
+ }
44
+ if (family) {
45
+ token.family = family
46
+ }
47
+ store.subject.selectorCreation.next(token)
48
+ return token
49
+ }
@@ -0,0 +1,65 @@
1
+ import type {
2
+ FamilyMetadata,
3
+ ReadonlySelectorOptions,
4
+ ReadonlySelectorToken,
5
+ SelectorOptions,
6
+ SelectorToken,
7
+ } from "atom.io"
8
+
9
+ import type { Store } from "../store"
10
+ import { IMPLICIT } from "../store"
11
+ import type { Subject } from "../subject"
12
+ import { target } from "../transaction"
13
+ import { createReadWriteSelector } from "./create-read-write-selector"
14
+ import { createReadonlySelector } from "./create-readonly-selector"
15
+
16
+ export type Selector<T> = {
17
+ key: string
18
+ type: `selector`
19
+ family?: FamilyMetadata
20
+ install: (store: Store) => void
21
+ subject: Subject<{ newValue: T; oldValue: T }>
22
+ get: () => T
23
+ set: (newValue: T | ((oldValue: T) => T)) => void
24
+ }
25
+ export type ReadonlySelector<T> = {
26
+ key: string
27
+ type: `readonly_selector`
28
+ family?: FamilyMetadata
29
+ install: (store: Store) => void
30
+ subject: Subject<{ newValue: T; oldValue: T }>
31
+ get: () => T
32
+ }
33
+
34
+ export function createSelector<T>(
35
+ options: SelectorOptions<T>,
36
+ family?: FamilyMetadata,
37
+ store?: Store,
38
+ ): SelectorToken<T>
39
+ export function createSelector<T>(
40
+ options: ReadonlySelectorOptions<T>,
41
+ family?: FamilyMetadata,
42
+ store?: Store,
43
+ ): ReadonlySelectorToken<T>
44
+ export function createSelector<T>(
45
+ options: ReadonlySelectorOptions<T> | SelectorOptions<T>,
46
+ family?: FamilyMetadata,
47
+ store: Store = IMPLICIT.STORE,
48
+ ): ReadonlySelectorToken<T> | SelectorToken<T> {
49
+ const core = target(store)
50
+ const existingWritable = core.selectors.get(options.key)
51
+ const existingReadonly = core.readonlySelectors.get(options.key)
52
+
53
+ if (existingWritable || existingReadonly) {
54
+ store.config.logger?.error?.(
55
+ `Tried to create ${existingReadonly ? `readonly selector` : `selector`}`,
56
+ `"${options.key}", but it already exists in the store.`,
57
+ `(Ignore if you are using hot module replacement.)`,
58
+ )
59
+ }
60
+
61
+ if (`set` in options) {
62
+ return createReadWriteSelector(options, family, store, core)
63
+ }
64
+ return createReadonlySelector(options, family, store, core)
65
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./lookup-selector-sources"
2
+ export * from "./register-selector"
3
+ export * from "./create-selector"
4
+ export * from "./trace-selector-atoms"
5
+ export * from "./update-selector-atoms"
@@ -0,0 +1,20 @@
1
+ import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "atom.io"
2
+
3
+ import type { Store } from "../store"
4
+ import { lookup } from "../store"
5
+ import { target } from "../transaction"
6
+
7
+ export const lookupSelectorSources = (
8
+ key: string,
9
+ store: Store,
10
+ ): (
11
+ | AtomToken<unknown>
12
+ | ReadonlySelectorToken<unknown>
13
+ | SelectorToken<unknown>
14
+ )[] => {
15
+ const sources = target(store)
16
+ .selectorGraph.getRelationEntries({ downstreamSelectorKey: key })
17
+ .filter(([_, { source }]) => source !== key)
18
+ .map(([_, { source }]) => lookup(source, store))
19
+ return sources
20
+ }
@@ -0,0 +1,61 @@
1
+ import type { Transactors } from "atom.io"
2
+
3
+ import { getState__INTERNAL } from "../get-state-internal"
4
+ import { setState__INTERNAL } from "../set-state"
5
+ import type { Store } from "../store"
6
+ import { IMPLICIT, withdraw } from "../store"
7
+ import { target } from "../transaction/transaction-internal"
8
+ import { updateSelectorAtoms } from "./update-selector-atoms"
9
+
10
+ export const registerSelector = (
11
+ selectorKey: string,
12
+ store: Store = IMPLICIT.STORE,
13
+ ): Transactors => ({
14
+ get: (dependency) => {
15
+ const core = target(store)
16
+ const alreadyRegistered = core.selectorGraph
17
+ .getRelationEntries({ downstreamSelectorKey: selectorKey })
18
+ .some(([_, { source }]) => source === dependency.key)
19
+
20
+ const dependencyState = withdraw(dependency, store)
21
+ if (dependencyState === null) {
22
+ throw new Error(
23
+ `State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`,
24
+ )
25
+ }
26
+ const dependencyValue = getState__INTERNAL(dependencyState, store)
27
+
28
+ if (alreadyRegistered) {
29
+ store.config.logger?.info(
30
+ ` || ${selectorKey} <- ${dependency.key} =`,
31
+ dependencyValue,
32
+ )
33
+ } else {
34
+ store.config.logger?.info(
35
+ `🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
36
+ dependencyValue,
37
+ `)`,
38
+ )
39
+ core.selectorGraph = core.selectorGraph.set(
40
+ {
41
+ upstreamSelectorKey: dependency.key,
42
+ downstreamSelectorKey: selectorKey,
43
+ },
44
+ {
45
+ source: dependency.key,
46
+ },
47
+ )
48
+ }
49
+ updateSelectorAtoms(selectorKey, dependency, store)
50
+ return dependencyValue
51
+ },
52
+ set: (stateToken, newValue) => {
53
+ const state = withdraw(stateToken, store)
54
+ if (state === null) {
55
+ throw new Error(
56
+ `State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`,
57
+ )
58
+ }
59
+ setState__INTERNAL(state, newValue, store)
60
+ },
61
+ })
@@ -0,0 +1,45 @@
1
+ import type { AtomToken, ReadonlySelectorToken, StateToken } from "atom.io"
2
+
3
+ import type { Store } from ".."
4
+ import { lookupSelectorSources } from "./lookup-selector-sources"
5
+
6
+ export const traceSelectorAtoms = (
7
+ selectorKey: string,
8
+ dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
9
+ store: Store,
10
+ ): AtomToken<unknown>[] => {
11
+ const roots: AtomToken<unknown>[] = []
12
+
13
+ const sources = lookupSelectorSources(dependency.key, store)
14
+ let depth = 0
15
+ while (sources.length > 0) {
16
+ // biome-ignore lint/style/noNonNullAssertion: just checked length ^^^
17
+ const source = sources.shift()!
18
+ ++depth
19
+ if (depth > 999) {
20
+ throw new Error(
21
+ `Maximum selector dependency depth exceeded in selector "${selectorKey}".`,
22
+ )
23
+ }
24
+
25
+ if (source.type !== `atom`) {
26
+ sources.push(...lookupSelectorSources(source.key, store))
27
+ } else {
28
+ roots.push(source)
29
+ }
30
+ }
31
+
32
+ return roots
33
+ }
34
+
35
+ export const traceAllSelectorAtoms = (
36
+ selectorKey: string,
37
+ store: Store,
38
+ ): AtomToken<unknown>[] => {
39
+ const sources = lookupSelectorSources(selectorKey, store)
40
+ return sources.flatMap((source) =>
41
+ source.type === `atom`
42
+ ? source
43
+ : traceSelectorAtoms(selectorKey, source, store),
44
+ )
45
+ }
@@ -0,0 +1,34 @@
1
+ import type { ReadonlySelectorToken, StateToken } from "atom.io"
2
+
3
+ import type { Store } from "../store"
4
+ import { target } from "../transaction"
5
+ import { traceSelectorAtoms } from "./trace-selector-atoms"
6
+
7
+ export const updateSelectorAtoms = (
8
+ selectorKey: string,
9
+ dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
10
+ store: Store,
11
+ ): void => {
12
+ const core = target(store)
13
+ if (dependency.type === `atom`) {
14
+ core.selectorAtoms = core.selectorAtoms.set({
15
+ selectorKey,
16
+ atomKey: dependency.key,
17
+ })
18
+ store.config.logger?.info(
19
+ ` || adding root for "${selectorKey}": ${dependency.key}`,
20
+ )
21
+ return
22
+ }
23
+ const roots = traceSelectorAtoms(selectorKey, dependency, store)
24
+ store.config.logger?.info(
25
+ ` || adding roots for "${selectorKey}":`,
26
+ roots.map((r) => r.key),
27
+ )
28
+ for (const root of roots) {
29
+ core.selectorAtoms = core.selectorAtoms.set({
30
+ selectorKey,
31
+ atomKey: root.key,
32
+ })
33
+ }
34
+ }
@@ -0,0 +1,10 @@
1
+ export type Modify<T> = (thing: T) => T
2
+
3
+ export const become =
4
+ <T>(nextVersionOfThing: Modify<T> | T) =>
5
+ (originalThing: T): T =>
6
+ nextVersionOfThing instanceof Function
7
+ ? nextVersionOfThing(
8
+ originalThing instanceof Function ? originalThing() : originalThing,
9
+ )
10
+ : nextVersionOfThing
@@ -0,0 +1,23 @@
1
+ import type { JsonInterface } from "atom.io/json"
2
+
3
+ import type { Atom } from "../atom"
4
+ import { Tracker } from "../mutable"
5
+ import type { Store, StoreCore } from "../store"
6
+
7
+ export function copyMutableIfNeeded<T>(
8
+ atom: Atom<T>,
9
+ transform: JsonInterface<T>,
10
+ origin: Store,
11
+ target: StoreCore,
12
+ ): T {
13
+ const originValue = origin.valueMap.get(atom.key)
14
+ const targetValue = target.valueMap.get(atom.key)
15
+ if (originValue === targetValue) {
16
+ origin.config.logger?.info(`📃 copying`, `${atom.key}`)
17
+ const copiedValue = transform.fromJson(transform.toJson(originValue))
18
+ target.valueMap.set(atom.key, copiedValue)
19
+ new Tracker(atom, origin)
20
+ return copiedValue
21
+ }
22
+ return targetValue
23
+ }
@@ -0,0 +1,59 @@
1
+ import type { AtomFamily } from "atom.io"
2
+ import type { Json, JsonInterface } from "atom.io/json"
3
+ import { getState__INTERNAL } from ".."
4
+ import type { Store, StoreCore } from ".."
5
+ import type { Atom } from "../atom"
6
+ import { copyMutableIfNeeded } from "./copy-mutable-if-needed"
7
+
8
+ export function copyMutableIfWithinTransaction<T>(
9
+ atom: Atom<T> | (Atom<T> & JsonInterface<T, Json.Serializable>),
10
+ store: Store,
11
+ ): T {
12
+ if (
13
+ store.transactionStatus.phase === `building` ||
14
+ store.transactionStatus.phase === `applying`
15
+ ) {
16
+ if (`toJson` in atom && `fromJson` in atom) {
17
+ store.config.logger?.info(
18
+ `📄 copyMutableIfWithinTransaction: ${atom.key} is mutable`,
19
+ )
20
+ const copiedValue = copyMutableIfNeeded(
21
+ atom,
22
+ atom,
23
+ store,
24
+ store.transactionStatus.core,
25
+ )
26
+ return copiedValue
27
+ }
28
+ if (`family` in atom) {
29
+ const family = store.transactionStatus.core.families.get(atom.family.key)
30
+ if (family && family.type === `atom_family`) {
31
+ const result = copyMutableFamilyMemberWithinTransaction<T>(
32
+ atom,
33
+ family,
34
+ store,
35
+ store.transactionStatus.core,
36
+ )
37
+ if (result) {
38
+ return result
39
+ }
40
+ }
41
+ }
42
+ }
43
+ return getState__INTERNAL(atom, store)
44
+ }
45
+
46
+ export function copyMutableFamilyMemberWithinTransaction<T>(
47
+ atom: Atom<T>,
48
+ family:
49
+ | AtomFamily<T, any>
50
+ | (AtomFamily<T, any> & JsonInterface<T, Json.Serializable>),
51
+ origin: Store,
52
+ target: StoreCore,
53
+ ): T | null {
54
+ if (`toJson` in family && `fromJson` in family) {
55
+ const copyCreated = copyMutableIfNeeded(atom, family, origin, target)
56
+ return copyCreated
57
+ }
58
+ return null
59
+ }
@@ -0,0 +1,34 @@
1
+ import type { AtomFamily } from "atom.io"
2
+ import type { Json, JsonInterface } from "atom.io/json"
3
+ import type { Store, StoreCore } from ".."
4
+ import type { Atom } from "../atom"
5
+ import { copyMutableIfNeeded } from "./copy-mutable-if-needed"
6
+
7
+ export function copyMutableIntoNewStore<T>(
8
+ atom: Atom<T> | (Atom<T> & JsonInterface<T, Json.Serializable>),
9
+ origin: Store,
10
+ target: Store,
11
+ ): void {
12
+ if (`toJson` in atom && `fromJson` in atom) {
13
+ copyMutableIfNeeded(atom, atom, origin, target)
14
+ }
15
+ if (`family` in atom) {
16
+ const family = target.families.get(atom.family.key)
17
+ if (family && family.type === `atom_family`) {
18
+ copyMutableFamilyMemberIntoNewStore(atom, family, origin, target)
19
+ }
20
+ }
21
+ }
22
+
23
+ export function copyMutableFamilyMemberIntoNewStore<T>(
24
+ atom: Atom<T>,
25
+ family:
26
+ | AtomFamily<T, any>
27
+ | (AtomFamily<T, any> & JsonInterface<T, Json.Serializable>),
28
+ origin: Store,
29
+ target: Store,
30
+ ): void {
31
+ if (`toJson` in family && `fromJson` in family) {
32
+ copyMutableIfNeeded(atom, family, origin, target)
33
+ }
34
+ }
@@ -0,0 +1,23 @@
1
+ import type { StateUpdate } from "atom.io"
2
+ import type { Store } from "atom.io/internal"
3
+
4
+ import type { Atom } from "../atom"
5
+ import type { ReadonlySelector, Selector } from "../selector"
6
+
7
+ export const emitUpdate = <T>(
8
+ state: Atom<T> | ReadonlySelector<T> | Selector<T>,
9
+ update: StateUpdate<T>,
10
+ store: Store,
11
+ ): void => {
12
+ const { key } = state
13
+ const { logger } = store.config
14
+ logger?.info(
15
+ `📢 ${state.type} "${key}" went (`,
16
+ update.oldValue,
17
+ `->`,
18
+ update.newValue,
19
+ `)`,
20
+ )
21
+ logger?.info(`📢 notifying subscribers:`, state.subject.subscribers)
22
+ state.subject.next(update)
23
+ }
@@ -0,0 +1,39 @@
1
+ import type { Atom } from "../atom"
2
+ import { isDone, markDone } from "../operation"
3
+ import type { Store } from "../store"
4
+ import { IMPLICIT } from "../store"
5
+ import { target } from "../transaction"
6
+
7
+ export const evictDownStream = <T>(
8
+ state: Atom<T>,
9
+ store: Store = IMPLICIT.STORE,
10
+ ): void => {
11
+ const core = target(store)
12
+ const downstreamKeys = core.selectorAtoms.getRelatedKeys(state.key)
13
+ store.config.logger?.info(
14
+ ` || ${downstreamKeys?.size ?? `none`} downstream:`,
15
+ downstreamKeys,
16
+ )
17
+ if (core.operation.open) {
18
+ store.config.logger?.info(` ||`, [...core.operation.done], `already done`)
19
+ }
20
+ if (downstreamKeys) {
21
+ for (const key of downstreamKeys) {
22
+ if (isDone(key, store)) {
23
+ store.config.logger?.info(` || ${key} already done`)
24
+ continue
25
+ }
26
+ const state = core.selectors.get(key) ?? core.readonlySelectors.get(key)
27
+ if (!state) {
28
+ store.config.logger?.info(
29
+ ` || ${key} was not found in selectors or readonlySelectors`,
30
+ )
31
+ return
32
+ }
33
+ core.valueMap.delete(key)
34
+ store.config.logger?.info(` xx evicted "${key}"`)
35
+
36
+ markDone(key, store)
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./become"
2
+ export * from "./set-state-internal"
@@ -0,0 +1,38 @@
1
+ import type { Atom } from "../atom"
2
+ import { isAtomDefault, markAtomAsNotDefault } from "../atom"
3
+ import { cacheValue } from "../caching"
4
+ import { getState__INTERNAL } from "../get-state-internal"
5
+ import { markDone } from "../operation"
6
+ import type { Store } from "../store"
7
+ import { IMPLICIT } from "../store"
8
+ import { become } from "./become"
9
+ import { copyMutableIfWithinTransaction } from "./copy-mutable-in-transaction"
10
+ import { emitUpdate } from "./emit-update"
11
+ import { evictDownStream } from "./evict-downstream"
12
+ import { stowUpdate } from "./stow-update"
13
+
14
+ export const setAtomState = <T>(
15
+ atom: Atom<T>,
16
+ next: T | ((oldValue: T) => T),
17
+ store: Store = IMPLICIT.STORE,
18
+ ): void => {
19
+ const oldValue = getState__INTERNAL(atom, store)
20
+ let newValue = copyMutableIfWithinTransaction(atom, store)
21
+ newValue = become(next)(newValue)
22
+ store.config.logger?.info(`<< setting atom "${atom.key}" to`, newValue)
23
+ cacheValue(atom.key, newValue, atom.subject, store)
24
+ if (isAtomDefault(atom.key, store)) {
25
+ markAtomAsNotDefault(atom.key, store)
26
+ }
27
+ markDone(atom.key, store)
28
+ store.config.logger?.info(
29
+ ` || evicting caches downstream from "${atom.key}"`,
30
+ )
31
+ evictDownStream(atom, store)
32
+ const update = { oldValue, newValue }
33
+ if (store.transactionStatus.phase !== `building`) {
34
+ emitUpdate(atom, update, store)
35
+ } else {
36
+ stowUpdate(atom, update, store)
37
+ }
38
+ }
@@ -0,0 +1,19 @@
1
+ import { getState__INTERNAL } from "../get-state-internal"
2
+ import type { Selector } from "../selector"
3
+ import type { Store } from "../store"
4
+ import { IMPLICIT } from "../store"
5
+ import { become } from "./become"
6
+
7
+ export const setSelectorState = <T>(
8
+ selector: Selector<T>,
9
+ next: T | ((oldValue: T) => T),
10
+ store: Store = IMPLICIT.STORE,
11
+ ): void => {
12
+ const oldValue = getState__INTERNAL(selector, store)
13
+ const newValue = become(next)(oldValue)
14
+
15
+ store.config.logger?.info(`<< setting selector "${selector.key}" to`, newValue)
16
+ store.config.logger?.info(` || propagating change made to "${selector.key}"`)
17
+
18
+ selector.set(newValue)
19
+ }