atom.io 0.9.9 → 0.10.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 (64) hide show
  1. package/dist/index.d.mts +11 -4
  2. package/dist/index.d.ts +11 -4
  3. package/dist/index.js +36 -56
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +34 -55
  6. package/dist/index.mjs.map +1 -1
  7. package/internal/dist/index.d.mts +52 -25
  8. package/internal/dist/index.d.ts +52 -25
  9. package/internal/dist/index.js +352 -385
  10. package/internal/dist/index.js.map +1 -1
  11. package/internal/dist/index.mjs +349 -385
  12. package/internal/dist/index.mjs.map +1 -1
  13. package/internal/src/atom/create-atom.ts +5 -5
  14. package/internal/src/atom/delete-atom.ts +9 -2
  15. package/internal/src/atom/is-default.ts +2 -2
  16. package/internal/src/caching.ts +7 -5
  17. package/internal/src/get-state-internal.ts +4 -4
  18. package/internal/src/index.ts +1 -0
  19. package/internal/src/keys.ts +30 -0
  20. package/internal/src/mutable/create-mutable-atom.ts +2 -2
  21. package/internal/src/mutable/tracker.ts +1 -1
  22. package/internal/src/operation.ts +7 -7
  23. package/internal/src/selector/create-read-write-selector.ts +2 -8
  24. package/internal/src/selector/create-readonly-selector.ts +1 -1
  25. package/internal/src/selector/create-selector.ts +5 -3
  26. package/internal/src/selector/get-selector-dependency-keys.ts +20 -0
  27. package/internal/src/selector/index.ts +1 -1
  28. package/internal/src/selector/register-selector.ts +4 -11
  29. package/internal/src/selector/trace-selector-atoms.ts +26 -26
  30. package/internal/src/selector/update-selector-atoms.ts +14 -14
  31. package/internal/src/set-state/copy-mutable-if-needed.ts +1 -1
  32. package/internal/src/set-state/copy-mutable-in-transaction.ts +1 -1
  33. package/internal/src/set-state/emit-update.ts +1 -1
  34. package/internal/src/set-state/evict-downstream.ts +5 -6
  35. package/internal/src/set-state/set-atom.ts +1 -4
  36. package/internal/src/set-state/stow-update.ts +10 -4
  37. package/internal/src/store/index.ts +0 -1
  38. package/internal/src/store/store.ts +27 -10
  39. package/internal/src/store/withdraw-new-family-member.ts +1 -1
  40. package/internal/src/store/withdraw.ts +1 -1
  41. package/internal/src/subscribe/recall-state.ts +2 -2
  42. package/internal/src/subscribe/subscribe-to-root-atoms.ts +7 -8
  43. package/internal/src/timeline/add-atom-to-timeline.ts +8 -8
  44. package/internal/src/timeline/time-travel-internal.ts +12 -12
  45. package/internal/src/timeline/timeline-internal.ts +2 -2
  46. package/internal/src/transaction/abort-transaction.ts +3 -3
  47. package/internal/src/transaction/apply-transaction.ts +6 -6
  48. package/internal/src/transaction/build-transaction.ts +2 -3
  49. package/internal/src/transaction/redo-transaction.ts +1 -1
  50. package/internal/src/transaction/transaction-internal.ts +2 -2
  51. package/internal/src/transaction/undo-transaction.ts +1 -1
  52. package/package.json +3 -3
  53. package/react-devtools/dist/index.d.mts +3 -3
  54. package/react-devtools/dist/index.d.ts +3 -3
  55. package/realtime-client/dist/index.js +6 -9
  56. package/realtime-client/dist/index.js.map +1 -1
  57. package/realtime-client/dist/index.mjs +6 -9
  58. package/realtime-client/dist/index.mjs.map +1 -1
  59. package/realtime-client/src/use-server-action.ts +6 -8
  60. package/src/atom.ts +3 -0
  61. package/src/logger.ts +25 -36
  62. package/src/subscribe.ts +7 -7
  63. package/internal/src/selector/lookup-selector-sources.ts +0 -20
  64. package/internal/src/store/lookup.ts +0 -26
