atom.io 0.33.21 → 0.34.1

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 (54) hide show
  1. package/dist/eslint-plugin/index.js +1 -2
  2. package/dist/eslint-plugin/index.js.map +1 -1
  3. package/dist/internal/index.d.ts +10 -6
  4. package/dist/internal/index.d.ts.map +1 -1
  5. package/dist/internal/index.js +1262 -1253
  6. package/dist/internal/index.js.map +1 -1
  7. package/dist/json/index.d.ts +3 -0
  8. package/dist/json/index.d.ts.map +1 -1
  9. package/dist/json/index.js.map +1 -1
  10. package/dist/main/index.d.ts +405 -23
  11. package/dist/main/index.d.ts.map +1 -1
  12. package/dist/main/index.js +107 -11
  13. package/dist/main/index.js.map +1 -1
  14. package/dist/realtime-client/index.js +6 -10
  15. package/dist/realtime-client/index.js.map +1 -1
  16. package/dist/realtime-server/index.js +1 -1
  17. package/dist/realtime-server/index.js.map +1 -1
  18. package/dist/realtime-testing/index.d.ts.map +1 -1
  19. package/dist/realtime-testing/index.js +0 -1
  20. package/dist/realtime-testing/index.js.map +1 -1
  21. package/package.json +6 -6
  22. package/src/internal/atom/create-regular-atom.ts +6 -7
  23. package/src/internal/caching.ts +9 -9
  24. package/src/internal/future.ts +3 -0
  25. package/src/internal/get-state/read-or-compute-value.ts +12 -7
  26. package/src/internal/join/create-join.ts +27 -0
  27. package/src/internal/join/index.ts +1 -0
  28. package/src/internal/junction.ts +2 -0
  29. package/src/internal/mutable/create-mutable-atom.ts +2 -4
  30. package/src/internal/selector/create-readonly-held-selector.ts +10 -0
  31. package/src/internal/selector/create-readonly-pure-selector.ts +13 -4
  32. package/src/internal/selector/create-writable-held-selector.ts +11 -2
  33. package/src/internal/selector/create-writable-pure-selector.ts +11 -1
  34. package/src/internal/selector/trace-selector-atoms.ts +18 -40
  35. package/src/internal/selector/update-selector-atoms.ts +5 -5
  36. package/src/internal/set-state/evict-downstream.ts +2 -2
  37. package/src/internal/set-state/reset-atom-or-selector.ts +3 -3
  38. package/src/internal/store/store.ts +0 -1
  39. package/src/internal/subscribe/subscribe-to-root-atoms.ts +42 -38
  40. package/src/internal/subscribe/subscribe-to-state.ts +23 -11
  41. package/src/json/index.ts +3 -0
  42. package/src/main/atom.ts +54 -13
  43. package/src/main/dispose-state.ts +8 -2
  44. package/src/main/find-state.ts +44 -14
  45. package/src/main/get-state.ts +2 -2
  46. package/src/main/index.ts +8 -0
  47. package/src/main/join.ts +79 -22
  48. package/src/main/realm.ts +50 -4
  49. package/src/main/selector.ts +116 -6
  50. package/src/main/subscribe.ts +31 -1
  51. package/src/main/timeline.ts +17 -0
  52. package/src/main/transaction.ts +39 -3
  53. package/src/realtime-client/realtime-client-stores/client-sync-store.ts +2 -2
  54. package/src/realtime-testing/setup-realtime-test.tsx +0 -5
@@ -1,5 +1,5 @@
1
1
  import type { ReadableState } from ".."
2
- import { readCachedValue } from "../caching"
2
+ import { cacheValue, readCachedValue } from "../caching"
3
3
  import type { Store } from "../store"
4
4
 
