atom.io 0.21.1 → 0.23.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 (116) hide show
  1. package/data/dist/index.cjs +152 -63
  2. package/data/dist/index.d.ts +6 -0
  3. package/data/dist/index.js +3 -3
  4. package/data/src/join.ts +164 -51
  5. package/data/src/struct-family.ts +2 -2
  6. package/dist/chunk-6MLFYN32.js +18 -0
  7. package/dist/{chunk-HITX3MO4.js → chunk-7DT3PVS3.js} +151 -62
  8. package/dist/{chunk-RT43TVKP.js → chunk-GVHKIJ3G.js} +1 -1
  9. package/dist/chunk-OAYGID5B.js +27 -0
  10. package/dist/index.cjs +4 -18
  11. package/dist/index.d.ts +71 -28
  12. package/dist/index.js +6 -19
  13. package/ephemeral/dist/index.cjs +11 -0
  14. package/ephemeral/dist/index.js +9 -0
  15. package/ephemeral/package.json +16 -0
  16. package/ephemeral/src/index.ts +1 -0
  17. package/eslint-plugin/dist/index.cjs +155 -1
  18. package/eslint-plugin/dist/index.js +155 -1
  19. package/eslint-plugin/src/rules/index.ts +1 -0
  20. package/eslint-plugin/src/rules/lifespan.ts +203 -0
  21. package/eslint-plugin/src/rules/synchronous-selector-dependencies.ts +1 -65
  22. package/eslint-plugin/src/walk.ts +73 -0
  23. package/immortal/dist/index.cjs +260 -0
  24. package/immortal/dist/index.js +212 -0
  25. package/immortal/package.json +16 -0
  26. package/immortal/src/index.ts +3 -0
  27. package/immortal/src/make-molecule.ts +222 -0
  28. package/immortal/src/molecule.ts +167 -0
  29. package/immortal/src/seek-state.ts +73 -0
  30. package/internal/dist/index.cjs +1242 -837
  31. package/internal/dist/index.d.ts +135 -22
  32. package/internal/dist/index.js +1215 -838
  33. package/internal/src/atom/create-regular-atom.ts +0 -2
  34. package/internal/src/atom/create-standalone-atom.ts +6 -2
  35. package/internal/src/atom/dispose-atom.ts +26 -3
  36. package/internal/src/families/create-readonly-selector-family.ts +15 -10
  37. package/internal/src/families/create-regular-atom-family.ts +20 -21
  38. package/internal/src/families/create-writable-selector-family.ts +13 -9
  39. package/{src/dispose.ts → internal/src/families/dispose-from-store.ts} +7 -4
  40. package/internal/src/families/find-in-store.ts +11 -6
  41. package/internal/src/families/index.ts +3 -0
  42. package/internal/src/families/init-family-member.ts +112 -0
  43. package/internal/src/families/seek-in-store.ts +123 -0
  44. package/internal/src/get-state/get-from-store.ts +2 -2
  45. package/internal/src/ingest-updates/index.ts +1 -0
  46. package/internal/src/ingest-updates/ingest-creation-disposal.ts +104 -0
  47. package/internal/src/ingest-updates/ingest-transaction-update.ts +26 -4
  48. package/internal/src/mutable/create-mutable-atom-family.ts +22 -24
  49. package/internal/src/mutable/create-mutable-atom.ts +3 -3
  50. package/internal/src/mutable/get-json-family.ts +2 -2
  51. package/internal/src/mutable/get-json-token.ts +26 -12
  52. package/internal/src/mutable/tracker-family.ts +21 -19
  53. package/internal/src/not-found-error.ts +16 -3
  54. package/internal/src/selector/create-readonly-selector.ts +2 -3
  55. package/internal/src/selector/create-standalone-selector.ts +6 -2
  56. package/internal/src/selector/create-writable-selector.ts +2 -3
  57. package/internal/src/selector/dispose-selector.ts +68 -24
  58. package/internal/src/selector/register-selector.ts +10 -5
  59. package/internal/src/set-state/set-into-store.ts +2 -2
  60. package/internal/src/set-state/stow-update.ts +5 -1
  61. package/internal/src/store/deposit.ts +41 -7
  62. package/internal/src/store/index.ts +0 -1
  63. package/internal/src/store/store.ts +29 -5
  64. package/internal/src/store/withdraw.ts +28 -1
  65. package/internal/src/subscribe/subscribe-to-state.ts +2 -2
  66. package/internal/src/timeline/add-atom-to-timeline.ts +206 -182
  67. package/internal/src/timeline/create-timeline.ts +181 -60
  68. package/internal/src/timeline/time-travel.ts +20 -0
  69. package/internal/src/transaction/apply-transaction.ts +2 -12
  70. package/internal/src/transaction/build-transaction.ts +16 -2
  71. package/introspection/dist/index.cjs +40 -53
  72. package/introspection/dist/index.js +40 -53
  73. package/introspection/src/attach-atom-index.ts +38 -48
  74. package/introspection/src/attach-selector-index.ts +45 -50
  75. package/introspection/src/attach-timeline-family.ts +1 -0
  76. package/json/dist/index.cjs +40 -6
  77. package/json/dist/index.js +44 -9
  78. package/json/src/select-json-family.ts +47 -9
  79. package/package.json +30 -10
  80. package/react/dist/index.cjs +1 -1
  81. package/react/dist/index.js +1 -1
  82. package/react/src/use-json.ts +1 -1
  83. package/react-devtools/dist/index.cjs +69 -57
  84. package/react-devtools/dist/index.js +62 -49
  85. package/react-devtools/src/StateIndex.tsx +2 -1
  86. package/react-devtools/src/TimelineIndex.tsx +17 -14
  87. package/react-devtools/src/TransactionIndex.tsx +7 -7
  88. package/react-devtools/src/Updates.tsx +41 -32
  89. package/realtime-client/dist/index.cjs +3 -3
  90. package/realtime-client/dist/index.js +3 -3
  91. package/realtime-client/src/pull-mutable-atom-family-member.ts +1 -1
  92. package/realtime-client/src/pull-mutable-atom.ts +1 -1
  93. package/realtime-client/src/sync-continuity.ts +1 -2
  94. package/realtime-react/dist/index.cjs +1 -1
  95. package/realtime-react/dist/index.js +1 -1
  96. package/realtime-server/dist/index.cjs +39 -27
  97. package/realtime-server/dist/index.d.ts +1 -1
  98. package/realtime-server/dist/index.js +27 -16
  99. package/realtime-server/src/realtime-continuity-synchronizer.ts +5 -3
  100. package/realtime-server/src/realtime-mutable-family-provider.ts +2 -1
  101. package/realtime-server/src/realtime-mutable-provider.ts +1 -1
  102. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +21 -11
  103. package/realtime-testing/dist/index.cjs +7 -2
  104. package/realtime-testing/dist/index.js +8 -5
  105. package/realtime-testing/src/setup-realtime-test.tsx +5 -2
  106. package/src/atom.ts +19 -7
  107. package/src/dispose-state.ts +10 -0
  108. package/src/index.ts +5 -2
  109. package/src/selector.ts +13 -7
  110. package/src/silo.ts +3 -3
  111. package/src/subscribe.ts +8 -4
  112. package/src/timeline.ts +18 -1
  113. package/src/transaction.ts +59 -4
  114. package/dist/chunk-BF4MVQF6.js +0 -44
  115. package/internal/src/store/withdraw-new-family-member.ts +0 -69
  116. /package/{src → ephemeral/src}/find-state.ts +0 -0
