atom.io 0.36.0 → 0.36.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 (100) hide show
  1. package/dist/internal/index.d.ts +83 -67
  2. package/dist/internal/index.d.ts.map +1 -1
  3. package/dist/internal/index.js +98 -98
  4. package/dist/internal/index.js.map +1 -1
  5. package/dist/introspection/index.d.ts +5 -6
  6. package/dist/introspection/index.d.ts.map +1 -1
  7. package/dist/introspection/index.js +2 -3
  8. package/dist/introspection/index.js.map +1 -1
  9. package/dist/main/index.d.ts +27 -29
  10. package/dist/main/index.d.ts.map +1 -1
  11. package/dist/main/index.js +8 -9
  12. package/dist/main/index.js.map +1 -1
  13. package/dist/react/index.d.ts +2 -2
  14. package/dist/react/index.d.ts.map +1 -1
  15. package/dist/react/index.js.map +1 -1
  16. package/dist/react-devtools/index.d.ts.map +1 -1
  17. package/dist/react-devtools/index.js.map +1 -1
  18. package/dist/realtime-client/index.d.ts +7 -8
  19. package/dist/realtime-client/index.d.ts.map +1 -1
  20. package/dist/realtime-client/index.js +3 -4
  21. package/dist/realtime-client/index.js.map +1 -1
  22. package/dist/realtime-react/index.d.ts +4 -4
  23. package/dist/realtime-react/index.d.ts.map +1 -1
  24. package/dist/realtime-react/index.js.map +1 -1
  25. package/dist/realtime-server/index.d.ts +2 -2
  26. package/dist/realtime-server/index.d.ts.map +1 -1
  27. package/dist/realtime-server/index.js.map +1 -1
  28. package/dist/transceivers/set-rtx/index.d.ts +9 -2
  29. package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
  30. package/dist/transceivers/set-rtx/index.js +3 -0
  31. package/dist/transceivers/set-rtx/index.js.map +1 -1
  32. package/package.json +3 -3
  33. package/src/internal/atom/create-regular-atom.ts +5 -5
  34. package/src/internal/atom/has-role.ts +12 -0
  35. package/src/internal/atom/index.ts +1 -0
  36. package/src/internal/caching.ts +38 -16
  37. package/src/internal/families/find-in-store.ts +1 -1
  38. package/src/internal/families/get-family-of-token.ts +1 -1
  39. package/src/internal/families/init-family-member.ts +1 -1
  40. package/src/internal/families/seek-in-store.ts +1 -1
  41. package/src/internal/get-state/read-or-compute-value.ts +29 -20
  42. package/src/internal/index.ts +8 -8
  43. package/src/internal/join/join-internal.ts +7 -4
  44. package/src/internal/mutable/create-mutable-atom-family.ts +6 -5
  45. package/src/internal/mutable/create-mutable-atom.ts +1 -1
  46. package/src/internal/mutable/get-json-family.ts +1 -1
  47. package/src/internal/mutable/get-json-token.ts +1 -1
  48. package/src/internal/mutable/get-update-family.ts +1 -1
  49. package/src/internal/mutable/get-update-token.ts +1 -1
  50. package/src/internal/mutable/tracker-family.ts +27 -31
  51. package/src/internal/mutable/tracker.ts +29 -23
  52. package/src/internal/mutable/transceiver.ts +11 -10
  53. package/src/internal/selector/create-readonly-held-selector.ts +2 -2
  54. package/src/internal/selector/create-readonly-pure-selector.ts +2 -2
  55. package/src/internal/selector/create-writable-held-selector.ts +3 -3
  56. package/src/internal/selector/create-writable-pure-selector.ts +3 -3
  57. package/src/internal/set-state/set-atom.ts +15 -22
  58. package/src/internal/set-state/set-into-store.ts +5 -4
  59. package/src/internal/store/counterfeit.ts +1 -1
  60. package/src/internal/store/deposit.ts +7 -8
  61. package/src/internal/store/store.ts +6 -6
  62. package/src/internal/store/withdraw.ts +8 -8
  63. package/src/internal/subscribe/subscribe-in-store.ts +2 -2
  64. package/src/internal/subscribe/subscribe-to-transaction.ts +2 -2
  65. package/src/internal/timeline/create-timeline.ts +3 -3
  66. package/src/internal/transaction/act-upon-store.ts +2 -2
  67. package/src/internal/transaction/apply-transaction.ts +2 -2
  68. package/src/internal/transaction/build-transaction.ts +2 -2
  69. package/src/internal/transaction/create-transaction.ts +3 -3
  70. package/src/internal/transaction/index.ts +2 -2
  71. package/src/internal/transaction/is-root-store.ts +4 -2
  72. package/src/internal/utility-types.ts +1 -1
  73. package/src/introspection/attach-introspection-states.ts +3 -3
  74. package/src/introspection/attach-transaction-index.ts +4 -4
  75. package/src/introspection/attach-transaction-logs.ts +4 -4
  76. package/src/introspection/auditor.ts +3 -3
  77. package/src/main/atom.ts +4 -4
  78. package/src/main/dispose-state.ts +4 -3
  79. package/src/main/find-state.ts +1 -1
  80. package/src/main/get-state.ts +6 -5
  81. package/src/main/reset-state.ts +3 -3
  82. package/src/main/set-state.ts +3 -3
  83. package/src/main/subscribe.ts +3 -3
  84. package/src/main/tokens.ts +6 -10
  85. package/src/main/transaction.ts +13 -15
  86. package/src/react/use-json.ts +5 -5
  87. package/src/react-devtools/TransactionIndex.tsx +3 -3
  88. package/src/react-devtools/Updates.tsx +2 -2
  89. package/src/realtime-client/continuity/register-and-attempt-confirmed-update.ts +2 -2
  90. package/src/realtime-client/pull-mutable-atom-family-member.ts +8 -13
  91. package/src/realtime-client/pull-mutable-atom.ts +5 -8
  92. package/src/realtime-client/push-state.ts +6 -5
  93. package/src/realtime-client/server-action.ts +5 -4
  94. package/src/realtime-react/use-pull-mutable-atom.ts +1 -1
  95. package/src/realtime-react/use-pull-mutable-family-member.ts +1 -1
  96. package/src/realtime-react/use-server-action.ts +2 -2
  97. package/src/realtime-server/realtime-mutable-family-provider.ts +1 -1
  98. package/src/realtime-server/realtime-mutable-provider.ts +1 -1
  99. package/src/transceivers/set-rtx/set-rtx.ts +14 -1
  100. package/src/internal/set-state/copy-mutable-if-needed.ts +0 -29