@@ -29,16 +29,16 @@ export function createAtom<T>(
29
29
  family?: FamilyMetadata,
30
30
  store: Store = IMPLICIT.STORE,
31
31
  ): AtomToken<T> {
32
- store.config.logger?.info?.(
32
+ store.logger.info(
33
33
  `🔨 creating atom "${options.key}" in store "${store.config.name}"`,
34
34
  )
35
35
  const core = target(store)
36
36
  const existing = core.atoms.get(options.key)
37
37
  if (existing) {
38
- store.config.logger?.error?.(
39
- `Tried to create atom "${options.key}",`,
38
+ store.logger.error(
39
+ `❓ Tried to create atom "${options.key}",`,
40
40
  `but it already exists in the store.`,
41
- `(Ignore if you are using hot module replacement.)`,
41
+ `(Ignore if you are in development using hot module replacement.)`,
42
42
  )
43
43
  return deposit(existing)
44
44
  }
@@ -47,7 +47,7 @@ export function createAtom<T>(
47
47
  ...options,
48
48
  type: `atom`,
49
49
  install: (store: Store) => {
50
- store.config.logger?.info?.(
50
+ store.logger.info(
51
51
  `🛠️ installing atom "${options.key}" in store "${store.config.name}"`,
52
52
  )
53
53
  return `mutable` in options
@@ -1,10 +1,17 @@
1
- import type { StoreCore } from ".."
1
+ import type { AtomToken } from "~/packages/atom.io/src"
2
+ import type { Store } from ".."
2
3
  import { IMPLICIT, target } from ".."
3
4
 
4
- export function deleteAtom(key: string, core: StoreCore = IMPLICIT.STORE): void {
5
+ export function deleteAtom(
6
+ atomToken: AtomToken<unknown>,
7
+ store: Store = IMPLICIT.STORE,
8
+ ): void {
9
+ const core = target(store)
10
+ const { key } = atomToken
5
11
  core.atoms.delete(key)
6
12
  core.valueMap.delete(key)
7
13
  core.selectorAtoms.delete(key)
8
14
  core.atomsThatAreDefault.delete(key)
9
15
  core.timelineAtoms.delete(key)
16
+ store.logger.info(`🔥 Atom "${key}" deleted`)
10
17
  }
@@ -32,6 +32,6 @@ export const isSelectorDefault = (
32
32
  key: string,
33
33
  store: Store = IMPLICIT.STORE,
34
34
  ): boolean => {
35
- const roots = traceAllSelectorAtoms(key, store)
36
- return roots.every((root) => isAtomDefault(root.key, store))
35
+ const rootKeys = traceAllSelectorAtoms(key, store)
36
+ return rootKeys.every((rootKey) => isAtomDefault(rootKey, store))
37
37
  }
@@ -27,10 +27,9 @@ export const cacheValue = (
27
27
  subject.next({ newValue: value, oldValue: value })
28
28
  })
29
29
  .catch((error) => {
30
- store.config.logger?.error(
31
- `Promised value for "${key}" rejected:`,
32
- error,
33
- )
30
+ if (error !== `canceled`) {
31
+ store.logger.error(`🙅‍♂️ Promised value for "${key}" rejected:`, error)
32
+ }
34
33
  })
35
34
  } else {
36
35
  target(store).valueMap.set(key, value)
@@ -56,6 +55,9 @@ export const evictCachedValue = (
56
55
  if (currentValue instanceof Future) {
57
56
  currentValue.cancel()
58
57
  }
58
+ if (core.operation.open) {
59
+ core.operation.prev.set(key, currentValue)
60
+ }
59
61
  core.valueMap.delete(key)
60
- store.config.logger?.info(` xx evicted "${key}"`)
62
+ store.logger.info(`🗑 evicted "${key}"`)
61
63
  }
@@ -9,15 +9,15 @@ export const getState__INTERNAL = <T>(
9
9
  store: Store = IMPLICIT.STORE,
10
10
  ): T => {
11
11
  if (isValueCached(state.key, store)) {
12
- store.config.logger?.info(`>> read "${state.key}"`)
12
+ store.logger.info(`📖 reading "${state.key}"`)
13
13
  return readCachedValue(state.key, store)
14
14
  }
15
15
  if (state.type !== `atom`) {
16
- store.config.logger?.info(`-> calc "${state.key}"`)
16
+ store.logger.info(`🧮 calculating "${state.key}"`)
17
17
  return state.get()
18
18
  }
19
- store.config.logger?.error(
20
- `Attempted to get atom "${state.key}", which was never initialized in store "${store.config.name}".`,
19
+ store.logger.error(
20
+ `🐞 Attempted to get atom "${state.key}", which was never initialized in store "${store.config.name}".`,
21
21
  )
22
22
  return state.default
23
23
  }
@@ -3,6 +3,7 @@ export * from "./caching"
3
3
  export * from "./families"
4
4
  export * from "./future"
5
5
  export * from "./get-state-internal"
6
+ export * from "./keys"
6
7
  export * from "./operation"
7
8
  export * from "./mutable"
8
9
  export * from "./selector"
@@ -0,0 +1,30 @@
1
+ import type { Store } from "./store"
2
+ import { target } from "./transaction"
3
+
4
+ export type AtomKey<T> = string & { __atomKey?: never; __brand?: T }
5
+ export type SelectorKey<T> = string & { __selectorKey?: never; __brand?: T }
6
+ export type ReadonlySelectorKey<T> = string & {
7
+ __readonlySelectorKey?: never
8
+ __brand?: T
9
+ }
10
+
11
+ export const isAtomKey = (key: string, store: Store): key is AtomKey<unknown> =>
12
+ target(store).atoms.has(key)
13
+ export const isSelectorKey = (
14
+ key: string,
15
+ store: Store,
16
+ ): key is SelectorKey<unknown> => target(store).selectors.has(key)
17
+ export const isReadonlySelectorKey = (
18
+ key: string,
19
+ store: Store,
20
+ ): key is ReadonlySelectorKey<unknown> =>
21
+ target(store).readonlySelectors.has(key)
22
+
23
+ export type StateKey<T> = AtomKey<T> | ReadonlySelectorKey<T> | SelectorKey<T>
24
+ export const isStateKey = (
25
+ key: string,
26
+ store: Store,
27
+ ): key is StateKey<unknown> =>
28
+ isAtomKey(key, store) ||
29
+ isSelectorKey(key, store) ||
30
+ isReadonlySelectorKey(key, store)
@@ -17,7 +17,7 @@ export function createMutableAtom<
17
17
  options: MutableAtomOptions<Core, SerializableCore>,
18
18
  store: Store = IMPLICIT.STORE,
19
19
  ): MutableAtomToken<Core, SerializableCore> {
20
- store.config.logger?.info(
20
+ store.logger.info(
21
21
  `🔧 creating mutable atom "${options.key}" in store "${store.config.name}"`,
22
22
  )
23
23
  const coreState = createAtom<Core>(options, undefined, store)
@@ -26,7 +26,7 @@ export function createMutableAtom<
26
26
  subscribe(
27
27
  jsonState,
28
28
  () => {
29
- store.config.logger?.info(
29
+ store.logger.info(
30
30
  `🔍 tracker-initializer:${store?.config.name}:${
31
31
  store.transactionStatus.phase === `idle`
32
32
  ? `main`
@@ -21,7 +21,7 @@ export class Tracker<Mutable extends Transceiver<any>> {
21
21
  store: Store = IMPLICIT.STORE,
22
22
  ): AtomToken<typeof this.Update | null> {
23
23
  const latestUpdateStateKey = `*${mutableState.key}`
24
- deleteAtom(latestUpdateStateKey, target(store))
24
+ deleteAtom({ type: `atom`, key: latestUpdateStateKey }, store)
25
25
  const familyMetaData: FamilyMetadata | undefined = mutableState.family
26
26
  ? {
27
27
  key: `*${mutableState.family.key}`,
@@ -22,7 +22,7 @@ export const openOperation = (
22
22
  ): `rejection` | undefined => {
23
23
  const core = target(store)
24
24
  if (core.operation.open) {
25
- store.config.logger?.error(
25
+ store.logger.error(
26
26
  `❌ failed to setState to "${token.key}" during a setState for "${core.operation.token.key}"`,
27
27
  )
28
28
  return `rejection`
@@ -30,11 +30,11 @@ export const openOperation = (
30
30
  core.operation = {
31
31
  open: true,
32
32
  done: new Set(),
33
- prev: new Map(store.valueMap),
33
+ prev: new Map(),
34
34
  time: Date.now(),
35
35
  token,
36
36
  }
37
- store.config.logger?.info(
37
+ store.logger.info(
38
38
  `⭕ operation start from "${token.key}" in store "${store.config.name}"${
39
39
  store.transactionStatus.phase === `idle`
40
40
  ? ``
@@ -45,14 +45,14 @@ export const openOperation = (
45
45
  export const closeOperation = (store: Store): void => {
46
46
  const core = target(store)
47
47
  core.operation = { open: false }
48
- store.config.logger?.info(`🔴 operation done`)
48
+ store.logger.info(`🔴 operation done`)
49
49
  store.subject.operationStatus.next(core.operation)
50
50
  }
51
51
 
52
52
  export const isDone = (key: string, store: Store = IMPLICIT.STORE): boolean => {
53
53
  const core = target(store)
54
54
  if (!core.operation.open) {
55
- store.config.logger?.warn(
55
+ store.logger.warn(
56
56
  `isDone called outside of an operation. This is probably a bug.`,
57
57
  )
58
58
  return true
@@ -62,8 +62,8 @@ export const isDone = (key: string, store: Store = IMPLICIT.STORE): boolean => {
62
62
  export const markDone = (key: string, store: Store = IMPLICIT.STORE): void => {
63
63
  const core = target(store)
64
64
  if (!core.operation.open) {
65
- store.config.logger?.warn(
66
- `markDone called outside of an operation. This is probably a bug.`,
65
+ store.logger.warn(
66
+ `🐞 markDone called outside of an operation. This is probably a bug.`,
67
67
  )
68
68
  return
69
69
  }
@@ -27,13 +27,7 @@ export const createReadWriteSelector = <T>(
27
27
  const setSelf = (next: T | ((oldValue: T) => T)): void => {
28
28
  const oldValue = getSelf()
29
29
  const newValue = become(next)(oldValue)
30
- store.config.logger?.info(
31
- ` <- "${options.key}" went (`,
32
- oldValue,
33
- `->`,
34
- newValue,
35
- `)`,
36
- )
30
+ store.logger.info(`📝 set "${options.key}" (`, oldValue, `->`, newValue, `)`)
37
31
  cacheValue(options.key, newValue, subject, store)
38
32
  markDone(options.key, store)
39
33
  if (store.transactionStatus.phase === `idle`) {
@@ -52,7 +46,7 @@ export const createReadWriteSelector = <T>(
52
46
  }
53
47
  core.selectors.set(options.key, mySelector)
54
48
  const initialValue = getSelf()
55
- store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
49
+ store.logger.info(`✨ "${options.key}" =`, initialValue)
56
50
  const token: SelectorToken<T> = {
57
51
  key: options.key,
58
52
  type: `selector`,
@@ -36,7 +36,7 @@ export const createReadonlySelector = <T>(
36
36
  }
37
37
  core.readonlySelectors.set(options.key, readonlySelector)
38
38
  const initialValue = getSelf()
39
- store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
39
+ store.logger.info(`✨ "${options.key}" =`, initialValue)
40
40
  const token: ReadonlySelectorToken<T> = {
41
41
  key: options.key,
42
42
  type: `readonly_selector`,
@@ -51,10 +51,12 @@ export function createSelector<T>(
51
51
  const existingReadonly = core.readonlySelectors.get(options.key)
52
52
 
53
53
  if (existingWritable || existingReadonly) {
54
- store.config.logger?.error?.(
55
- `Tried to create ${existingReadonly ? `readonly selector` : `selector`}`,
54
+ store.logger.error(
55
+ `❓ Tried to create ${
56
+ existingReadonly ? `readonly selector` : `selector`
57
+ }`,
56
58
  `"${options.key}", but it already exists in the store.`,
57
- `(Ignore if you are using hot module replacement.)`,
59
+ `(Ignore if you are in development using hot module replacement.)`,
58
60
  )
59
61
  }
60
62
 
@@ -0,0 +1,20 @@
1
+ import type { AtomKey, ReadonlySelectorKey, SelectorKey } from "../keys"
2
+ import { isStateKey } from "../keys"
3
+ import type { Store } from "../store"
4
+ import { target } from "../transaction"
5
+
6
+ export const getSelectorDependencyKeys = (
7
+ key: string,
8
+ store: Store,
9
+ ): (
10
+ | AtomKey<unknown>
11
+ | ReadonlySelectorKey<unknown>
12
+ | SelectorKey<unknown>
13
+ )[] => {
14
+ const sources = target(store)
15
+ .selectorGraph.getRelationEntries({ downstreamSelectorKey: key })
16
+ .filter(([_, { source }]) => source !== key)
17
+ .map(([_, { source }]) => source)
18
+ .filter((source) => isStateKey(source, store))
19
+ return sources
20
+ }
@@ -1,4 +1,4 @@
1
- export * from "./lookup-selector-sources"
1
+ export * from "./get-selector-dependency-keys"
2
2
  export * from "./register-selector"
3
3
  export * from "./create-selector"
4
4
  export * from "./trace-selector-atoms"
@@ -25,17 +25,10 @@ export const registerSelector = (
25
25
  }
26
26
  const dependencyValue = getState__INTERNAL(dependencyState, store)
27
27
 
28
- if (alreadyRegistered) {
29
- store.config.logger?.info(
30
- ` || ${selectorKey} <- ${dependency.key} =`,
31
- dependencyValue,
32
- )
33
- } else {
34
- store.config.logger?.info(
35
- `🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
36
- dependencyValue,
37
- `)`,
38
- )
28
+ store.logger.info(
29
+ `🔌 selector "${selectorKey}" registers dependency ( "${dependency.key}" = ${dependencyValue} )`,
30
+ )
31
+ if (!alreadyRegistered) {
39
32
  core.selectorGraph = core.selectorGraph.set(
40
33
  {
41
34
  upstreamSelectorKey: dependency.key,
@@ -1,50 +1,50 @@
1
- import type { AtomToken, ReadonlySelectorToken, StateToken } from "atom.io"
2
-
3
1
  import type { Store } from ".."
4
- import { lookupSelectorSources } from "./lookup-selector-sources"
2
+ import type { AtomKey, StateKey } from "../keys"
3
+ import { isAtomKey } from "../keys"
4
+ import { getSelectorDependencyKeys } from "./get-selector-dependency-keys"
5
5
 
6
6
  export const traceSelectorAtoms = (
7
7
  selectorKey: string,
8
- dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
8
+ directDependencyKey: StateKey<unknown>,
9
9
  store: Store,
10
- ): AtomToken<unknown>[] => {
11
- const roots: AtomToken<unknown>[] = []
10
+ ): AtomKey<unknown>[] => {
11
+ const rootKeys: AtomKey<unknown>[] = []
12
12
 
13
- const sources = lookupSelectorSources(dependency.key, store)
13
+ const indirectDependencyKeys = getSelectorDependencyKeys(
14
+ directDependencyKey,
15
+ store,
16
+ )
14
17
  let depth = 0
15
- while (sources.length > 0) {
18
+ while (indirectDependencyKeys.length > 0) {
16
19
  // biome-ignore lint/style/noNonNullAssertion: just checked length ^^^
17
- const source = sources.shift()!
20
+ const indirectDependencyKey = indirectDependencyKeys.shift()!
18
21
  ++depth
19
- if (depth > 999) {
20
- store.config.logger?.warn(
21
- `Maximum selector dependency depth exceeded 999 in selector "${selectorKey}".`,
22
- )
23
- }
24
22
  if (depth > 99999) {
25
23
  throw new Error(
26
- `Maximum selector dependency depth exceeded in selector "${selectorKey}".`,
24
+ `Maximum selector dependency depth exceeded (> 99999) in selector "${selectorKey}". This is likely due to a circular dependency.`,
27
25
  )
28
26
  }
29
27
 
30
- if (source.type !== `atom`) {
31
- sources.push(...lookupSelectorSources(source.key, store))
32
- } else {
33
- roots.push(source)
28
+ if (!isAtomKey(indirectDependencyKey, store)) {
29
+ indirectDependencyKeys.push(
30
+ ...getSelectorDependencyKeys(indirectDependencyKey, store),
31
+ )
32
+ } else if (!rootKeys.includes(indirectDependencyKey)) {
33
+ rootKeys.push(indirectDependencyKey)
34
34
  }
35
35
  }
36
36
 
37
- return roots
37
+ return rootKeys
38
38
  }
39
39
 
40
40
  export const traceAllSelectorAtoms = (
41
41
  selectorKey: string,
42
42
  store: Store,
43
- ): AtomToken<unknown>[] => {
44
- const sources = lookupSelectorSources(selectorKey, store)
45
- return sources.flatMap((source) =>
46
- source.type === `atom`
47
- ? source
48
- : traceSelectorAtoms(selectorKey, source, store),
43
+ ): AtomKey<unknown>[] => {
44
+ const directDependencyKeys = getSelectorDependencyKeys(selectorKey, store)
45
+ return directDependencyKeys.flatMap((depKey) =>
46
+ isAtomKey(depKey, store)
47
+ ? depKey
48
+ : traceSelectorAtoms(selectorKey, depKey, store),
49
49
  )
50
50
  }
@@ -15,20 +15,20 @@ export const updateSelectorAtoms = (
15
15
  selectorKey,
16
16
  atomKey: dependency.key,
17
17
  })
18
- store.config.logger?.info(
19
- ` || adding root for "${selectorKey}": ${dependency.key}`,
18
+ store.logger.info(
19
+ `🔍 selector "${selectorKey}" discovers root atom "${dependency.key}"`,
20
20
  )
21
- return
22
- }
23
- const roots = traceSelectorAtoms(selectorKey, dependency, store)
24
- store.config.logger?.info(
25
- ` || adding roots for "${selectorKey}":`,
26
- roots.map((r) => r.key),
27
- )
28
- for (const root of roots) {
29
- core.selectorAtoms = core.selectorAtoms.set({
30
- selectorKey,
31
- atomKey: root.key,
32
- })
21
+ } else {
22
+ const rootKeys = traceSelectorAtoms(selectorKey, dependency.key, store)
23
+ store.logger.info(
24
+ `🔍 selector "${selectorKey}" discovers root atoms:`,
25
+ rootKeys.map((r) => r),
26
+ )
27
+ for (const atomKey of rootKeys) {
28
+ core.selectorAtoms = core.selectorAtoms.set({
29
+ selectorKey,
30
+ atomKey,
31
+ })
32
+ }
33
33
  }
34
34
  }
@@ -13,7 +13,7 @@ export function copyMutableIfNeeded<T>(
13
13
  const originValue = origin.valueMap.get(atom.key)
14
14
  const targetValue = target.valueMap.get(atom.key)
15
15
  if (originValue === targetValue) {
16
- origin.config.logger?.info(`📃 copying`, `${atom.key}`)
16
+ origin.logger.info(`📃 copying`, `${atom.key}`)
17
17
  const copiedValue = transform.fromJson(transform.toJson(originValue))
18
18
  target.valueMap.set(atom.key, copiedValue)
19
19
  new Tracker(atom, origin)
@@ -14,7 +14,7 @@ export function copyMutableIfWithinTransaction<T>(
14
14
  store.transactionStatus.phase === `applying`
15
15
  ) {
16
16
  if (`toJson` in atom && `fromJson` in atom) {
17
- store.config.logger?.info(
17
+ store.logger.info(
18
18
  `📄 copyMutableIfWithinTransaction: ${atom.key} is mutable`,
19
19
  )
20
20
  const copiedValue = copyMutableIfNeeded(
@@ -10,7 +10,7 @@ export const emitUpdate = <T>(
10
10
  store: Store,
11
11
  ): void => {
12
12
  const { key } = state
13
- const { logger } = store.config
13
+ const { logger } = store
14
14
  logger?.info(
15
15
  `📢 ${state.type} "${key}" went (`,
16
16
  update.oldValue,
@@ -11,23 +11,22 @@ export const evictDownStream = <T>(
11
11
  ): void => {
12
12
  const core = target(store)
13
13
  const downstreamKeys = core.selectorAtoms.getRelatedKeys(state.key)
14
- store.config.logger?.info(
15
- ` || ${downstreamKeys?.size ?? `none`} downstream:`,
14
+ store.logger.info(
15
+ `🧹 evicting ${downstreamKeys?.size} states downstream from ${state.type} "${state.key}":`,
16
16
  downstreamKeys,
17
17
  )
18
18
  if (core.operation.open) {
19
- store.config.logger?.info(` ||`, [...core.operation.done], `already done`)
19
+ store.logger.info(`🧹`, [...core.operation.done], `already done`)
20
20
  }
21
21
  if (downstreamKeys) {
22
22
  for (const key of downstreamKeys) {
23
23
  if (isDone(key, store)) {
24
- store.config.logger?.info(` || ${key} already done`)
25
24
  continue
26
25
  }
27
26
  const state = core.selectors.get(key) ?? core.readonlySelectors.get(key)
28
27
  if (!state) {
29
- store.config.logger?.info(
30
- ` || ${key} was not found in selectors or readonlySelectors`,
28
+ store.logger.error(
29
+ `🐞 "${key}" was not found in selectors or readonlySelectors`,
31
30
  )
32
31
  return
33
32
  }
@@ -19,15 +19,12 @@ export const setAtom = <T>(
19
19
  const oldValue = getState__INTERNAL(atom, store)
20
20
  let newValue = copyMutableIfWithinTransaction(atom, store)
21
21
  newValue = become(next)(newValue)
22
- store.config.logger?.info(`<< setting atom "${atom.key}" to`, newValue)
22
+ store.logger.info(`📝 setting atom "${atom.key}" to`, newValue)
23
23
  cacheValue(atom.key, newValue, atom.subject, store)
24
24
  if (isAtomDefault(atom.key, store)) {
25
25
  markAtomAsNotDefault(atom.key, store)
26
26
  }
27
27
  markDone(atom.key, store)
28
- store.config.logger?.info(
29
- ` || evicting caches downstream from "${atom.key}"`,
30
- )
31
28
  evictDownStream(atom, store)
32
29
  const update = { oldValue, newValue }
33
30
  if (store.transactionStatus.phase !== `building`) {
@@ -22,10 +22,10 @@ export const stowUpdate = <T>(
22
22
  store: Store,
23
23
  ): void => {
24
24
  const { key } = state
25
- const { logger } = store.config
25
+ const { logger } = store
26
26
  if (store.transactionStatus.phase !== `building`) {
27
- store.config.logger?.warn(
28
- `stowUpdate called outside of a transaction. This is probably a bug.`,
27
+ store.logger.warn(
28
+ `🐞 stowUpdate called outside of a transaction. This is probably a bug.`,
29
29
  )
30
30
  return
31
31
  }
@@ -38,5 +38,11 @@ export const stowUpdate = <T>(
38
38
  atomUpdate.family = state.family
39
39
  }
40
40
  store.transactionStatus.atomUpdates.push(atomUpdate)
41
- logger?.info(`📝 ${key} stowed (`, update.oldValue, `->`, update.newValue, `)`)
41
+ store.logger.info(
42
+ `📁 ${key} stowed (`,
43
+ update.oldValue,
44
+ `->`,
45
+ update.newValue,
46
+ `)`,
47
+ )
42
48
  }
@@ -1,5 +1,4 @@
1
1
  export * from "./deposit"
2
- export * from "./lookup"
3
2
  export * from "./store"
4
3
  export * from "./withdraw"
5
4
  export * from "./withdraw-new-family-member"
@@ -1,3 +1,4 @@
1
+ import { AtomIOLogger } from "atom.io"
1
2
  import type {
2
3
  AtomFamily,
3
4
  AtomToken,
@@ -94,12 +95,33 @@ export class Store {
94
95
 
95
96
  public config: {
96
97
  name: string
97
- logger: Logger | null
98
- logger__INTERNAL: Logger
99
98
  } = {
100
99
  name: `IMPLICIT_STORE`,
101
- logger: { ...console, info: () => undefined },
102
- logger__INTERNAL: console,
100
+ }
101
+
102
+ public loggers: AtomIOLogger[] = [
103
+ new AtomIOLogger(
104
+ { ...console },
105
+ `warn`,
106
+ (message) => !message.includes(`👁‍🗨`),
107
+ ),
108
+ ]
109
+ public logger: Logger = {
110
+ error: (message: string) => {
111
+ for (const logger of this.loggers) {
112
+ logger.error(message)
113
+ }
114
+ },
115
+ info: (message: string) => {
116
+ for (const logger of this.loggers) {
117
+ logger.info(message)
118
+ }
119
+ },
120
+ warn: (message: string) => {
121
+ for (const logger of this.loggers) {
122
+ logger.warn(message)
123
+ }
124
+ },
103
125
  }
104
126
 
105
127
  public constructor(name: string, store: Store | null = null) {
@@ -110,14 +132,9 @@ export class Store {
110
132
  this.transactionStatus = { ...store?.transactionStatus }
111
133
  this.config = {
112
134
  ...store?.config,
113
- logger__INTERNAL: console,
114
- logger: {
115
- ...console,
116
- info: () => undefined,
117
- ...store?.config?.logger,
118
- },
119
135
  name,
120
136
  }
137
+
121
138
  for (const [, atom] of store.atoms) {
122
139
  atom.install(this)
123
140
  }
@@ -36,7 +36,7 @@ export function withdrawNewFamilyMember<T>(
36
36
  | StateToken<T>,
37
37
  store: Store,
38
38
  ): Atom<T> | ReadonlySelector<T> | Selector<T> | undefined {
39
- store.config.logger?.info(
39
+ store.logger.info(
40
40
  `👪 creating new family member "${token.key}" in store "${store.config.name}"`,
41
41
  )
42
42
  if (token.family) {
@@ -78,7 +78,7 @@ export function withdraw<T>(
78
78
  core.timelines.get(token.key)
79
79
 
80
80
  if (state) {
81
- store.config.logger?.info(`🛠️ add ${token.type} "${token.key}"`)
81
+ store.logger.info(`🛠️ add ${token.type} "${token.key}"`)
82
82
  switch (state.type) {
83
83
  case `atom`: {
84
84
  store.atoms.set(token.key, state)
@@ -10,8 +10,8 @@ export const recallState = <T>(
10
10
  ): T => {
11
11
  const core = target(store)
12
12
  if (!core.operation.open) {
13
- store.config.logger?.warn(
14
- `recall called outside of an operation. This is probably a bug.`,
13
+ store.logger.warn(
14
+ `🐞recall called outside of an operation. This is probably a bug.`,
15
15
  )
16
16
  return core.valueMap.get(state.key)
17
17
  }