@@ -0,0 +1,104 @@
1
+ import { parseJson } from "anvl/json"
2
+ import type {
3
+ MoleculeCreation,
4
+ MoleculeDisposal,
5
+ ReadableToken,
6
+ StateCreation,
7
+ StateDisposal,
8
+ } from "atom.io"
9
+ import { disposeMolecule, makeMoleculeInStore } from "atom.io/immortal"
10
+
11
+ import { disposeFromStore, initFamilyMember } from "../families"
12
+ import type { Store } from "../store"
13
+
14
+ export function ingestCreationEvent(
15
+ update: StateCreation<any>,
16
+ applying: `newValue` | `oldValue`,
17
+ store: Store,
18
+ ): void {
19
+ switch (applying) {
20
+ case `newValue`: {
21
+ createInStore(update.token, store)
22
+ break
23
+ }
24
+ case `oldValue`: {
25
+ disposeFromStore(update.token, store)
26
+ break
27
+ }
28
+ }
29
+ }
30
+
31
+ export function ingestDisposalEvent(
32
+ update: StateDisposal<any>,
33
+ applying: `newValue` | `oldValue`,
34
+ store: Store,
35
+ ): void {
36
+ switch (applying) {
37
+ case `newValue`: {
38
+ disposeFromStore(update.token, store)
39
+ break
40
+ }
41
+ case `oldValue`: {
42
+ createInStore(update.token, store)
43
+ store.valueMap.set(update.token.key, update.value)
44
+ break
45
+ }
46
+ }
47
+ }
48
+
49
+ function createInStore(token: ReadableToken<any>, store: Store): void {
50
+ if (token.family) {
51
+ const family = store.families.get(token.family.key)
52
+ if (family) {
53
+ const molecule = store.molecules.get(token.family.subKey)
54
+ if (molecule) {
55
+ molecule.bond(family)
56
+ return
57
+ }
58
+ if (store.config.lifespan === `immortal`) {
59
+ throw new Error(`No molecule found for key "${token.family.subKey}"`)
60
+ }
61
+ initFamilyMember(family, parseJson(token.family.subKey), store)
62
+ }
63
+ }
64
+ }
65
+
66
+ export function ingestMoleculeCreationEvent(
67
+ update: MoleculeCreation<any>,
68
+ applying: `newValue` | `oldValue`,
69
+ store: Store,
70
+ ): void {
71
+ switch (applying) {
72
+ case `newValue`:
73
+ makeMoleculeInStore(
74
+ store,
75
+ update.context[0],
76
+ update.family,
77
+ update.token.key,
78
+ ...update.params,
79
+ )
80
+ break
81
+ case `oldValue`:
82
+ disposeMolecule(update.token, store)
83
+ break
84
+ }
85
+ }
86
+ export function ingestMoleculeDisposalEvent(
87
+ update: MoleculeDisposal<any>,
88
+ applying: `newValue` | `oldValue`,
89
+ store: Store,
90
+ ): void {
91
+ switch (applying) {
92
+ case `newValue`:
93
+ disposeMolecule(update.token, store)
94
+ break
95
+ case `oldValue`:
96
+ makeMoleculeInStore(
97
+ store,
98
+ update.context[0],
99
+ update.family,
100
+ update.token.key,
101
+ )
102
+ break
103
+ }
104
+ }
@@ -2,6 +2,12 @@ import type { TransactionUpdate } from "atom.io"
2
2
 
