atom.io 0.34.2 โ†’ 0.36.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 (113) hide show
  1. package/dist/eslint-plugin/index.d.ts +2 -18
  2. package/dist/eslint-plugin/index.d.ts.map +1 -1
  3. package/dist/eslint-plugin/index.js +4 -141
  4. package/dist/eslint-plugin/index.js.map +1 -1
  5. package/dist/internal/index.d.ts +74 -77
  6. package/dist/internal/index.d.ts.map +1 -1
  7. package/dist/internal/index.js +197 -202
  8. package/dist/internal/index.js.map +1 -1
  9. package/dist/json/index.d.ts +9 -17
  10. package/dist/json/index.d.ts.map +1 -1
  11. package/dist/json/index.js +3 -33
  12. package/dist/json/index.js.map +1 -1
  13. package/dist/main/index.d.ts +689 -795
  14. package/dist/main/index.d.ts.map +1 -1
  15. package/dist/main/index.js +55 -23
  16. package/dist/main/index.js.map +1 -1
  17. package/dist/react/index.d.ts +4 -4
  18. package/dist/react/index.d.ts.map +1 -1
  19. package/dist/react/index.js.map +1 -1
  20. package/dist/react-devtools/index.d.ts.map +1 -1
  21. package/dist/react-devtools/index.js +10 -10
  22. package/dist/react-devtools/index.js.map +1 -1
  23. package/dist/realtime/index.d.ts +4 -4
  24. package/dist/realtime/index.d.ts.map +1 -1
  25. package/dist/realtime/index.js +5 -11
  26. package/dist/realtime/index.js.map +1 -1
  27. package/dist/realtime-client/index.d.ts +2 -2
  28. package/dist/realtime-client/index.d.ts.map +1 -1
  29. package/dist/realtime-client/index.js +10 -10
  30. package/dist/realtime-client/index.js.map +1 -1
  31. package/dist/realtime-react/index.d.ts +2 -2
  32. package/dist/realtime-react/index.d.ts.map +1 -1
  33. package/dist/realtime-react/index.js.map +1 -1
  34. package/dist/realtime-server/index.d.ts +18 -18
  35. package/dist/realtime-server/index.d.ts.map +1 -1
  36. package/dist/realtime-server/index.js +13 -19
  37. package/dist/realtime-server/index.js.map +1 -1
  38. package/dist/transceivers/set-rtx/index.d.ts +1 -1
  39. package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
  40. package/dist/transceivers/set-rtx/index.js.map +1 -1
  41. package/package.json +7 -7
  42. package/src/eslint-plugin/index.ts +0 -1
  43. package/src/eslint-plugin/rules/explicit-state-types.ts +8 -1
  44. package/src/eslint-plugin/rules/index.ts +0 -1
  45. package/src/internal/atom/create-regular-atom.ts +1 -0
  46. package/src/internal/atom/dispose-atom.ts +1 -0
  47. package/src/internal/atom/index.ts +0 -1
  48. package/src/internal/families/find-in-store.ts +4 -5
  49. package/src/internal/families/get-family-of-token.ts +4 -5
  50. package/src/internal/families/index.ts +0 -1
  51. package/src/internal/families/init-family-member.ts +3 -4
  52. package/src/internal/families/seek-in-store.ts +4 -5
  53. package/src/internal/get-state/read-or-compute-value.ts +14 -2
  54. package/src/internal/index.ts +116 -96
  55. package/src/internal/ingest-updates/ingest-creation-disposal.ts +18 -15
  56. package/src/internal/ingest-updates/ingest-selector-update.ts +9 -5
  57. package/src/internal/join/get-internal-relations-from-store.ts +2 -2
  58. package/src/internal/join/join-internal.ts +6 -18
  59. package/src/internal/molecule.ts +1 -0
  60. package/src/internal/mutable/create-mutable-atom-family.ts +37 -21
  61. package/src/internal/mutable/create-mutable-atom.ts +17 -13
  62. package/src/internal/mutable/get-json-family.ts +7 -6
  63. package/src/internal/mutable/get-json-token.ts +6 -13
  64. package/src/internal/mutable/get-update-family.ts +7 -8
  65. package/src/internal/mutable/get-update-token.ts +5 -9
  66. package/src/internal/mutable/tracker-family.ts +10 -13
  67. package/src/internal/mutable/tracker.ts +66 -90
  68. package/src/internal/mutable/transceiver.ts +35 -8
  69. package/src/internal/selector/dispose-selector.ts +9 -9
  70. package/src/internal/selector/register-selector.ts +2 -2
  71. package/src/internal/set-state/copy-mutable-if-needed.ts +8 -6
  72. package/src/internal/set-state/reset-atom-or-selector.ts +11 -4
  73. package/src/internal/set-state/set-atom.ts +1 -1
  74. package/src/internal/store/counterfeit.ts +3 -4
  75. package/src/internal/store/deposit.ts +7 -9
  76. package/src/internal/store/store.ts +2 -2
  77. package/src/internal/store/withdraw.ts +7 -11
  78. package/src/json/entries.ts +7 -7
  79. package/src/json/index.ts +0 -2
  80. package/src/main/atom.ts +68 -127
  81. package/src/main/dispose-state.ts +4 -6
  82. package/src/main/find-state.ts +6 -13
  83. package/src/main/get-state.ts +0 -2
  84. package/src/main/index.ts +1 -176
  85. package/src/main/join.ts +2 -9
  86. package/src/main/logger.ts +7 -7
  87. package/src/main/reset-state.ts +0 -2
  88. package/src/main/selector.ts +5 -72
  89. package/src/main/set-state.ts +1 -4
  90. package/src/main/silo.ts +14 -5
  91. package/src/main/subscribe.ts +0 -7
  92. package/src/main/timeline.ts +1 -18
  93. package/src/main/tokens.ts +245 -0
  94. package/src/main/transaction.ts +28 -60
  95. package/src/main/validators.ts +2 -2
  96. package/src/react/use-json.ts +15 -25
  97. package/src/react-devtools/store.ts +61 -45
  98. package/src/realtime/shared-room-store.ts +12 -25
  99. package/src/realtime-client/pull-mutable-atom-family-member.ts +5 -9
  100. package/src/realtime-client/pull-mutable-atom.ts +5 -9
  101. package/src/realtime-react/use-pull-mutable-atom.ts +3 -5
  102. package/src/realtime-react/use-pull-mutable-family-member.ts +3 -4
  103. package/src/realtime-server/realtime-mutable-family-provider.ts +3 -4
  104. package/src/realtime-server/realtime-mutable-provider.ts +2 -3
  105. package/src/realtime-server/realtime-server-stores/server-room-external-actions.ts +6 -5
  106. package/src/realtime-server/realtime-server-stores/server-user-store.ts +9 -18
  107. package/src/transceivers/set-rtx/set-rtx.ts +1 -1
  108. package/src/eslint-plugin/rules/synchronous-selector-dependencies.ts +0 -140
  109. package/src/eslint-plugin/walk.ts +0 -81
  110. package/src/internal/atom/create-standalone-atom.ts +0 -39
  111. package/src/internal/families/create-atom-family.ts +0 -38
  112. package/src/json/select-json-family.ts +0 -55
  113. package/src/json/select-json.ts +0 -19