@@ -1,9 +1,8 @@
1
1
  import type { StateUpdate } from "atom.io"
2
2
 
3
- import type { ReadableState } from "."
4
- import { closeOperation, isChildStore, openOperation } from "."
3
+ import type { ReadableState, Transceiver } from "."
4
+ import { closeOperation, isChildStore, openOperation, Tracker } from "."
5
5
  import { Future } from "./future"
6
- import { copyMutableIfNeeded } from "./set-state/copy-mutable-if-needed"
7
6
  import {
8
7
  evictDownStream,
9
8
  evictDownStreamFromSelector,
@@ -11,19 +10,19 @@ import {
11
10
  import type { Store } from "./store"
12
11
  import type { Subject } from "./subject"
13
12
 
14
- export function cacheValue<T>(
15
- store: Store,
13
+ export function writeToCache<T>(
14
+ target: Store,
16
15
  key: string,
17
16
  value: T,
18
17
  subject: Subject<StateUpdate<unknown>>,
19
18
  ): T
20
- export function cacheValue<T extends Promise<any>>(
21
- store: Store,
19
+ export function writeToCache<T extends Promise<any>>(
20
+ target: Store,
22
21
  key: string,
23
22
  value: T,
24
23
  subject: Subject<StateUpdate<unknown>>,
25
- ): Future<T>
26
- export function cacheValue<T>(
24
+ ): Future<Awaited<T>>
25
+ export function writeToCache<T>(
27
26
  target: Store,
28
27
  key: string,
29
28
  value: T,
@@ -46,7 +45,7 @@ export function cacheValue<T>(
46
45
  .then(function handleResolvedFuture(resolved) {
47
46
  const current = target.valueMap.get(key)
48
47
  if (current === future) {
49
- cacheValue(target, key, resolved, subject)
48
+ writeToCache(target, key, resolved, subject)
50
49
  const atom = target.atoms.get(key)
51
50
  if (atom) {
52
51
  openOperation(target, atom)
@@ -74,21 +73,44 @@ export function cacheValue<T>(
74
73
  return value
75
74
  }
76
75
 
77
- export const readCachedValue = <T>(
78
- state: ReadableState<any>,
76
+ /**
77
+ * @param target - the newest layer of the store
78
+ * @param state - the state to read from cache
79
+ * @param mut - whether the value is intended to be mutable
80
+ * @returns the state's current value
81
+ */
82
+ export function readFromCache<T>(
79
83
  target: Store,
80
- ): T => {
84
+ state: ReadableState<T>,
85
+ mut: `mut` | undefined,
86
+ ): T {
81
87
  target.logger.info(`📖`, state.type, state.key, `reading cached value`)
82
88
  let value = target.valueMap.get(state.key) as T
83
- if (state.type === `mutable_atom` && isChildStore(target)) {
89
+
90
+ const mayNeedToBeCopied =
91
+ mut === `mut` && state.type === `mutable_atom` && isChildStore(target)
92
+ if (mayNeedToBeCopied) {
93
+ const mutableAtom = state
84
94
  const { parent } = target
85
- const copiedValue = copyMutableIfNeeded(target, state, parent)
95
+
96
+ if (target.valueMap.hasOwn(mutableAtom.key)) {
97
+ return value
98
+ }
99
+
100
+ const parentValue = parent.valueMap.get(mutableAtom.key) as T &
101
+ Transceiver<any, any, any>
102
+
103
+ target.logger.info(`📃`, `atom`, mutableAtom.key, `copying`)
104
+ const jsonValue = parentValue.toJSON()
105
+ const copiedValue = mutableAtom.class.fromJSON(jsonValue)
106
+ target.valueMap.set(mutableAtom.key, copiedValue)
107
+ new Tracker(mutableAtom, parent)
86
108
  value = copiedValue
87
109
  }
88
110
  return value
89
111
  }
90
112
 
91
- export const evictCachedValue = (target: Store, key: string): void => {
113
+ export function evictCachedValue(target: Store, key: string): void {
92
114
  const currentValue = target.valueMap.get(key)
93
115
  if (currentValue instanceof Future) {
94
116
  const selector =
@@ -25,7 +25,7 @@ import { initFamilyMemberInStore } from "./init-family-member"
25
25
  import { seekInStore } from "./seek-in-store"
26
26
 
27
27
  export function findInStore<
28
- T extends Transceiver<any, any>,
28
+ T extends Transceiver<any, any, any>,
29
29
  K extends Canonical,
30
30
  Key extends K,
31
31
  >(
@@ -18,7 +18,7 @@ import type { Transceiver } from "../mutable"
18
18
  import type { Store } from "../store"
19
19
 
20
20
  export function getFamilyOfToken<
21
- T extends Transceiver<any, any>,
21
+ T extends Transceiver<any, any, any>,
22
22
  K extends Canonical,
23
23
  >(
24
24
  store: Store,
@@ -25,7 +25,7 @@ import type { Store } from "../store"
25
25
  import { isChildStore, isRootStore } from "../transaction"
26
26
 
27
27
  export function initFamilyMemberInStore<
28
- T extends Transceiver<any, any>,
28
+ T extends Transceiver<any, any, any>,
29
29
  K extends Canonical,
30
30
  Key extends K,
31
31
  >(
@@ -25,7 +25,7 @@ import type { Transceiver } from "../mutable"
25
25
  import { deposit, type Store } from "../store"
26
26
 
27
27
  export function seekInStore<
28
- T extends Transceiver<any, any>,
28
+ T extends Transceiver<any, any, any>,
29
29
  K extends Canonical,
30
30
  Key extends K,
31
31
  >(
@@ -1,13 +1,24 @@
1
- import type { ReadableState } from ".."
2
- import { cacheValue, readCachedValue } from "../caching"
1
+ import type { ReadableState, ViewOf } from ".."
2
+ import { readFromCache, writeToCache } from "../caching"
3
3
  import type { Store } from "../store"
4
4
 
5
- export const readOrComputeValue = <T>(
5
+ export function readOrComputeValue<T>(
6
6
  target: Store,
7
7
  state: ReadableState<T>,
8
- ): T => {
8
+ mut?: undefined,
9
+ ): ViewOf<T>
10
+ export function readOrComputeValue<T>(
11
+ target: Store,
12
+ state: ReadableState<T>,
13
+ mut: `mut`,
14
+ ): T
15
+ export function readOrComputeValue<T>(
16
+ target: Store,
17
+ state: ReadableState<T>,
18
+ mut: `mut` | undefined,
19
+ ): T {
9
20
  if (target.valueMap.has(state.key)) {
10
- return readCachedValue(state, target)
21
+ return readFromCache(target, state, mut)
11
22
  }
12
23
  switch (state.type) {
13
24
  case `readonly_held_selector`:
@@ -17,32 +28,30 @@ export const readOrComputeValue = <T>(
17
28
  target.logger.info(`🧮`, state.type, state.key, `computing value`)
18
29
  return state.get()
19
30
  case `atom`: {
20
- const def = state.default
21
- let defaultValue: T
22
- if (def instanceof Function) {
23
- defaultValue = def()
31
+ let def: T
32
+ if (state.default instanceof Function) {
33
+ def = state.default()
24
34
  } else {
25
- defaultValue = def
35
+ def = state.default
26
36
  }
27
- const cachedValue = cacheValue(
28
- target,
29
- state.key,
30
- defaultValue,
31
- state.subject,
32
- )
37
+ const cachedValue = writeToCache(target, state.key, def, state.subject)
33
38
  target.logger.info(
34
39
  `💁`,
35
40
  `atom`,
36
41
  state.key,
37
42
  `could not find cached value; using default`,
38
- defaultValue,
43
+ def,
39
44
  )
40
45
  return cachedValue
41
46
  }
42
47
  case `mutable_atom`: {
43
- const Ctor = state.class
44
- const instance = new Ctor()
45
- const cachedValue = cacheValue(target, state.key, instance, state.subject)
48
+ const instance = new state.class()
49
+ const cachedValue = writeToCache(
50
+ target,
51
+ state.key,
52
+ instance,
53
+ state.subject,
54
+ )
46
55
  target.logger.info(
47
56
  `💁`,
48
57
  `mutable_atom`,
@@ -11,6 +11,7 @@ import type {
11
11
  RegularAtomToken,
12
12
  StateCreation,
13
13
  StateDisposal,
14
+ StateLifecycleEvent,
14
15
  WritableHeldSelectorFamilyToken,
15
16
  WritableHeldSelectorToken,
16
17
  WritablePureSelectorFamilyToken,
@@ -18,6 +19,7 @@ import type {
18
19
  } from "atom.io"
19
20
  import type { Canonical } from "atom.io/json"
20
21
 
22
+ import type { internalRole } from "./atom/has-role"
21
23
  import type { ConstructorOf, Transceiver } from "./mutable"
22
24
  import type { Store } from "./store"
23
25
  import type { Subject } from "./subject"
@@ -67,9 +69,10 @@ export type RegularAtom<T> = Flat<
67
69
  type: `atom`
68
70
  default: T | (() => T)
69
71
  cleanup?: () => void
72
+ internalRoles?: internalRole[]
70
73
  }
71
74
  >
72
- export type MutableAtom<T extends Transceiver<any, any>> = Flat<
75
+ export type MutableAtom<T extends Transceiver<any, any, any>> = Flat<
73
76
  AtomIOState & {
74
77
  type: `mutable_atom`
75
78
  class: ConstructorOf<T>
@@ -78,7 +81,7 @@ export type MutableAtom<T extends Transceiver<any, any>> = Flat<
78
81
  >
79
82
  export type Atom<T> =
80
83
  | RegularAtom<T>
81
- | (T extends Transceiver<any, any> ? MutableAtom<T> : never)
84
+ | (T extends Transceiver<any, any, any> ? MutableAtom<T> : never)
82
85
 
83
86
  export type WritableHeldSelector<T> = Flat<
84
87
  AtomIOState & {
@@ -138,7 +141,7 @@ export type RegularAtomFamily<T, K extends Canonical> =
138
141
  // biome-ignore format: intersection
139
142
  export type MutableAtomFamily<
140
143
  // C extends TransceiverConstructor<any,any>,
141
- T extends Transceiver<any,any>,
144
+ T extends Transceiver<any, any, any>,
142
145
  K extends Canonical,
143
146
  > =
144
147
  & Flat<
@@ -146,16 +149,13 @@ export type MutableAtomFamily<
146
149
  & {
147
150
  install: (store: Store) => void
148
151
  internalRoles: string[] | undefined
149
- subject: Subject<
150
- | StateCreation<MutableAtomToken<T>>
151
- | StateDisposal<MutableAtomToken<T>>
152
- >
152
+ subject: Subject<StateLifecycleEvent<MutableAtomToken<T>>>
153
153
  }
154
154
  >
155
155
  & ((key: K) => MutableAtomToken<T>)
156
156
 
157
157
  export type AtomFamily<T, K extends Canonical = Canonical> =
158
- | MutableAtomFamily<T extends Transceiver<any, any> ? T : never, K>
158
+ | MutableAtomFamily<T extends Transceiver<any, any, any> ? T : never, K>
159
159
  | RegularAtomFamily<T, K>
160
160
 
161
161
  // biome-ignore format: intersection
@@ -29,6 +29,7 @@ import type {
29
29
  } from "../junction"
30
30
  import { Junction } from "../junction"
31
31
  import type { Molecule } from "../molecule"
32
+ import type { ViewOf } from "../mutable"
32
33
  import { createMutableAtomFamily, getJsonFamily, getJsonToken } from "../mutable"
33
34
  import { setIntoStore } from "../set-state"
34
35
  import type { Store } from "../store"
@@ -343,8 +344,10 @@ export class Join<
343
344
  [`join`, `content`],
344
345
  )
345
346
 
346
- const getContent: Read<(key: string) => Content | null> = ({ get }, key) =>
347
- get(contentAtoms, key)
347
+ const getContent: Read<(key: string) => ViewOf<Content> | null> = (
348
+ { get },
349
+ key,
350
+ ) => get(contentAtoms, key)
348
351
  const setContent: Write<(key: string, content: Content) => void> = (
349
352
  { set },
350
353
  key,
@@ -433,7 +436,7 @@ export class Join<
433
436
  )
434
437
  }
435
438
  const createSingleEntrySelectorFamily = () =>
436
- createReadonlyPureSelectorFamily<[string, Content] | null, string>(
439
+ createReadonlyPureSelectorFamily<[string, ViewOf<Content>] | null, string>(
437
440
  store,
438
441
  {
439
442
  key: `${options.key}/singleRelatedEntry`,
@@ -456,7 +459,7 @@ export class Join<
456
459
  [`join`, `entries`],
457
460
  )
458
461
  const getMultipleEntrySelectorFamily = () =>
459
- createReadonlyPureSelectorFamily<[string, Content][], string>(
462
+ createReadonlyPureSelectorFamily<[string, ViewOf<Content>][], string>(
460
463
  store,
461
464
  {
462
465
  key: `${options.key}/multipleRelatedEntries`,
@@ -23,17 +23,18 @@ import { FamilyTracker } from "./tracker-family"
23
23
  import type { AsJSON, Transceiver } from "./transceiver"
24
24
 
25
25
  export function createMutableAtomFamily<
26
- T extends Transceiver<any, any>,
26
+ T extends Transceiver<any, any, any>,
27
27
  K extends Canonical,
28
28
  >(
29
29
  store: Store,
30
30
  options: MutableAtomFamilyOptions<T, K>,
31
31
  internalRoles?: string[],
32
32
  ): MutableAtomFamilyToken<T, K> {
33
- const familyToken: MutableAtomFamilyToken<T & Transceiver<any, any>, K> = {
34
- key: options.key,
35
- type: `mutable_atom_family`,
36
- }
33
+ const familyToken: MutableAtomFamilyToken<T & Transceiver<any, any, any>, K> =
34
+ {
35
+ key: options.key,
36
+ type: `mutable_atom_family`,
37
+ }
37
38
 
38
39
  const existing = store.families.get(options.key)
39
40
  if (existing) {
@@ -14,7 +14,7 @@ import { subscribeToState } from "../subscribe"
14
14
  import { Tracker } from "./tracker"
15
15
  import type { Transceiver } from "./transceiver"
16
16
 
17
- export function createMutableAtom<T extends Transceiver<any, any>>(
17
+ export function createMutableAtom<T extends Transceiver<any, any, any>>(
18
18
  store: Store,
19
19
  options: MutableAtomOptions<T>,
20
20
  family: FamilyMetadata | undefined,
@@ -7,7 +7,7 @@ import type { Store } from "../store"
7
7
  import type { Transceiver } from "./transceiver"
8
8
 
9
9
  export const getJsonFamily = <
10
- Core extends Transceiver<Json.Serializable, Json.Serializable>,
10
+ Core extends Transceiver<any, Json.Serializable, Json.Serializable>,
11
11
  Key extends Canonical,
12
12
  >(
13
13
  mutableAtomFamily: MutableAtomFamilyToken<Core, Key>,
@@ -9,7 +9,7 @@ import { newest } from "../lineage"
9
9
  import { type Store, withdraw } from "../store"
10
10
  import type { AsJSON, Transceiver } from "./transceiver"
11
11
 
12
- export const getJsonToken = <T extends Transceiver<any, any>>(
12
+ export const getJsonToken = <T extends Transceiver<any, any, any>>(
13
13
  store: Store,
14
14
  mutableAtomToken: MutableAtomToken<T>,
15
15
  ): WritablePureSelectorToken<AsJSON<T>> => {
@@ -7,7 +7,7 @@ import type { Store } from "../store"
7
7
  import type { SignalFrom, Transceiver } from "./transceiver"
8
8
 
9
9
  export const getUpdateFamily = <
10
- T extends Transceiver<Json.Serializable, Json.Serializable>,
10
+ T extends Transceiver<any, Json.Serializable, Json.Serializable>,
11
11
  K extends string,
12
12
  >(
13
13
  mutableAtomFamily: MutableAtomFamilyToken<T, K>,
@@ -2,7 +2,7 @@ import type { MutableAtomToken, RegularAtomToken } from "atom.io"
2
2
 
3
3
  import type { SignalFrom, Transceiver } from "./transceiver"
4
4
 
5
- export const getUpdateToken = <T extends Transceiver<any, any>>(
5
+ export const getUpdateToken = <T extends Transceiver<any, any, any>>(
6
6
  mutableAtomToken: MutableAtomToken<T>,
7
7
  ): RegularAtomToken<SignalFrom<T>> => {
8
8
  const key = `*${mutableAtomToken.key}`
@@ -1,3 +1,4 @@
1
+ import type { MutableAtomToken, StateLifecycleEvent } from "atom.io"
1
2
  import type { Canonical } from "atom.io/json"
2
3
  import { parseJson } from "atom.io/json"
3
4
 
@@ -5,25 +6,19 @@ import type { MutableAtomFamily, RegularAtomFamily } from ".."
5
6
  import { createRegularAtomFamily } from "../families"
6
7
  import { type Store, withdraw } from "../store"
7
8
  import { Tracker } from "./tracker"
8
- import type { Transceiver } from "./transceiver"
9
+ import type { SignalFrom, Transceiver } from "./transceiver"
9
10
 
10
11
  export class FamilyTracker<
11
- T extends Transceiver<any, any>,
12
+ T extends Transceiver<any, any, any>,
12
13
  K extends Canonical,
13
14
  > {
14
15
  private trackers: Map<K, Tracker<T>> = new Map()
15
16
 
16
- public readonly latestUpdateAtoms: RegularAtomFamily<
17
- (T extends Transceiver<infer Signal, any> ? Signal : never) | null,
18
- K
19
- >
17
+ public readonly latestSignalAtoms: RegularAtomFamily<SignalFrom<T> | null, K>
20
18
  public readonly mutableAtoms: MutableAtomFamily<T, K>
21
19
 
22
20
  public constructor(mutableAtoms: MutableAtomFamily<T, K>, store: Store) {
23
- const updateAtoms = createRegularAtomFamily<
24
- (T extends Transceiver<infer Signal, any> ? Signal : never) | null,
25
- K
26
- >(
21
+ const latestSignalAtoms = createRegularAtomFamily<SignalFrom<T> | null, K>(
27
22
  store,
28
23
  {
29
24
  key: `*${mutableAtoms.key}`,
@@ -31,30 +26,31 @@ export class FamilyTracker<
31
26
  },
32
27
  [`mutable`, `updates`],
33
28
  )
34
- this.latestUpdateAtoms = withdraw(store, updateAtoms)
29
+ this.latestSignalAtoms = withdraw(store, latestSignalAtoms)
35
30
  this.mutableAtoms = mutableAtoms
36
- this.mutableAtoms.subject.subscribe(
37
- `store=${store.config.name}::tracker-atom-family`,
38
- (event) => {
39
- const { type, token } = event
40
- if (token.family) {
41
- const key = parseJson(token.family.subKey)
42
- switch (type) {
43
- case `state_creation`:
44
- this.trackers.set(key, new Tracker<T>(token, store))
45
- break
46
- case `state_disposal`:
47
- {
48
- const tracker = this.trackers.get(key)
49
- if (tracker) {
50
- tracker[Symbol.dispose]()
51
- this.trackers.delete(key)
52
- }
53
- }
54
- break
31
+ const trackerFamilyWatchesForCreationAndDisposalEvents = (
32
+ event: StateLifecycleEvent<MutableAtomToken<T>>,
33
+ ) => {
34
+ const { type, token } = event
35
+ if (token.family) {
36
+ const key = parseJson(token.family.subKey)
37
+ switch (type) {
38
+ case `state_creation`:
39
+ this.trackers.set(key, new Tracker<T>(token, store))
40
+ break
41
+ case `state_disposal`: {
42
+ const tracker = this.trackers.get(key)
43
+ if (tracker) {
44
+ tracker[Symbol.dispose]()
45
+ this.trackers.delete(key)
46
+ }
55
47
  }
56
48
  }
57
- },
49
+ }
50
+ }
51
+ this.mutableAtoms.subject.subscribe(
52
+ `store=${store.config.name}::tracker-atom-family`,
53
+ trackerFamilyWatchesForCreationAndDisposalEvents,
58
54
  )
59
55
  }
60
56
  }
@@ -17,48 +17,52 @@ import type { SignalFrom, Transceiver } from "./transceiver"
17
17
  * subscribe to the transceiver's inner value. When the inner value changes,
18
18
  * the tracker will update its own state to reflect the change.
19
19
  */
20
- export class Tracker<T extends Transceiver<any, any>> {
21
- private initializeState(
20
+ export class Tracker<T extends Transceiver<any, any, any>> {
21
+ private initializeSignalAtom(
22
22
  mutableState: MutableAtomToken<T>,
23
23
  store: Store,
24
24
  ): RegularAtomToken<SignalFrom<T> | null> {
25
- const latestUpdateStateKey = `*${mutableState.key}`
26
- store.atoms.delete(latestUpdateStateKey)
27
- store.valueMap.delete(latestUpdateStateKey)
25
+ const latestSignalStateKey = `*${mutableState.key}`
26
+ store.atoms.delete(latestSignalStateKey)
27
+ store.valueMap.delete(latestSignalStateKey)
28
28
  const familyMetaData: FamilyMetadata | undefined = mutableState.family
29
29
  ? {
30
30
  key: `*${mutableState.family.key}`,
31
31
  subKey: mutableState.family.subKey,
32
32
  }
33
33
  : undefined
34
- const latestUpdateState = createRegularAtom<SignalFrom<T> | null>(
34
+ const latestSignalState = createRegularAtom<SignalFrom<T> | null>(
35
35
  store,
36
36
  {
37
- key: latestUpdateStateKey,
37
+ key: latestSignalStateKey,
38
38
  default: null,
39
39
  },
40
40
  familyMetaData,
41
+ [`tracker:signal`],
41
42
  )
42
- if (store.parent?.valueMap.has(latestUpdateStateKey)) {
43
- const parentValue = store.parent.valueMap.get(latestUpdateStateKey)
44
- store.valueMap.set(latestUpdateStateKey, parentValue)
43
+ if (store.parent?.valueMap.has(latestSignalStateKey)) {
44
+ const parentValue = store.parent.valueMap.get(latestSignalStateKey)
45
+ store.valueMap.set(latestSignalStateKey, parentValue)
45
46
  }
46
47
 
47
- return latestUpdateState
48
+ return latestSignalState
48
49
  }
49
50
 
50
51
  private unsubscribeFromInnerValue!: () => void
51
52
  private unsubscribeFromState!: () => void
52
53
  private captureSignalsFromCore(
53
54
  mutableState: MutableAtomToken<T, any>,
54
- latestUpdateState: RegularAtomToken<SignalFrom<T> | null>,
55
+ latestSignalState: RegularAtomToken<SignalFrom<T> | null>,
55
56
  target: Store,
56
57
  ): void {
57
- const subscriptionKey = `tracker:${target.config.name}:${
58
- isChildStore(target) ? target.transactionMeta.update.key : `main`
59
- }:${mutableState.key}`
58
+ const stateKey = mutableState.key
59
+ const storeName = target.config.name
60
+ const storeStatus = isChildStore(target)
61
+ ? target.transactionMeta.update.key
62
+ : `main`
63
+ const subscriptionKey = `tracker:${storeName}:${storeStatus}:${stateKey}`
60
64
  const trackerCapturesOutboundSignal = (update: SignalFrom<T>) => {
61
- setIntoStore(target, latestUpdateState, update)
65
+ setIntoStore(target, latestSignalState, update)
62
66
  }
63
67
  const originalInnerValue = getFromStore(target, mutableState)
64
68
  this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
@@ -83,7 +87,7 @@ export class Tracker<T extends Transceiver<any, any>> {
83
87
 
84
88
  private supplySignalsToCore(
85
89
  mutableState: MutableAtomToken<T>,
86
- latestUpdateState: RegularAtomToken<SignalFrom<T> | null>,
90
+ latestSignalState: RegularAtomToken<SignalFrom<T> | null>,
87
91
  target: Store,
88
92
  ): void {
89
93
  const subscriptionKey = `tracker:${target.config.name}:${
@@ -91,11 +95,11 @@ export class Tracker<T extends Transceiver<any, any>> {
91
95
  }:${mutableState.key}`
92
96
  subscribeToState(
93
97
  target,
94
- latestUpdateState,
98
+ latestSignalState,
95
99
  subscriptionKey,
96
100
  function trackerCapturesInboundSignal({ newValue, oldValue }) {
97
101
  const timelineId = target.timelineTopics.getRelatedKey(
98
- latestUpdateState.key,
102
+ latestSignalState.key,
99
103
  )
100
104
 
101
105
  if (timelineId && target.timelines.get(timelineId)?.timeTraveling) {
@@ -128,13 +132,15 @@ export class Tracker<T extends Transceiver<any, any>> {
128
132
  (transceiver) => (transceiver.do(newValue), transceiver),
129
133
  )
130
134
  } else {
135
+ const expected = mutable.cacheUpdateNumber + 1
131
136
  target.logger.info(
132
137
  `❌`,
133
138
  `mutable_atom`,
134
139
  mutableState.key,
135
- `could not be updated. Expected update number ${
136
- mutable.cacheUpdateNumber + 1
137
- }, but got ${updateNumber}`,
140
+ `could not be updated. Expected update number`,
141
+ expected,
142
+ `but got`,
143
+ updateNumber,
138
144
  )
139
145
  }
140
146
  },
@@ -148,7 +154,7 @@ export class Tracker<T extends Transceiver<any, any>> {
148
154
 
149
155
  public constructor(mutableAtomToken: MutableAtomToken<T>, store: Store) {
150
156
  const target = newest(store)
151
- const latestSignalToken = this.initializeState(mutableAtomToken, target)
157
+ const latestSignalToken = this.initializeSignalAtom(mutableAtomToken, target)
152
158
  this.mutableAtomToken = mutableAtomToken
153
159
  this.latestSignalToken = latestSignalToken
154
160
  this.captureSignalsFromCore(mutableAtomToken, latestSignalToken, target)
@@ -1,6 +1,7 @@
1
1
  import type { Json } from "atom.io/json"
2
2
 
3
3
  export interface Transceiver<
4
+ V,
4
5
  S extends Json.Serializable,
5
6
  J extends Json.Serializable,
6
7
  > {
@@ -9,20 +10,21 @@ export interface Transceiver<
9
10
  subscribe: (key: string, fn: (update: S) => void) => () => void
10
11
  cacheUpdateNumber: number
11
12
  getUpdateNumber: (update: S) => number
13
+ view: () => V
12
14
  toJSON: () => J
13
15
  }
14
16
 
15
17
  // biome-ignore format: intersection
16
18
  export type TransceiverConstructor<
17
19
  J extends Json.Serializable,
18
- T extends Transceiver<any, J>
20
+ T extends Transceiver<any, any, J>
19
21
  > =
20
22
  & ( new () => T )
21
23
  & { fromJSON: (json: J) => T }
22
24
 
23
25
  export function isTransceiver(
24
26
  value: unknown,
25
- ): value is Transceiver<Json.Serializable, Json.Serializable> {
27
+ ): value is Transceiver<any, Json.Serializable, Json.Serializable> {
26
28
  return (
27
29
  typeof value === `object` &&
28
30
  value !== null &&
@@ -34,21 +36,20 @@ export function isTransceiver(
34
36
 
35
37
  export type TransceiverMode = `playback` | `record` | `transaction`
36
38
 
37
- export type SignalFrom<T extends Transceiver<any, any>> = T extends Transceiver<
38
- infer S,
39
- any
40
- >
41
- ? S
42
- : never
39
+ export type ViewOf<T> = T extends Transceiver<infer V, any, any> ? V : T
43
40
 
44
- export type AsJSON<T extends Transceiver<any, any>> = T extends Transceiver<
41
+ export type SignalFrom<T extends Transceiver<any, any, any>> =
42
+ T extends Transceiver<any, infer S, any> ? S : never
43
+
44
+ export type AsJSON<T extends Transceiver<any, any, any>> = T extends Transceiver<
45
+ any,
45
46
  any,
46
47
  infer J
47
48
  >
48
49
  ? J
49
50
  : never
50
51
 
51
- export type ConstructorOf<T extends Transceiver<any, any>> =
52
+ export type ConstructorOf<T extends Transceiver<any, any, any>> =
52
53
  TransceiverConstructor<AsJSON<T>, T>
53
54
 
54
55
  /*