3
3
  import type { Store } from "../store"
4
4
  import { ingestAtomUpdate } from "./ingest-atom-update"
5
+ import {
6
+ ingestCreationEvent,
7
+ ingestDisposalEvent,
8
+ ingestMoleculeCreationEvent,
9
+ ingestMoleculeDisposalEvent,
10
+ } from "./ingest-creation-disposal"
5
11
 
6
12
  export function ingestTransactionUpdate(
7
13
  applying: `newValue` | `oldValue`,
@@ -13,10 +19,26 @@ export function ingestTransactionUpdate(
13
19
  ? transactionUpdate.updates
14
20
  : [...transactionUpdate.updates].reverse()
15
21
  for (const updateFromTransaction of updates) {
16
- if (`newValue` in updateFromTransaction) {
17
- ingestAtomUpdate(applying, updateFromTransaction, store)
18
- } else {
19
- ingestTransactionUpdate(applying, updateFromTransaction, store)
22
+ switch (updateFromTransaction.type) {
23
+ case `atom_update`:
24
+ case `selector_update`:
25
+ ingestAtomUpdate(applying, updateFromTransaction, store)
26
+ break
27
+ case `state_creation`:
28
+ ingestCreationEvent(updateFromTransaction, applying, store)
29
+ break
30
+ case `state_disposal`:
31
+ ingestDisposalEvent(updateFromTransaction, applying, store)
32
+ break
33
+ case `molecule_creation`:
34
+ ingestMoleculeCreationEvent(updateFromTransaction, applying, store)
35
+ break
36
+ case `molecule_disposal`:
37
+ ingestMoleculeDisposalEvent(updateFromTransaction, applying, store)
38
+ break
39
+ case `transaction_update`:
40
+ ingestTransactionUpdate(applying, updateFromTransaction, store)
41
+ break
20
42
  }
21
43
  }
22
44
  }
@@ -4,6 +4,8 @@ import type {
4
4
  MutableAtomFamilyOptions,
5
5
  MutableAtomOptions,
6
6
  MutableAtomToken,
7
+ StateCreation,
8
+ StateDisposal,
7
9
  } from "atom.io"
8
10
  import type { Json } from "atom.io/json"
9
11
  import { selectJsonFamily, stringifyJson } from "atom.io/json"
@@ -23,33 +25,31 @@ export function createMutableAtomFamily<
23
25
  options: MutableAtomFamilyOptions<T, J, K>,
24
26
  store: Store,
25
27
  ): MutableAtomFamily<T, J, K> {
26
- const subject = new Subject<MutableAtomToken<T, J>>()
27
- const atomFamily: MutableAtomFamily<T, J, K> = Object.assign(
28
+ const subject = new Subject<
29
+ StateCreation<MutableAtomToken<T, J>> | StateDisposal<MutableAtomToken<T, J>>
30
+ >()
31
+
32
+ const atomFamily = Object.assign(
28
33
  (key: K): MutableAtomToken<T, J> => {
29
34
  const subKey = stringifyJson(key)
30
35
  const family: FamilyMetadata = { key: options.key, subKey }
31
36
  const fullKey = `${options.key}(${subKey})`
32
37
  const target = newest(store)
33
- const atomAlreadyCreated = target.atoms.has(fullKey)
34
- let token: MutableAtomToken<T, J>
35
- if (atomAlreadyCreated) {
36
- token = { type: `mutable_atom`, key: fullKey, family }
37
- } else {
38
- const individualOptions: MutableAtomOptions<T, J> = {
39
- key: fullKey,
40
- default: () => options.default(key),
41
- toJson: options.toJson,
42
- fromJson: options.fromJson,
43
- mutable: true,
44
- }
45
- if (options.effects) {
46
- individualOptions.effects = options.effects(key)
47
- }
48
-
49
- token = createMutableAtom(individualOptions, family, store)
50
38
 
51
- subject.next(token)
39
+ const individualOptions: MutableAtomOptions<T, J> = {
40
+ key: fullKey,
41
+ default: () => options.default(key),
42
+ toJson: options.toJson,
43
+ fromJson: options.fromJson,
44
+ mutable: true,
45
+ }
46
+ if (options.effects) {
47
+ individualOptions.effects = options.effects(key)
52
48
  }
49
+
50
+ const token = createMutableAtom(individualOptions, family, target)
51
+
52
+ subject.next({ type: `state_creation`, token })
53
53
  return token
54
54
  },
55
55
  {
@@ -60,10 +60,8 @@ export function createMutableAtomFamily<
60
60
  toJson: options.toJson,
61
61
  fromJson: options.fromJson,
62
62
  } as const,
63
- )
64
-
65
- const target = newest(store)
66
- target.families.set(options.key, atomFamily)
63
+ ) satisfies MutableAtomFamily<T, J, K>
64
+ store.families.set(options.key, atomFamily)
67
65
  selectJsonFamily(atomFamily, options, store)
68
66
  new FamilyTracker(atomFamily, store)
69
67
  return atomFamily
@@ -88,9 +88,9 @@ export function createMutableAtom<
88
88
  }
89
89
 
90
90
  new Tracker(token, store)
91
- selectJson(token, options, store)
92
-
93
- store.on.atomCreation.next(token)
91
+ if (!family) {
92
+ selectJson(token, options, store)
93
+ }
94
94
 
95
95
  return token
96
96
  }
@@ -1,4 +1,4 @@
1
- import type { MutableAtomFamily, WritableSelectorFamily } from "atom.io"
1
+ import type { MutableAtomFamilyToken, WritableSelectorFamily } from "atom.io"
2
2
  import type { Json } from "atom.io/json"
3
3
 
4
4
  import { newest } from "../lineage"
@@ -10,7 +10,7 @@ export const getJsonFamily = <
10
10
  SerializableCore extends Json.Serializable,
11
11
  Key extends string,
12
12
  >(
13
- mutableAtomFamily: MutableAtomFamily<Core, SerializableCore, Key>,
13
+ mutableAtomFamily: MutableAtomFamilyToken<Core, SerializableCore, Key>,
14
14
  store: Store,
15
15
  ): WritableSelectorFamily<SerializableCore, Key> => {
16
16
  const target = newest(store)
@@ -1,6 +1,13 @@
1
- import type { MutableAtomToken, WritableSelectorToken } from "atom.io"
1
+ import type {
2
+ MutableAtomToken,
3
+ WritableSelectorFamilyToken,
4
+ WritableSelectorToken,
5
+ } from "atom.io"
2
6
  import type { Json } from "atom.io/json"
3
7
 
8
+ import { findInStore } from "../families"
9
+ import { newest } from "../lineage"
10
+ import { type Store, withdraw } from "../store"
4
11
  import type { Transceiver } from "./transceiver"
5
12
 
6
13
  export const getJsonToken = <
@@ -8,19 +15,26 @@ export const getJsonToken = <
8
15
  SerializableCore extends Json.Serializable,
9
16
  >(
10
17
  mutableAtomToken: MutableAtomToken<Core, SerializableCore>,
18
+ store: Store,
11
19
  ): WritableSelectorToken<SerializableCore> => {
12
- const key = mutableAtomToken.family
13
- ? `${mutableAtomToken.family.key}:JSON(${mutableAtomToken.family.subKey})`
14
- : `${mutableAtomToken.key}:JSON`
15
- const jsonToken: WritableSelectorToken<SerializableCore> = {
16
- type: `selector`,
17
- key,
18
- }
19
20
  if (mutableAtomToken.family) {
20
- jsonToken.family = {
21
- key: `${mutableAtomToken.family.key}:JSON`,
22
- subKey: mutableAtomToken.family.subKey,
21
+ const target = newest(store)
22
+ const jsonFamilyKey = `${mutableAtomToken.family.key}:JSON`
23
+ const jsonFamilyToken: WritableSelectorFamilyToken<
24
+ SerializableCore,
25
+ string
26
+ > = {
27
+ key: jsonFamilyKey,
28
+ type: `selector_family`,
23
29
  }
30
+ const family = withdraw(jsonFamilyToken, target)
31
+ const subKey = JSON.parse(mutableAtomToken.family.subKey)
32
+ const jsonToken = findInStore(family, subKey, store)
33
+ return jsonToken
34
+ }
35
+ const token: WritableSelectorToken<SerializableCore> = {
36
+ type: `selector`,
37
+ key: `${mutableAtomToken.key}:JSON`,
24
38
  }
25
- return jsonToken
39
+ return token
26
40
  }
@@ -2,7 +2,7 @@ import type { MutableAtomFamily, RegularAtomFamily } from "atom.io"
2
2
  import type { Json } from "atom.io/json"
3
3
  import { parseJson } from "atom.io/json"
4
4
 
5
- import { createRegularAtomFamily } from "../families"
5
+ import { createRegularAtomFamily, seekInStore } from "../families"
6
6
  import type { Store } from "../store"
7
7
  import { Tracker } from "./tracker"
8
8
  import type { Transceiver } from "./transceiver"
@@ -15,44 +15,46 @@ export class FamilyTracker<
15
15
  ? Signal
16
16
  : never
17
17
 
18
- public readonly findLatestUpdateState: RegularAtomFamily<
18
+ public readonly latestUpdateAtoms: RegularAtomFamily<
19
19
  typeof this.Update | null,
20
20
  FamilyMemberKey
21
21
  >
22
- public readonly findMutableState: MutableAtomFamily<Core, any, FamilyMemberKey>
22
+ public readonly mutableAtoms: MutableAtomFamily<Core, any, FamilyMemberKey>
23
23
 
24
24
  public constructor(
25
- findMutableState: MutableAtomFamily<Core, any, FamilyMemberKey>,
25
+ mutableAtoms: MutableAtomFamily<Core, any, FamilyMemberKey>,
26
26
  store: Store,
27
27
  ) {
28
- this.findLatestUpdateState = createRegularAtomFamily<
28
+ this.latestUpdateAtoms = createRegularAtomFamily<
29
29
  typeof this.Update | null,
30
30
  FamilyMemberKey
31
31
  >(
32
32
  {
33
- key: `*${findMutableState.key}`,
33
+ key: `*${mutableAtoms.key}`,
34
34
  default: null,
35
35
  },
36
36
  store,
37
37
  )
38
- this.findMutableState = findMutableState
39
- this.findMutableState.subject.subscribe(
38
+ this.mutableAtoms = mutableAtoms
39
+ this.mutableAtoms.subject.subscribe(
40
40
  `store=${store.config.name}::tracker-atom-family`,
41
- (atomToken) => {
42
- if (atomToken.family) {
43
- const key = parseJson(atomToken.family.subKey) as FamilyMemberKey
44
- this.findLatestUpdateState(key)
45
- new Tracker<Core>(atomToken, store)
41
+ (event) => {
42
+ if (event.token.family) {
43
+ const key = parseJson(event.token.family.subKey) as FamilyMemberKey
44
+ seekInStore(this.latestUpdateAtoms, key, store)
45
+ new Tracker<Core>(event.token, store)
46
46
  }
47
47
  },
48
48
  )
49
- this.findLatestUpdateState.subject.subscribe(
49
+ this.latestUpdateAtoms.subject.subscribe(
50
50
  `store=${store.config.name}::tracker-atom-family`,
51
- (atomToken) => {
52
- if (atomToken.family) {
53
- const key = parseJson(atomToken.family.subKey) as FamilyMemberKey
54
- const mutableAtomToken = this.findMutableState(key)
55
- new Tracker<Core>(mutableAtomToken, store)
51
+ (event) => {
52
+ if (event.token.family) {
53
+ const key = parseJson(event.token.family.subKey) as FamilyMemberKey
54
+ const mutableAtomToken = seekInStore(this.mutableAtoms, key, store)
55
+ if (mutableAtomToken) {
56
+ new Tracker<Core>(mutableAtomToken, store)
57
+ }
56
58
  }
57
59
  },
58
60
  )
@@ -4,22 +4,35 @@ import type {
4
4
  TimelineToken,
5
5
  TransactionToken,
6
6
  } from "atom.io"
7
+ import type { MoleculeFamilyToken, MoleculeToken } from "atom.io/immortal"
7
8
 
8
9
  import type { Store } from "./store"
9
10
 
10
11
  const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1)
11
12
 
12
13
  type AtomIOToken =
14
+ | MoleculeFamilyToken<any, any, any>
15
+ | MoleculeToken<any, any, any>
13
16
  | ReadableFamilyToken<any, any>
14
17
  | ReadableToken<any>
15
18
  | TimelineToken<any>
16
19
  | TransactionToken<any>
17
20
 
18
21
  function prettyPrintTokenType(token: AtomIOToken) {
19
- if (token.type === `readonly_selector`) {
20
- return `Readonly Selector`
22
+ switch (token.type) {
23
+ case `atom_family`:
24
+ return `Atom Family`
25
+ case `molecule_family`:
26
+ return `Molecule Family`
27
+ case `readonly_selector`:
28
+ return `Readonly Selector`
29
+ case `readonly_selector_family`:
30
+ return `Readonly Selector Family`
31
+ case `selector_family`:
32
+ return `Selector Family`
33
+ default:
34
+ return capitalize(token.type)
21
35
  }
22
- return capitalize(token.type)
23
36
  }
24
37
 
25
38
  export class NotFoundError extends Error {
@@ -19,9 +19,9 @@ export const createReadonlySelector = <T>(
19
19
  const target = newest(store)
20
20
  const subject = new Subject<{ newValue: T; oldValue: T }>()
21
21
 
22
- const { get, find } = registerSelector(options.key, target)
22
+ const { get, find, seek, json } = registerSelector(options.key, target)
23
23
  const getSelf = () => {
24
- const value = options.get({ get, find })
24
+ const value = options.get({ get, find, seek, json })
25
25
  cacheValue(options.key, value, subject, newest(store))
26
26
  return value
27
27
  }
@@ -50,6 +50,5 @@ export const createReadonlySelector = <T>(
50
50
  if (family) {
51
51
  token.family = family
52
52
  }
53
- store.on.selectorCreation.next(token)
54
53
  return token
55
54
  }
@@ -24,7 +24,11 @@ export function createStandaloneSelector<T>(
24
24
  const isWritable = `set` in options
25
25
 
26
26
  if (isWritable) {
27
- return createWritableSelector(options, undefined, store)
27
+ const state = createWritableSelector(options, undefined, store)
28
+ store.on.selectorCreation.next(state)
29
+ return state
28
30
  }
29
- return createReadonlySelector(options, undefined, store)
31
+ const state = createReadonlySelector(options, undefined, store)
32
+ store.on.selectorCreation.next(state)
33
+ return state
30
34
  }
@@ -22,8 +22,8 @@ export const createWritableSelector = <T>(
22
22
  const target = newest(store)
23
23
  const subject = new Subject<{ newValue: T; oldValue: T }>()
24
24
  const transactors = registerSelector(options.key, target)
25
- const { find, get } = transactors
26
- const readonlyTransactors = { find, get }
25
+ const { find, get, seek, json } = transactors
26
+ const readonlyTransactors = { find, get, seek, json }
27
27
 
28
28
  const getSelf = () => {
29
29
  const value = options.get(readonlyTransactors)
@@ -70,6 +70,5 @@ export const createWritableSelector = <T>(
70
70
  if (family) {
71
71
  token.family = family
72
72
  }
73
- store.on.selectorCreation.next(token)
74
73
  return token
75
74
  }
@@ -1,7 +1,7 @@
1
1
  import type { ReadonlySelectorToken, WritableSelectorToken } from "atom.io"
2
2
 
3
3
  import type { Store } from ".."
4
- import { newest } from ".."
4
+ import { isChildStore, newest, withdraw } from ".."
5
5
 
6
6
  export function disposeSelector(
7
7
  selectorToken: ReadonlySelectorToken<unknown> | WritableSelectorToken<unknown>,
@@ -9,30 +9,74 @@ export function disposeSelector(
9
9
  ): void {
10
10
  const target = newest(store)
11
11
  const { key } = selectorToken
12
- switch (selectorToken.type) {
13
- case `selector`:
14
- target.selectors.delete(key)
15
- break
16
- case `readonly_selector`:
17
- target.readonlySelectors.delete(key)
18
- break
19
- }
20
- target.valueMap.delete(key)
21
- target.selectorAtoms.delete(key)
22
- const downstreamTokens = target.selectorGraph
23
- .getRelationEntries({ upstreamSelectorKey: key })
24
- .filter(([_, { source }]) => source === key)
25
- .map(
26
- ([downstreamSelectorKey]) =>
27
- target.selectors.get(downstreamSelectorKey) ??
28
- target.readonlySelectors.get(downstreamSelectorKey),
12
+ const selector = target.selectors.get(key) ?? target.readonlySelectors.get(key)
13
+ if (!selector) {
14
+ store.logger.info(
15
+ `❌`,
16
+ `selector`,
17
+ key,
18
+ `Tried to dispose selector, but it does not exist in the store.`,
19
+ )
20
+ } else if (!selector.family) {
21
+ store.logger.error(
22
+ `❌`,
23
+ `selector`,
24
+ key,
25
+ `Standalone selectors cannot be disposed.`,
29
26
  )
30
- for (const downstreamToken of downstreamTokens) {
31
- if (downstreamToken) {
32
- disposeSelector(downstreamToken, store)
27
+ } else {
28
+ switch (selectorToken.type) {
29
+ case `selector`:
30
+ {
31
+ target.selectors.delete(key)
32
+ const family = withdraw(
33
+ { key: selector.family.key, type: `selector_family` },
34
+ store,
35
+ )
36
+ family.subject.next({
37
+ type: `state_disposal`,
38
+ token: selectorToken,
39
+ })
40
+ }
41
+ break
42
+ case `readonly_selector`:
43
+ {
44
+ target.readonlySelectors.delete(key)
45
+ const family = withdraw(
46
+ { key: selector.family.key, type: `readonly_selector_family` },
47
+ store,
48
+ )
49
+ family.subject.next({
50
+ type: `state_disposal`,
51
+ token: selectorToken,
52
+ })
53
+ }
54
+ break
55
+ }
56
+ target.valueMap.delete(key)
57
+ target.selectorAtoms.delete(key)
58
+ const downstreamTokens = target.selectorGraph
59
+ .getRelationEntries({ upstreamSelectorKey: key })
60
+ .filter(([_, { source }]) => source === key)
61
+ .map(
62
+ ([downstreamSelectorKey]) =>
63
+ target.selectors.get(downstreamSelectorKey) ??
64
+ target.readonlySelectors.get(downstreamSelectorKey),
65
+ )
66
+ for (const downstreamToken of downstreamTokens) {
67
+ if (downstreamToken) {
68
+ disposeSelector(downstreamToken, store)
69
+ }
70
+ }
71
+ target.selectorGraph.delete(key)
72
+ store.logger.info(`🔥`, selectorToken.type, key, `deleted`)
73
+ if (isChildStore(target) && target.transactionMeta.phase === `building`) {
74
+ target.transactionMeta.update.updates.push({
75
+ type: `state_disposal`,
76
+ token: selectorToken,
77
+ })
78
+ } else {
79
+ store.on.selectorDisposal.next(selectorToken)
33
80
  }
34
81
  }
35
- target.selectorGraph.delete(key)
36
- store.logger.info(`🔥`, selectorToken.type, key, `deleted`)
37
- store.on.selectorDisposal.next(selectorToken)
38
82
  }
@@ -1,11 +1,14 @@
1
- import type { findState, Transactors } from "atom.io"
1
+ import type { Transactors } from "atom.io"
2
+ import type { findState } from "atom.io/ephemeral"
3
+ import type { seekState } from "atom.io/immortal"
2
4
 
3
- import { findInStore } from "../families"
5
+ import { findInStore, seekInStore } from "../families"
4
6
  import { readOrComputeValue } from "../get-state/read-or-compute-value"
5
7
  import { newest } from "../lineage"
8
+ import { getJsonToken } from "../mutable"
6
9
  import { setAtomOrSelector } from "../set-state"
7
10
  import type { Store } from "../store"
8
- import { withdrawOrCreate } from "../store"
11
+ import { withdraw } from "../store"
9
12
  import { updateSelectorAtoms } from "./update-selector-atoms"
10
13
 
11
14
  export const registerSelector = (
@@ -15,7 +18,7 @@ export const registerSelector = (
15
18
  get: (dependency) => {
16
19
  const target = newest(store)
17
20
 
18
- const dependencyState = withdrawOrCreate(dependency, store)
21
+ const dependencyState = withdraw(dependency, store)
19
22
  const dependencyValue = readOrComputeValue(dependencyState, store)
20
23
 
21
24
  store.logger.info(
@@ -40,8 +43,10 @@ export const registerSelector = (
40
43
  return dependencyValue
41
44
  },
42
45
  set: (WritableToken, newValue) => {
43
- const state = withdrawOrCreate(WritableToken, store)
46
+ const state = withdraw(WritableToken, store)
44
47
  setAtomOrSelector(state, newValue, store)
45
48
  },
46
49
  find: ((token, key) => findInStore(token, key, store)) as typeof findState,
50
+ seek: ((token, key) => seekInStore(token, key, store)) as typeof seekState,
51
+ json: (token) => getJsonToken(token, store),
47
52
  })
@@ -2,7 +2,7 @@ import type { WritableToken } from "atom.io"
2
2
 
3
3
  import { closeOperation, openOperation } from "../operation"
4
4
  import type { Store } from "../store"
5
- import { withdrawOrCreate } from "../store"
5
+ import { withdraw } from "../store"
6
6
  import { setAtomOrSelector } from "./set-atom-or-selector"
7
7
 
8
8
  export function setIntoStore<T, New extends T>(
@@ -27,7 +27,7 @@ export function setIntoStore<T, New extends T>(
27
27
  )
28
28
  return
29
29
  }
30
- const state = withdrawOrCreate(token, store)
30
+ const state = withdraw(token, store)
31
31
  setAtomOrSelector(state, value, store)
32
32
  closeOperation(store)
33
33
  }
@@ -38,7 +38,11 @@ export const stowUpdate = <T>(
38
38
  if (!shouldStow) {
39
39
  return
40
40
  }
41
- const atomUpdate: KeyedStateUpdate<T> = { key, ...update }
41
+ const atomUpdate: KeyedStateUpdate<T> = {
42
+ type: `atom_update`,
43
+ key,
44
+ ...update,
45
+ }
42
46
  if (state.family) {
43
47
  atomUpdate.family = state.family
44
48
  }