@@ -4,11 +4,9 @@ import type {
4
4
  MutableAtomToken,
5
5
  UpdateHandler,
6
6
  } from "atom.io"
7
- import type { Json } from "atom.io/json"
8
- import { selectJson } from "atom.io/json"
9
7
 
10
8
  import type { MutableAtom } from ".."
11
- import { resetInStore, setIntoStore } from ".."
9
+ import { createStandaloneSelector, resetInStore, setIntoStore } from ".."
12
10
  import { newest } from "../lineage"
13
11
  import { deposit, type Store } from "../store"
14
12
  import { Subject } from "../subject"
@@ -16,14 +14,11 @@ import { subscribeToState } from "../subscribe"
16
14
  import { Tracker } from "./tracker"
17
15
  import type { Transceiver } from "./transceiver"
18
16
 
19
- export function createMutableAtom<
20
- T extends Transceiver<any>,
21
- J extends Json.Serializable,
22
- >(
17
+ export function createMutableAtom<T extends Transceiver<any, any>>(
23
18
  store: Store,
24
- options: MutableAtomOptions<T, J>,
19
+ options: MutableAtomOptions<T>,
25
20
  family: FamilyMetadata | undefined,
26
- ): MutableAtomToken<T, J> {
21
+ ): MutableAtomToken<T> {
27
22
  store.logger.info(
28
23
  `๐Ÿ”จ`,
29
24
  `atom`,
@@ -43,8 +38,11 @@ export function createMutableAtom<
43
38
  )
44
39
  return deposit(existing)
45
40
  }
46
- const subject = new Subject<{ newValue: T; oldValue: T }>()
47
- const newAtom: MutableAtom<T, J> = {
41
+ const subject = new Subject<{
42
+ newValue: T
43
+ oldValue: T
44
+ }>()
45
+ const newAtom: MutableAtom<T> = {
48
46
  ...options,
49
47
  type,
50
48
  install: (s: Store) => {
@@ -86,8 +84,14 @@ export function createMutableAtom<
86
84
 
87
85
  new Tracker(token, store)
88
86
  if (!family) {
89
- selectJson(token, options, store)
87
+ createStandaloneSelector(store, {
88
+ key: `${key}:JSON`,
89
+ get: ({ get }) => get(token).toJSON(),
90
+ set: ({ set }, newValue) => {
91
+ set(token, options.class.fromJSON(newValue))
92
+ },
93
+ })
90
94
  }
91
-
95
+ store.on.atomCreation.next(token)
92
96
  return token
93
97
  }
@@ -7,16 +7,17 @@ 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>,
11
- SerializableCore extends Json.Serializable,
10
+ Core extends Transceiver<Json.Serializable, Json.Serializable>,
12
11
  Key extends Canonical,
13
12
  >(
14
- mutableAtomFamily: MutableAtomFamilyToken<Core, SerializableCore, Key>,
13
+ mutableAtomFamily: MutableAtomFamilyToken<Core, Key>,
15
14
  store: Store,
16
- ): WritablePureSelectorFamily<SerializableCore, Key> => {
15
+ ): WritablePureSelectorFamily<ReturnType<Core[`toJSON`]>, Key> => {
17
16
  const target = newest(store)
18
17
  const key = `${mutableAtomFamily.key}:JSON`
19
- const jsonFamily: WritablePureSelectorFamily<SerializableCore, Key> =
20
- target.families.get(key) as WritablePureSelectorFamily<SerializableCore, Key>
18
+ const jsonFamily = target.families.get(key) as WritablePureSelectorFamily<
19
+ ReturnType<Core[`toJSON`]>,
20
+ Key
21
+ >
21
22
  return jsonFamily
22
23
  }
@@ -3,27 +3,20 @@ import type {
3
3
  WritablePureSelectorFamilyToken,
4
4
  WritablePureSelectorToken,
5
5
  } from "atom.io"
6
- import type { Json } from "atom.io/json"
7
6
 
8
7
  import { findInStore } from "../families"
9
8
  import { newest } from "../lineage"
10
9
  import { type Store, withdraw } from "../store"
11
- import type { Transceiver } from "./transceiver"
10
+ import type { AsJSON, Transceiver } from "./transceiver"
12
11
 
13
- export const getJsonToken = <
14
- Core extends Transceiver<any>,
15
- SerializableCore extends Json.Serializable,
16
- >(
12
+ export const getJsonToken = <T extends Transceiver<any, any>>(
17
13
  store: Store,
18
- mutableAtomToken: MutableAtomToken<Core, SerializableCore>,
19
- ): WritablePureSelectorToken<SerializableCore> => {
14
+ mutableAtomToken: MutableAtomToken<T>,
15
+ ): WritablePureSelectorToken<AsJSON<T>> => {
20
16
  if (mutableAtomToken.family) {
21
17
  const target = newest(store)
22
18
  const jsonFamilyKey = `${mutableAtomToken.family.key}:JSON`
23
- const jsonFamilyToken: WritablePureSelectorFamilyToken<
24
- SerializableCore,
25
- string
26
- > = {
19
+ const jsonFamilyToken: WritablePureSelectorFamilyToken<AsJSON<T>, string> = {
27
20
  key: jsonFamilyKey,
28
21
  type: `writable_pure_selector_family`,
29
22
  }
@@ -32,7 +25,7 @@ export const getJsonToken = <
32
25
  const jsonToken = findInStore(store, family, subKey)
33
26
  return jsonToken
34
27
  }
35
- const token: WritablePureSelectorToken<SerializableCore> = {
28
+ const token: WritablePureSelectorToken<AsJSON<T>> = {
36
29
  type: `writable_pure_selector`,
37
30
  key: `${mutableAtomToken.key}:JSON`,
38
31
  }
@@ -4,20 +4,19 @@ import type { Json } from "atom.io/json"
4
4
  import type { AtomFamily } from ".."
5
5
  import { newest } from "../lineage"
6
6
  import type { Store } from "../store"
7
- import type { Signal, Transceiver } from "./transceiver"
7
+ import type { SignalFrom, Transceiver } from "./transceiver"
8
8
 
9
9
  export const getUpdateFamily = <
10
- Core extends Transceiver<Json.Serializable>,
11
- SerializableCore extends Json.Serializable,
12
- Key extends string,
10
+ T extends Transceiver<Json.Serializable, Json.Serializable>,
11
+ K extends string,
13
12
  >(
14
- mutableAtomFamily: MutableAtomFamilyToken<Core, SerializableCore, Key>,
13
+ mutableAtomFamily: MutableAtomFamilyToken<T, K>,
15
14
  store: Store,
16
- ): AtomFamily<Signal<Core>, Key> => {
15
+ ): AtomFamily<SignalFrom<T>, K> => {
17
16
  const target = newest(store)
18
17
  const key = `*${mutableAtomFamily.key}`
19
- const updateFamily: AtomFamily<Signal<Core>, Key> = target.families.get(
18
+ const updateFamily: AtomFamily<SignalFrom<T>, K> = target.families.get(
20
19
  key,
21
- ) as AtomFamily<Signal<Core>, Key>
20
+ ) as AtomFamily<SignalFrom<T>, K>
22
21
  return updateFamily
23
22
  }
@@ -1,16 +1,12 @@
1
1
  import type { MutableAtomToken, RegularAtomToken } from "atom.io"
2
- import type { Json } from "atom.io/json"
3
2
 
4
- import type { Signal, Transceiver } from "./transceiver"
3
+ import type { SignalFrom, Transceiver } from "./transceiver"
5
4
 
6
- export const getUpdateToken = <
7
- Core extends Transceiver<any>,
8
- SerializableCore extends Json.Serializable,
9
- >(
10
- mutableAtomToken: MutableAtomToken<Core, SerializableCore>,
11
- ): RegularAtomToken<Signal<Core>> => {
5
+ export const getUpdateToken = <T extends Transceiver<any, any>>(
6
+ mutableAtomToken: MutableAtomToken<T>,
7
+ ): RegularAtomToken<SignalFrom<T>> => {
12
8
  const key = `*${mutableAtomToken.key}`
13
- const updateToken: RegularAtomToken<Signal<Core>> = { type: `atom`, key }
9
+ const updateToken: RegularAtomToken<SignalFrom<T>> = { type: `atom`, key }
14
10
  if (mutableAtomToken.family) {
15
11
  updateToken.family = {
16
12
  key: `*${mutableAtomToken.family.key}`,
@@ -8,24 +8,21 @@ import { Tracker } from "./tracker"
8
8
  import type { Transceiver } from "./transceiver"
9
9
 
10
10
  export class FamilyTracker<
11
- Core extends Transceiver<any>,
12
- FamilyMemberKey extends Canonical,
11
+ T extends Transceiver<any, any>,
12
+ K extends Canonical,
13
13
  > {
14
- private trackers: Map<FamilyMemberKey, Tracker<Core>> = new Map()
14
+ private trackers: Map<K, Tracker<T>> = new Map()
15
15
 
16
16
  public readonly latestUpdateAtoms: RegularAtomFamily<
17
- (Core extends Transceiver<infer Signal> ? Signal : never) | null,
18
- FamilyMemberKey
17
+ (T extends Transceiver<infer Signal, any> ? Signal : never) | null,
18
+ K
19
19
  >
20
- public readonly mutableAtoms: MutableAtomFamily<Core, any, FamilyMemberKey>
20
+ public readonly mutableAtoms: MutableAtomFamily<T, K>
21
21
 
22
- public constructor(
23
- mutableAtoms: MutableAtomFamily<Core, any, FamilyMemberKey>,
24
- store: Store,
25
- ) {
22
+ public constructor(mutableAtoms: MutableAtomFamily<T, K>, store: Store) {
26
23
  const updateAtoms = createRegularAtomFamily<
27
- (Core extends Transceiver<infer Signal> ? Signal : never) | null,
28
- FamilyMemberKey
24
+ (T extends Transceiver<infer Signal, any> ? Signal : never) | null,
25
+ K
29
26
  >(
30
27
  store,
31
28
  {
@@ -44,7 +41,7 @@ export class FamilyTracker<
44
41
  const key = parseJson(token.family.subKey)
45
42
  switch (type) {
46
43
  case `state_creation`:
47
- this.trackers.set(key, new Tracker<Core>(token, store))
44
+ this.trackers.set(key, new Tracker<T>(token, store))
48
45
  break
49
46
  case `state_disposal`:
50
47
  {
@@ -1,5 +1,4 @@
1
1
  import type { FamilyMetadata, MutableAtomToken, RegularAtomToken } from "atom.io"
2
- import type { Json } from "atom.io/json"
3
2
 
4
3
  import type { Store } from ".."
5
4
  import {
@@ -11,20 +10,18 @@ import {
11
10
  } from ".."
12
11
  import { createRegularAtom } from "../atom"
13
12
  import { isChildStore } from "../transaction/is-root-store"
14
- import type { Transceiver } from "./transceiver"
13
+ import type { SignalFrom, Transceiver } from "./transceiver"
15
14
 
16
15
  /**
17
16
  * @internal Give the tracker a transceiver state and a store, and it will
18
17
  * subscribe to the transceiver's inner value. When the inner value changes,
19
18
  * the tracker will update its own state to reflect the change.
20
19
  */
21
- export class Tracker<Mutable extends Transceiver<any>> {
20
+ export class Tracker<T extends Transceiver<any, any>> {
22
21
  private initializeState(
23
- mutableState: MutableAtomToken<Mutable, Json.Serializable>,
22
+ mutableState: MutableAtomToken<T>,
24
23
  store: Store,
25
- ): RegularAtomToken<
26
- (Mutable extends Transceiver<infer Signal> ? Signal : never) | null
27
- > {
24
+ ): RegularAtomToken<SignalFrom<T> | null> {
28
25
  const latestUpdateStateKey = `*${mutableState.key}`
29
26
  store.atoms.delete(latestUpdateStateKey)
30
27
  store.valueMap.delete(latestUpdateStateKey)
@@ -34,9 +31,7 @@ export class Tracker<Mutable extends Transceiver<any>> {
34
31
  subKey: mutableState.family.subKey,
35
32
  }
36
33
  : undefined
37
- const latestUpdateState = createRegularAtom<
38
- (Mutable extends Transceiver<infer Signal> ? Signal : never) | null
39
- >(
34
+ const latestUpdateState = createRegularAtom<SignalFrom<T> | null>(
40
35
  store,
41
36
  {
42
37
  key: latestUpdateStateKey,
@@ -54,46 +49,41 @@ export class Tracker<Mutable extends Transceiver<any>> {
54
49
 
55
50
  private unsubscribeFromInnerValue!: () => void
56
51
  private unsubscribeFromState!: () => void
57
- private observeCore(
58
- mutableState: MutableAtomToken<Mutable, any>,
59
- latestUpdateState: RegularAtomToken<
60
- (Mutable extends Transceiver<infer Signal> ? Signal : never) | null
61
- >,
52
+ private captureSignalsFromCore(
53
+ mutableState: MutableAtomToken<T, any>,
54
+ latestUpdateState: RegularAtomToken<SignalFrom<T> | null>,
62
55
  target: Store,
63
56
  ): void {
64
57
  const subscriptionKey = `tracker:${target.config.name}:${
65
58
  isChildStore(target) ? target.transactionMeta.update.key : `main`
66
59
  }:${mutableState.key}`
60
+ const trackerCapturesOutboundSignal = (update: SignalFrom<T>) => {
61
+ setIntoStore(target, latestUpdateState, update)
62
+ }
67
63
  const originalInnerValue = getFromStore(target, mutableState)
68
64
  this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
69
65
  subscriptionKey,
70
- (update) => {
71
- setIntoStore(target, latestUpdateState, update)
72
- },
66
+ trackerCapturesOutboundSignal,
73
67
  )
74
68
  this.unsubscribeFromState = subscribeToState(
75
69
  target,
76
70
  mutableState,
77
71
  subscriptionKey,
78
- (update) => {
72
+ function trackerLooksForNewReference(update: SignalFrom<T>) {
79
73
  if (update.newValue !== update.oldValue) {
80
74
  this.unsubscribeFromInnerValue()
81
75
  this.unsubscribeFromInnerValue = update.newValue.subscribe(
82
76
  subscriptionKey,
83
- (transceiverUpdate) => {
84
- setIntoStore(target, latestUpdateState, transceiverUpdate)
85
- },
77
+ trackerCapturesOutboundSignal,
86
78
  )
87
79
  }
88
- },
80
+ }.bind(this),
89
81
  )
90
82
  }
91
83
 
92
- private updateCore<Core extends Transceiver<any>>(
93
- mutableState: MutableAtomToken<Core, Json.Serializable>,
94
- latestUpdateState: RegularAtomToken<
95
- (Mutable extends Transceiver<infer Signal> ? Signal : never) | null
96
- >,
84
+ private supplySignalsToCore(
85
+ mutableState: MutableAtomToken<T>,
86
+ latestUpdateState: RegularAtomToken<SignalFrom<T> | null>,
97
87
  target: Store,
98
88
  ): void {
99
89
  const subscriptionKey = `tracker:${target.config.name}:${
@@ -103,85 +93,71 @@ export class Tracker<Mutable extends Transceiver<any>> {
103
93
  target,
104
94
  latestUpdateState,
105
95
  subscriptionKey,
106
- ({ newValue, oldValue }) => {
96
+ function trackerCapturesInboundSignal({ newValue, oldValue }) {
107
97
  const timelineId = target.timelineTopics.getRelatedKey(
108
98
  latestUpdateState.key,
109
99
  )
110
100
 
111
- if (timelineId) {
112
- const timelineData = target.timelines.get(timelineId)
113
- if (timelineData?.timeTraveling) {
114
- const unsubscribe = subscribeToTimeline(
115
- target,
116
- { key: timelineId, type: `timeline` },
117
- subscriptionKey,
118
- (update) => {
119
- unsubscribe()
120
- setIntoStore(target, mutableState, (transceiver) => {
121
- if (update === `redo` && newValue) {
122
- transceiver.do(newValue)
123
- } else if (update === `undo` && oldValue) {
124
- transceiver.undo(oldValue)
125
- }
126
- return transceiver
127
- })
128
- },
129
- )
130
- return
131
- }
101
+ if (timelineId && target.timelines.get(timelineId)?.timeTraveling) {
102
+ const unsubscribe = subscribeToTimeline(
103
+ target,
104
+ { key: timelineId, type: `timeline` },
105
+ subscriptionKey,
106
+ function trackerWaitsForTimeTravelToFinish(update) {
107
+ unsubscribe()
108
+ setIntoStore(target, mutableState, (transceiver) => {
109
+ if (update === `redo` && newValue) {
110
+ transceiver.do(newValue)
111
+ } else if (update === `undo` && oldValue) {
112
+ transceiver.undo(oldValue)
113
+ }
114
+ return transceiver
115
+ })
116
+ },
117
+ )
118
+ return
132
119
  }
133
120
 
134
- const unsubscribe = target.on.operationClose.subscribe(
135
- subscriptionKey,
136
- () => {
137
- unsubscribe()
138
- const mutable = getFromStore(target, mutableState)
139
- const updateNumber =
140
- newValue === null ? -1 : mutable.getUpdateNumber(newValue)
141
- const eventOffset = updateNumber - mutable.cacheUpdateNumber
142
- if (newValue && eventOffset === 1) {
143
- setIntoStore(
144
- target,
145
- mutableState,
146
- (transceiver) => (transceiver.do(newValue), transceiver),
147
- )
148
- } else {
149
- target.logger.info(
150
- `โŒ`,
151
- `mutable_atom`,
152
- mutableState.key,
153
- `could not be updated. Expected update number ${
154
- mutable.cacheUpdateNumber + 1
155
- }, but got ${updateNumber}`,
156
- )
157
- }
158
- },
159
- )
121
+ const mutable = getFromStore(target, mutableState)
122
+ const updateNumber = mutable.getUpdateNumber(newValue)
123
+ const eventOffset = updateNumber - mutable.cacheUpdateNumber
124
+ if (newValue && eventOffset === 1) {
125
+ setIntoStore(
126
+ target,
127
+ mutableState,
128
+ (transceiver) => (transceiver.do(newValue), transceiver),
129
+ )
130
+ } else {
131
+ target.logger.info(
132
+ `โŒ`,
133
+ `mutable_atom`,
134
+ mutableState.key,
135
+ `could not be updated. Expected update number ${
136
+ mutable.cacheUpdateNumber + 1
137
+ }, but got ${updateNumber}`,
138
+ )
139
+ }
160
140
  },
161
141
  )
162
142
  }
163
143
 
164
- public mutableState: MutableAtomToken<Mutable, Json.Serializable>
165
- public latestUpdateState: RegularAtomToken<
166
- (Mutable extends Transceiver<infer Signal> ? Signal : never) | null
167
- >
144
+ public mutableAtomToken: MutableAtomToken<T>
145
+ public latestSignalToken: RegularAtomToken<SignalFrom<T> | null>
168
146
 
169
147
  public [Symbol.dispose]!: () => void
170
148
 
171
- public constructor(
172
- mutableState: MutableAtomToken<Mutable, Json.Serializable>,
173
- store: Store,
174
- ) {
175
- this.mutableState = mutableState
149
+ public constructor(mutableAtomToken: MutableAtomToken<T>, store: Store) {
176
150
  const target = newest(store)
177
- this.latestUpdateState = this.initializeState(mutableState, target)
178
- this.observeCore(mutableState, this.latestUpdateState, target)
179
- this.updateCore(mutableState, this.latestUpdateState, target)
180
- target.trackers.set(mutableState.key, this)
151
+ const latestSignalToken = this.initializeState(mutableAtomToken, target)
152
+ this.mutableAtomToken = mutableAtomToken
153
+ this.latestSignalToken = latestSignalToken
154
+ this.captureSignalsFromCore(mutableAtomToken, latestSignalToken, target)
155
+ this.supplySignalsToCore(mutableAtomToken, latestSignalToken, target)
156
+ target.trackers.set(mutableAtomToken.key, this)
181
157
  this[Symbol.dispose] = () => {
182
158
  this.unsubscribeFromInnerValue()
183
159
  this.unsubscribeFromState()
184
- target.trackers.delete(mutableState.key)
160
+ target.trackers.delete(mutableAtomToken.key)
185
161
  }
186
162
  }
187
163
  }
@@ -1,16 +1,28 @@
1
1
  import type { Json } from "atom.io/json"
2
2
 
3
- export interface Transceiver<S extends Json.Serializable> {
3
+ export interface Transceiver<
4
+ S extends Json.Serializable,
5
+ J extends Json.Serializable,
6
+ > {
4
7
  do: (update: S) => number | `OUT_OF_RANGE` | null
5
8
  undo: (update: S) => void
6
9
  subscribe: (key: string, fn: (update: S) => void) => () => void
7
10
  cacheUpdateNumber: number
8
11
  getUpdateNumber: (update: S) => number
12
+ toJSON: () => J
9
13
  }
10
14
 
15
+ // biome-ignore format: intersection
16
+ export type TransceiverConstructor<
17
+ J extends Json.Serializable,
18
+ T extends Transceiver<any, J>
19
+ > =
20
+ & ( new () => T )
21
+ & { fromJSON: (json: J) => T }
22
+
11
23
  export function isTransceiver(
12
24
  value: unknown,
13
- ): value is Transceiver<Json.Serializable> {
25
+ ): value is Transceiver<Json.Serializable, Json.Serializable> {
14
26
  return (
15
27
  typeof value === `object` &&
16
28
  value !== null &&
@@ -22,17 +34,28 @@ export function isTransceiver(
22
34
 
23
35
  export type TransceiverMode = `playback` | `record` | `transaction`
24
36
 
25
- export type Signal<TVR extends Transceiver<any>> = TVR extends Transceiver<
26
- infer S
37
+ export type SignalFrom<T extends Transceiver<any, any>> = T extends Transceiver<
38
+ infer S,
39
+ any
27
40
  >
28
41
  ? S
29
42
  : never
30
43
 
44
+ export type AsJSON<T extends Transceiver<any, any>> = T extends Transceiver<
45
+ any,
46
+ infer J
47
+ >
48
+ ? J
49
+ : never
50
+
51
+ export type ConstructorOf<T extends Transceiver<any, any>> =
52
+ TransceiverConstructor<AsJSON<T>, T>
53
+
31
54
  /*
32
55
  A transceiver may also keep a list of updates that have been applied to it.
33
56
  This is useful for undo/redo functionality, especially in the context of
34
- revising history. It is a good idea to accept a cache limit in your
35
- constructor, and overwrite old updates. Here's an example of how we
57
+ revising history. It is a good idea to accept a cache limit in your
58
+ constructor, and overwrite old updates. Here's an example of how we
36
59
  might set that up:
37
60
 
38
61
  myTransceiver = Transceiver {
@@ -48,7 +71,7 @@ myTransceiver = Transceiver {
48
71
 
49
72
  CONFIRM/NO-OP
50
73
  Update `27=del:"x"` is passed to myTransceiver.do:
51
- - [updateNumber = 27, update = `del:"x"`]
74
+ - [updateNumber = 27, update = `del:"x"`]
52
75
  - updateOffset = updateNumber - cacheUpdateNumber // 0
53
76
  - eventOffset < 1 // true (we're validating the past)
54
77
  - |eventOffset| < cacheLimit // true (we remember this update)
@@ -75,7 +98,7 @@ Update `29=del:"x"` is passed to myTransceiver.do:
75
98
  - eventOffset === 1 // false (we're NOT ready to apply this update)
76
99
  - updateIdx := cacheIdx + updateOffset // 3
77
100
  - updateIdx %= cacheLimit // 0
78
- - cache[updateIdx] = update // cache = <{ 0 => del:"x" }>
101
+ - cache[updateIdx] = update // cache = <{ 0 => del:"x" }>
79
102
  - expectedUpdateNumber = cacheUpdateNumber + 1 // 28
80
103
  - return expectedUpdateNumber // ๐Ÿคจ๐Ÿ‘‚
81
104
 
@@ -110,3 +133,7 @@ Update `24=add:"z"` is passed to myTransceiver.do:
110
133
  - return `OUT_OF_RANGE` // ๐Ÿ˜ตโ€๐Ÿ’ซ๐Ÿ‘‚
111
134
 
112
135
  */
136
+
137
+ // The function wants a constructor C
138
+ // - that has a static fromJSON(json) returning an instance of C
139
+ // - and whose instances have toJSON() whose return type matches fromJSON's param
@@ -8,9 +8,8 @@ export function disposeSelector(
8
8
  selectorToken: SelectorToken<unknown>,
9
9
  ): void {
10
10
  const target = newest(store)
11
- const { key, type } = selectorToken
12
- const selector = withdraw(target, selectorToken)
13
- if (!selector.family) {
11
+ const { key, type, family: familyMeta } = selectorToken
12
+ if (!familyMeta) {
14
13
  store.logger.error(
15
14
  `โŒ`,
16
15
  type,
@@ -18,9 +17,9 @@ export function disposeSelector(
18
17
  `Standalone selectors cannot be disposed.`,
19
18
  )
20
19
  } else {
21
- const molecule = target.molecules.get(selector.family.subKey)
20
+ const molecule = target.molecules.get(familyMeta.subKey)
22
21
  if (molecule) {
23
- target.moleculeData.delete(selector.family.subKey, selector.family.key)
22
+ target.moleculeData.delete(familyMeta.subKey, familyMeta.key)
24
23
  }
25
24
  let familyToken: SelectorFamilyToken<any, any>
26
25
  switch (selectorToken.type) {
@@ -28,7 +27,7 @@ export function disposeSelector(
28
27
  {
29
28
  target.writableSelectors.delete(key)
30
29
  familyToken = {
31
- key: selector.family.key,
30
+ key: familyMeta.key,
32
31
  type: `writable_held_selector_family`,
33
32
  }
34
33
  const family = withdraw(store, familyToken)
@@ -43,7 +42,7 @@ export function disposeSelector(
43
42
  {
44
43
  target.writableSelectors.delete(key)
45
44
  familyToken = {
46
- key: selector.family.key,
45
+ key: familyMeta.key,
47
46
  type: `writable_pure_selector_family`,
48
47
  }
49
48
  const family = withdraw(store, familyToken)
@@ -58,7 +57,7 @@ export function disposeSelector(
58
57
  {
59
58
  target.readonlySelectors.delete(key)
60
59
  familyToken = {
61
- key: selector.family.key,
60
+ key: familyMeta.key,
62
61
  type: `readonly_held_selector_family`,
63
62
  }
64
63
  const family = withdraw(store, familyToken)
@@ -73,7 +72,7 @@ export function disposeSelector(
73
72
  {
74
73
  target.readonlySelectors.delete(key)
75
74
  familyToken = {
76
- key: selector.family.key,
75
+ key: familyMeta.key,
77
76
  type: `readonly_pure_selector_family`,
78
77
  }
79
78
  const family = withdraw(store, familyToken)
@@ -89,6 +88,7 @@ export function disposeSelector(
89
88
  target.valueMap.delete(key)
90
89
  target.selectorAtoms.delete(key)
91
90
  target.selectorGraph.delete(key)
91
+ target.moleculeData.delete(familyMeta.key, familyMeta.subKey)
92
92
  store.logger.info(`๐Ÿ”ฅ`, selectorToken.type, key, `deleted`)
93
93
  if (isChildStore(target) && target.transactionMeta.phase === `building`) {
94
94
  target.transactionMeta.update.updates.push({
@@ -3,9 +3,9 @@ import type {
3
3
  ReadableFamilyToken,
4
4
  ReadableToken,
5
5
  setState,
6
- SetterToolkit,
7
6
  WritableFamilyToken,
8
7
  WritableToken,
8
+ WriterToolkit,
9
9
  } from "atom.io"
10
10
  import type { Json } from "atom.io/json"
11
11
 
@@ -27,7 +27,7 @@ export const registerSelector = (
27
27
  | `writable_pure_selector`,
28
28
  selectorKey: string,
29
29
  covered: Set<string>,
30
- ): SetterToolkit => ({
30
+ ): WriterToolkit => ({
31
31
  get: (
32
32
  ...params:
33
33
  | [ReadableFamilyToken<any, any>, Json.Serializable]
@@ -2,12 +2,14 @@ import type { MutableAtom, Transceiver } from ".."
2
2
  import { Tracker } from "../mutable"
3
3
  import type { Store } from "../store"
4
4
 
5
- export function copyMutableIfNeeded<T extends Transceiver<any>>(
5
+ export function copyMutableIfNeeded<T extends Transceiver<any, any>>(
6
6
  target: Store,
7
- atom: MutableAtom<T, any>,
7
+ atom: MutableAtom<T>,
8
8
  origin: Store,
9
9
  ): T {
10
- const originValue = origin.valueMap.get(atom.key)
10
+ const originValue = origin.valueMap.get(atom.key) as
11
+ | Transceiver<any, any>
12
+ | undefined
11
13
  const targetValue = target.valueMap.get(atom.key)
12
14
 
13
15
  if (originValue !== targetValue) {
@@ -15,12 +17,12 @@ export function copyMutableIfNeeded<T extends Transceiver<any>>(
15
17
  }
16
18
 
17
19
  if (originValue === undefined) {
18
- return atom.default()
20
+ return new atom.class()
19
21
  }
20
22
 
21
23
  origin.logger.info(`๐Ÿ“ƒ`, `atom`, atom.key, `copying`)
22
- const jsonValue = atom.toJson(originValue)
23
- const copiedValue = atom.fromJson(jsonValue)
24
+ const jsonValue = originValue.toJSON()
25
+ const copiedValue = atom.class.fromJSON(jsonValue)
24
26
  target.valueMap.set(atom.key, copiedValue)
25
27
  new Tracker(atom, origin)
26
28
  return copiedValue