5
5
  export const readOrComputeValue = <T>(
@@ -7,7 +7,6 @@ export const readOrComputeValue = <T>(
7
7
  state: ReadableState<T>,
8
8
  ): T => {
9
9
  if (target.valueMap.has(state.key)) {
10
- target.logger.info(`📖`, state.type, state.key, `reading cached value`)
11
10
  return readCachedValue(state, target)
12
11
  }
13
12
  switch (state.type) {
@@ -20,20 +19,26 @@ export const readOrComputeValue = <T>(
20
19
  case `atom`:
21
20
  case `mutable_atom`: {
22
21
  const def = state.default
23
- let fallback: T
22
+ let defaultValue: T
24
23
  if (def instanceof Function) {
25
- fallback = def()
24
+ defaultValue = def()
26
25
  } else {
27
- fallback = def
26
+ defaultValue = def
28
27
  }
28
+ const cachedValue = cacheValue(
29
+ target,
30
+ state.key,
31
+ defaultValue,
32
+ state.subject,
33
+ )
29
34
  target.logger.info(
30
35
  `💁`,
31
36
  `atom`,
32
37
  state.key,
33
38
  `could not find cached value; using default`,
34
- fallback,
39
+ defaultValue,
35
40
  )
36
- return fallback
41
+ return cachedValue
37
42
  }
38
43
  }
39
44
  }
@@ -0,0 +1,27 @@
1
+ import type { JoinOptions, JoinToken } from "atom.io"
2
+ import type { Store } from "atom.io/internal"
3
+ import { Join } from "atom.io/internal"
4
+ import type { Json } from "atom.io/json"
5
+
6
+ export function createJoin<
7
+ ASide extends string,
8
+ AType extends string,
9
+ BSide extends string,
10
+ BType extends string,
11
+ Cardinality extends `1:1` | `1:n` | `n:n`,
12
+ Content extends Json.Object,
13
+ >(
14
+ store: Store,
15
+ options: JoinOptions<ASide, AType, BSide, BType, Cardinality, Content>,
16
+ defaultContent: Content | undefined,
17
+ ): JoinToken<ASide, AType, BSide, BType, Cardinality, Content> {
18
+ store.joins.set(options.key, new Join(options, defaultContent, store))
19
+ const token: JoinToken<ASide, AType, BSide, BType, Cardinality, Content> = {
20
+ key: options.key,
21
+ type: `join`,
22
+ a: options.between[0],
23
+ b: options.between[1],
24
+ cardinality: options.cardinality,
25
+ }
26
+ return token
27
+ }
@@ -1,3 +1,4 @@
1
+ export * from "./create-join"
1
2
  export * from "./edit-relations-in-store"
2
3
  export * from "./find-relations-in-store"
3
4
  export * from "./get-internal-relations-from-store"
@@ -18,7 +18,9 @@ export interface JunctionEntries<
18
18
  JunctionEntriesBase<AType, BType, Content> {}
19
19
 
20
20
  export type JunctionSchemaBase<ASide extends string, BSide extends string> = {
21
+ /** Description of the relationship between the two sides */
21
22
  readonly between: [a: ASide, b: BSide]
23
+ /** How many relations are allowed in each direction? */
22
24
  readonly cardinality: `1:1` | `1:n` | `n:n`
23
25
  }
24
26
  export interface JunctionSchema<ASide extends string, BSide extends string>
@@ -8,7 +8,7 @@ import type { Json } from "atom.io/json"
8
8
  import { selectJson } from "atom.io/json"
9
9
 
10
10
  import type { MutableAtom } from ".."
11
- import { cacheValue, resetInStore, setIntoStore } from ".."
11
+ import { resetInStore, setIntoStore } from ".."
12
12
  import { newest } from "../lineage"
13
13
  import { deposit, type Store } from "../store"
14
14
  import { Subject } from "../subject"
@@ -31,7 +31,7 @@ export function createMutableAtom<
31
31
  `creating in store "${store.config.name}"`,
32
32
  )
33
33
  const target = newest(store)
34
- const { key, default: def } = options
34
+ const { key } = options
35
35
  const existing = target.atoms.get(key)
36
36
  const type = `mutable_atom`
37
37
  if (existing && existing.type === type) {
@@ -56,9 +56,7 @@ export function createMutableAtom<
56
56
  if (family) {
57
57
  newAtom.family = family
58
58
  }
59
- const initialValue = def()
60
59
  target.atoms.set(newAtom.key, newAtom)
61
- cacheValue(target, key, initialValue, subject)
62
60
  const token = deposit(newAtom)
63
61
  if (options.effects) {
64
62
  let effectIndex = 0
@@ -23,6 +23,16 @@ export const createReadonlyHeldSelector = <T extends object>(
23
23
  const type = `readonly_held_selector` as const
24
24
  const { get, find, json } = registerSelector(target, type, key, covered)
25
25
  const getSelf = () => {
26
+ const innerTarget = newest(store)
27
+ const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
28
+ downstreamSelectorKey: key,
29
+ })
30
+ for (const [downstreamSelectorKey, { source }] of upstreamStates) {
31
+ if (source !== key) {
32
+ innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
33
+ }
34
+ }
35
+ innerTarget.selectorAtoms.delete(key)
26
36
  options.get({ get, find, json }, constant)
27
37
  cacheValue(newest(store), key, constant, subject)
28
38
  covered.clear()
@@ -23,10 +23,21 @@ export const createReadonlyPureSelector = <T>(
23
23
  const type = `readonly_pure_selector` as const
24
24
  const { get, find, json } = registerSelector(target, type, key, covered)
25
25
  const getSelf = () => {
26
+ const innerTarget = newest(store)
27
+ const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
28
+ downstreamSelectorKey: key,
29
+ })
30
+ for (const [downstreamSelectorKey, { source }] of upstreamStates) {
31
+ if (source !== key) {
32
+ innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
33
+ }
34
+ }
35
+ innerTarget.selectorAtoms.delete(key)
26
36
  const value = options.get({ get, find, json })
27
- cacheValue(newest(store), key, value, subject)
37
+ const cached = cacheValue(innerTarget, key, value, subject)
38
+ store.logger.info(`✨`, type, key, `=`, cached)
28
39
  covered.clear()
29
- return value
40
+ return cached
30
41
  }
31
42
 
32
43
  const readonlySelector: ReadonlyPureSelector<T> = {
@@ -38,8 +49,6 @@ export const createReadonlyPureSelector = <T>(
38
49
  ...(family && { family }),
39
50
  }
40
51
  target.readonlySelectors.set(key, readonlySelector)
41
- const initialValue = getSelf()
42
- store.logger.info(`✨`, type, key, `=`, initialValue)
43
52
  const token: ReadonlyPureSelectorToken<T> = {
44
53
  key,
45
54
  type,
@@ -29,8 +29,18 @@ export const createWritableHeldSelector = <T extends object>(
29
29
  const getterToolkit = { find, get, json }
30
30
 
31
31
  const getSelf = (getFn = options.get, innerTarget = newest(store)): T => {
32
+ const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
33
+ downstreamSelectorKey: key,
34
+ })
35
+ for (const [downstreamSelectorKey, { source }] of upstreamStates) {
36
+ if (source !== key) {
37
+ innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
38
+ }
39
+ }
40
+ innerTarget.selectorAtoms.delete(key)
32
41
  getFn(getterToolkit, constant)
33
42
  cacheValue(innerTarget, key, constant, subject)
43
+ store.logger.info(`✨`, type, key, `=`, constant)
34
44
  covered.clear()
35
45
  return constant
36
46
  }
@@ -57,8 +67,7 @@ export const createWritableHeldSelector = <T extends object>(
57
67
  ...(family && { family }),
58
68
  }
59
69
  target.writableSelectors.set(key, mySelector)
60
- const initialValue = getSelf()
61
- store.logger.info(`✨`, type, key, `=`, initialValue)
70
+ // const initialValue = getSelf()
62
71
 
63
72
  const token: WritableHeldSelectorToken<T> = { key, type }
64
73
  if (family) {
@@ -29,8 +29,18 @@ export const createWritablePureSelector = <T>(
29
29
  const getterToolkit = { find, get, json }
30
30
 
31
31
  const getSelf = (getFn = options.get, innerTarget = newest(store)): T => {
32
+ const upstreamStates = innerTarget.selectorGraph.getRelationEntries({
33
+ downstreamSelectorKey: key,
34
+ })
35
+ for (const [downstreamSelectorKey, { source }] of upstreamStates) {
36
+ if (source !== key) {
37
+ innerTarget.selectorGraph.delete(downstreamSelectorKey, key)
38
+ }
39
+ }
40
+ innerTarget.selectorAtoms.delete(key)
32
41
  const value = getFn(getterToolkit)
33
- cacheValue(innerTarget, key, value, subject)
42
+ const cached = cacheValue(innerTarget, key, value, subject)
43
+ store.logger.info(`✨`, type, key, `=`, cached)
34
44
  covered.clear()
35
45
  return value
36
46
  }
@@ -1,51 +1,29 @@
1
- import type { Atom, Selector, Store } from ".."
2
- import type { AtomKey, StateKey } from "../keys"
1
+ import type { Atom, Store } from ".."
3
2
  import { isAtomKey } from "../keys"
4
3
  import { getSelectorDependencyKeys } from "./get-selector-dependency-keys"
5
4
 
6
- export const traceSelectorAtoms = (
5
+ export const traceRootSelectorAtoms = (
7
6
  store: Store,
8
- directDependencyKey: StateKey<unknown>,
9
- covered: Set<string>,
10
- ): AtomKey<unknown>[] => {
11
- const rootKeys: AtomKey<unknown>[] = []
7
+ selectorKey: string,
8
+ covered: Set<string> = new Set<string>(),
9
+ ): Map<string, Atom<unknown>> => {
10
+ const dependencies = getSelectorDependencyKeys(store, selectorKey)
12
11
 
13
- const indirectDependencyKeys = getSelectorDependencyKeys(
14
- store,
15
- directDependencyKey,
16
- )
17
- while (indirectDependencyKeys.length > 0) {
12
+ const roots = new Map<string, Atom<unknown>>()
13
+
14
+ while (dependencies.length > 0) {
18
15
  // biome-ignore lint/style/noNonNullAssertion: just checked length ^^^
19
- const indirectDependencyKey = indirectDependencyKeys.shift()!
20
- if (covered.has(indirectDependencyKey)) {
16
+ const dependencyKey = dependencies.pop()!
17
+ if (covered.has(dependencyKey)) {
21
18
  continue
22
19
  }
23
- covered.add(indirectDependencyKey)
24
-
25
- if (!isAtomKey(store, indirectDependencyKey)) {
26
- indirectDependencyKeys.push(
27
- ...getSelectorDependencyKeys(store, indirectDependencyKey),
28
- )
29
- } else if (!rootKeys.includes(indirectDependencyKey)) {
30
- rootKeys.push(indirectDependencyKey)
20
+ covered.add(dependencyKey)
21
+ if (isAtomKey(store, dependencyKey)) {
22
+ const atom = store.atoms.get(dependencyKey) as Atom<unknown>
23
+ roots.set(atom.key, atom)
24
+ } else {
25
+ dependencies.push(...getSelectorDependencyKeys(store, dependencyKey))
31
26
  }
32
27
  }
33
-
34
- return rootKeys
35
- }
36
-
37
- export const traceAllSelectorAtoms = (
38
- selector: Selector<any>,
39
- store: Store,
40
- ): Atom<unknown>[] => {
41
- const selectorKey = selector.key
42
- const directDependencyKeys = getSelectorDependencyKeys(store, selectorKey)
43
- const covered = new Set<string>()
44
- return directDependencyKeys
45
- .flatMap((depKey) =>
46
- isAtomKey(store, depKey)
47
- ? depKey
48
- : traceSelectorAtoms(store, depKey, covered),
49
- )
50
- .map((atomKey) => store.atoms.get(atomKey) as Atom<unknown>)
28
+ return roots
51
29
  }
@@ -2,7 +2,7 @@ import type { ReadonlyPureSelectorToken, WritableToken } from "atom.io"
2
2
 
3
3
  import { newest } from "../lineage"
4
4
  import type { Store } from "../store"
5
- import { traceSelectorAtoms } from "./trace-selector-atoms"
5
+ import { traceRootSelectorAtoms } from "./trace-selector-atoms"
6
6
 
7
7
  export const updateSelectorAtoms = (
8
8
  store: Store,
@@ -29,16 +29,16 @@ export const updateSelectorAtoms = (
29
29
  `discovers root atom "${dependencyKey}"`,
30
30
  )
31
31
  } else {
32
- const rootKeys = traceSelectorAtoms(store, dependencyKey, covered)
32
+ const rootKeys = traceRootSelectorAtoms(store, dependencyKey, covered)
33
33
  store.logger.info(
34
34
  `🔍`,
35
35
  selectorType,
36
36
  selectorKey,
37
- `discovers root atoms: [ ${rootKeys
38
- .map((key) => `"${key}"`)
37
+ `discovers root atoms: [ ${[...rootKeys.values()]
38
+ .map((root) => `"${root.key}"`)
39
39
  .join(`, `)} ]`,
40
40
  )
41
- for (const atomKey of rootKeys) {
41
+ for (const { key: atomKey } of rootKeys.values()) {
42
42
  target.selectorAtoms = target.selectorAtoms.set({
43
43
  selectorKey,
44
44
  atomKey,
@@ -29,7 +29,7 @@ export function evictDownStream(store: Store, atom: Atom<any>): void {
29
29
  if (isDone(target, key)) {
30
30
  continue
31
31
  }
32
- evictCachedValue(key, target)
32
+ evictCachedValue(target, key)
33
33
  markDone(target, key)
34
34
  }
35
35
  }
@@ -49,7 +49,7 @@ export function evictDownStreamFromSelector(
49
49
  if (isDone(target, downstreamSelectorKey)) {
50
50
  continue
51
51
  }
52
- evictCachedValue(downstreamSelectorKey, target)
52
+ evictCachedValue(target, downstreamSelectorKey)
53
53
  markDone(target, downstreamSelectorKey)
54
54
  }
55
55
  }
@@ -1,5 +1,5 @@
1
1
  import type { Atom, WritableState } from ".."
2
- import { traceAllSelectorAtoms } from ".."
2
+ import { traceRootSelectorAtoms } from ".."
3
3
  import type { Store } from "../store"
4
4
  import { setAtom } from "./set-atom"
5
5
 
@@ -23,8 +23,8 @@ export function resetAtomOrSelector(
23
23
  case `writable_pure_selector`:
24
24
  case `writable_held_selector`:
25
25
  {
26
- const atoms = traceAllSelectorAtoms(state, store)
27
- for (const atom of atoms) {
26
+ const atoms = traceRootSelectorAtoms(store, state.key)
27
+ for (const atom of atoms.values()) {
28
28
  resetAtom(store, atom)
29
29
  }
30
30
  }
@@ -193,7 +193,6 @@ export class Store implements Lineage {
193
193
  ...config,
194
194
  }
195
195
  if (store !== null) {
196
- this.valueMap = new Map(store?.valueMap)
197
196
  this.operation = { ...store?.operation }
198
197
  if (isRootStore(store)) {
199
198
  this.transactionMeta = {
@@ -1,46 +1,50 @@
1
- import type { Selector } from ".."
1
+ import type { Atom, Selector } from ".."
2
2
  import { readOrComputeValue } from "../get-state/read-or-compute-value"
3
- import { newest } from "../lineage"
4
- import { traceAllSelectorAtoms } from "../selector"
5
3
  import type { Store } from "../store"
6
4
  import { recallState } from "./recall-state"
7
5
 
8
- export const subscribeToRootAtoms = <T>(
9
- store: Store,
10
- selector: Selector<T>,
11
- ): (() => void)[] => {
12
- const target = newest(store)
13
- const dependencySubscriptions = traceAllSelectorAtoms(selector, store).map(
14
- (atom) => {
15
- return atom.subject.subscribe(
16
- `${selector.type}:${selector.key}`,
17
- (atomChange) => {
18
- store.logger.info(
19
- `📢`,
20
- selector.type,
21
- selector.key,
22
- `root`,
23
- atom.key,
24
- `went`,
25
- atomChange.oldValue,
26
- `->`,
27
- atomChange.newValue,
28
- )
29
- const oldValue = recallState(target, selector)
30
- const newValue = readOrComputeValue(target, selector)
31
- store.logger.info(
32
- `✨`,
33
- selector.type,
34
- selector.key,
35
- `went`,
36
- oldValue,
37
- `->`,
38
- newValue,
39
- )
40
- selector.subject.next({ newValue, oldValue })
41
- },
6
+ export const subscribeToRootDependency = (
7
+ target: Store,
8
+ selector: Selector<any>,
9
+ atom: Atom<any>,
10
+ ): (() => void) => {
11
+ return atom.subject.subscribe(
12
+ `${selector.type}:${selector.key}`,
13
+ (atomChange) => {
14
+ target.logger.info(
15
+ `📢`,
16
+ selector.type,
17
+ selector.key,
18
+ `root`,
19
+ atom.key,
20
+ `went`,
21
+ atomChange.oldValue,
22
+ `->`,
23
+ atomChange.newValue,
42
24
  )
25
+ const oldValue = recallState(target, selector)
26
+ const newValue = readOrComputeValue(target, selector)
27
+ target.logger.info(
28
+ `✨`,
29
+ selector.type,
30
+ selector.key,
31
+ `went`,
32
+ oldValue,
33
+ `->`,
34
+ newValue,
35
+ )
36
+ selector.subject.next({ newValue, oldValue })
43
37
  },
44
38
  )
45
- return dependencySubscriptions
46
39
  }
40
+
41
+ // export const subscribeToRootAtoms = (
42
+ // store: Store,
43
+ // selector: Selector<any>,
44
+ // ): (() => void)[] => {
45
+ // const target = newest(store)
46
+ // const dependencySubscriptions = traceAllSelectorAtoms(selector, store).map(
47
+ // (atom) => subscribeToRootDependency(target, selector, atom),
48
+ // )
49
+ // return dependencySubscriptions
50
+ // }
@@ -1,8 +1,10 @@
1
1
  import type { ReadableToken, StateUpdate, UpdateHandler } from "atom.io"
2
2
 
3
+ import { readOrComputeValue } from "../get-state"
4
+ import { traceRootSelectorAtoms } from "../selector"
3
5
  import type { Store } from "../store"
4
6
  import { withdraw } from "../store"
5
- import { subscribeToRootAtoms } from "./subscribe-to-root-atoms"
7
+ import { subscribeToRootDependency } from "./subscribe-to-root-atoms"
6
8
 
7
9
  export function subscribeToState<T>(
8
10
  store: Store,
@@ -28,14 +30,26 @@ export function subscribeToState<T>(
28
30
  const isSelector =
29
31
  state.type === `writable_pure_selector` ||
30
32
  state.type === `readonly_pure_selector`
31
- let dependencyUnsubFunctions: (() => void)[] | null = null
33
+ const rootSubs = new Map<string, () => void>()
32
34
  let updateHandler: UpdateHandler<T> = safelyHandleUpdate
33
35
  if (isSelector) {
34
- dependencyUnsubFunctions = subscribeToRootAtoms(store, state)
35
- updateHandler = (update) => {
36
- if (dependencyUnsubFunctions) {
37
- dependencyUnsubFunctions.length = 0
38
- dependencyUnsubFunctions.push(...subscribeToRootAtoms(store, state))
36
+ readOrComputeValue(store, state)
37
+ for (const [atomKey, atom] of traceRootSelectorAtoms(store, state.key)) {
38
+ rootSubs.set(atomKey, subscribeToRootDependency(store, state, atom))
39
+ }
40
+ updateHandler = function updateRootsBeforeHandlingUpdate(update) {
41
+ const dependencies = traceRootSelectorAtoms(store, state.key)
42
+ for (const [previousRootKey, unsub] of rootSubs) {
43
+ const currentRoot = dependencies.get(previousRootKey)
44
+ if (currentRoot) {
45
+ dependencies.delete(previousRootKey)
46
+ } else {
47
+ unsub()
48
+ rootSubs.delete(previousRootKey)
49
+ }
50
+ }
51
+ for (const [atomKey, atom] of dependencies) {
52
+ rootSubs.set(atomKey, subscribeToRootDependency(store, state, atom))
39
53
  }
40
54
  safelyHandleUpdate(update)
41
55
  }
@@ -49,10 +63,8 @@ export function subscribeToState<T>(
49
63
  `Removing subscription "${key}"`,
50
64
  )
51
65
  mainUnsubFunction()
52
- if (dependencyUnsubFunctions) {
53
- for (const unsubFromDependency of dependencyUnsubFunctions) {
54
- unsubFromDependency()
55
- }
66
+ for (const unsubFromDependency of rootSubs.values()) {
67
+ unsubFromDependency()
56
68
  }
57
69
  }
58
70
 
package/src/json/index.ts CHANGED
@@ -51,6 +51,9 @@ export const stringifyJson = <J extends Json.Serializable>(
51
51
  json: J,
52
52
  ): stringified<J> => JSON.stringify(json) as stringified<J>
53
53
 
54
+ /**
55
+ * Always serializes to the same string.
56
+ */
54
57
  export type Canonical = primitive | ReadonlyArray<Canonical>
55
58
 
56
59
  export type JsonIO = (...params: Json.Serializable[]) => Json.Serializable | void
package/src/main/atom.ts CHANGED
@@ -22,7 +22,6 @@ import type { AtomToken, MutableAtomToken, RegularAtomToken, Setter } from "."
22
22
  export function atom<T extends Transceiver<any>, J extends Json.Serializable>(
23
23
  options: MutableAtomOptions<T, J>,
24
24
  ): MutableAtomToken<T, J>
25
-
26
25
  /**
27
26
  * @public
28
27
  * Create a regular atom, a global reactive variable in the implicit store
@@ -71,14 +70,24 @@ export type RegularAtomOptions<T> = {
71
70
  /** Hooks used to run side effects when the atom is set */
72
71
  effects?: AtomEffect<T>[]
73
72
  }
74
- // biome-ignore format: complex intersection
75
- export type MutableAtomOptions<T extends Transceiver<any>, J extends Json.Serializable> =
73
+
74
+ /** @public */
75
+ // biome-ignore format: intersection
76
+ export type MutableAtomOptions<
77
+ T extends Transceiver<any>,
78
+ J extends Json.Serializable,
79
+ > =
76
80
  & JsonInterface<T, J>
77
- & Omit<RegularAtomOptions<T>, `default`>
78
- & {
79
- default: () => T
80
- mutable: true
81
- }
81
+ & {
82
+ /** Used to signal that the atom is mutable */
83
+ mutable: true
84
+ /** The unique identifier of the atom */
85
+ key: string
86
+ /** A function to create an initial value for the atom */
87
+ default: () => T
88
+ /** Hooks used to run side effects when the atom is set */
89
+ effects?: AtomEffect<T>[]
90
+ }
82
91
 
83
92
  /** @public */
84
93
  export type RegularAtomFamilyOptions<T, K extends Canonical> = {
@@ -91,24 +100,33 @@ export type RegularAtomFamilyOptions<T, K extends Canonical> = {
91
100
  }
92
101
 
93
102
  export type RegularAtomFamilyToken<T, K extends Canonical> = {
103
+ /** The unique identifier of the atom family */
94
104
  key: string
105
+ /** Discriminator */
95
106
  type: `atom_family`
107
+ /** Never present. This is a marker that preserves the type of atoms in this family */
96
108
  __T?: T
109
+ /** Never present. This is a marker that preserves the type of keys used for atoms in this family */
97
110
  __K?: K
98
111
  }
99
112
 
113
+ /** @public */
100
114
  // biome-ignore format: intersection
101
115
  export type MutableAtomFamilyOptions<
102
116
  T extends Transceiver<any>,
103
117
  J extends Json.Serializable,
104
118
  K extends Canonical,
105
- > =
106
- & JsonInterface<T, J>
107
- & {
119
+ > =
120
+ & JsonInterface<T, J>
121
+ & {
122
+ /** Used to signal that the atoms created from this family are mutable */
123
+ mutable: true
124
+ /** The unique identifier of the atom family */
108
125
  key: string
126
+ /** A function to create an initial value for each atom in the family */
109
127
  default: (key: K) => T
128
+ /** Hooks used to run side effects when an atom in the family is set */
110
129
  effects?: (key: K) => AtomEffect<T>[]
111
- mutable: true,
112
130
  }
113
131
 
114
132
  export type MutableAtomFamilyToken<
@@ -116,22 +134,45 @@ export type MutableAtomFamilyToken<
116
134
  J extends Json.Serializable,
117
135
  K extends Canonical,
118
136
  > = {
137
+ /** The unique identifier of the atom family */
119
138
  key: string
139
+ /** Discriminator */
120
140
  type: `mutable_atom_family`
141
+ /** Never present. This is a marker that preserves the type of atoms in this family */
121
142
  __T?: T
143
+ /** Never present. This is a marker that preserves the type of the JSON form of atoms in this family */
122
144
  __J?: J
145
+ /** Never present. This is a marker that preserves the type of keys used for atoms in this family */
123
146
  __K?: K
124
147
  }
125
-
126
148
  export type AtomFamilyToken<T, K extends Canonical = Canonical> =
127
149
  | MutableAtomFamilyToken<T extends Transceiver<any> ? T : never, any, K>
128
150
  | RegularAtomFamilyToken<T, K>
129
151
 
152
+ /**
153
+ * @public
154
+ * Create a family of mutable atoms, allowing for the dynamic creation and disposal of atoms.
155
+ *
156
+ * The value of a mutable atom must be some kind of {@link Transceiver}.
157
+ *
158
+ * @param options - {@link MutableAtomFamilyOptions}
159
+ * @returns
160
+ * A reference to the atom family created: a {@link MutableAtomFamilyToken}
161
+ * @overload Mutable
162
+ */
130
163
  export function atomFamily<
131
164
  T extends Transceiver<any>,
132
165
  J extends Json.Serializable,
133
166
  K extends Canonical,
134
167
  >(options: MutableAtomFamilyOptions<T, J, K>): MutableAtomFamilyToken<T, J, K>
168
+ /**
169
+ * @public
170
+ * Create a family of regular atoms, allowing for the dynamic creation and disposal of atoms.
171
+ * @param options - {@link RegularAtomFamilyOptions}
172
+ * @returns
173
+ * A reference to the atom family created: a {@link RegularAtomFamilyToken}
174
+ * @overload Regular
175
+ */
135
176
  export function atomFamily<T, K extends Canonical>(
136
177
  options: RegularAtomFamilyOptions<T, K>,
137
178
  ): RegularAtomFamilyToken<T, K>
@@ -5,14 +5,20 @@ import type { ReadableFamilyToken, ReadableToken } from "."
5
5
 
6
6
  /**
7
7
  * @public
8
- * Disposes of a state in the implicit store
8
+ * Disposes of a state in the implicit store.
9
+ *
10
+ * Only family members can be disposed of.
11
+ *
9
12
  * @param token - The token of the state to dispose
10
13
  * @overload Default
11
14
  */
12
15
  export function disposeState(token: ReadableToken<any>): void
13
16
  /**
14
17
  * @public
15
- * Disposes of a state family in the implicit store
18
+ * Disposes of a state in the implicit store.
19
+ *
20
+ * Only family members can be disposed of.
21
+ *
16
22
  * @param token - The token of the state family to dispose
17
23
  * @param key - The unique key of the state to dispose
18
24
  */