atom.io 0.42.1 → 0.43.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 (53) hide show
  1. package/dist/data/index.js +4 -6
  2. package/dist/data/index.js.map +1 -1
  3. package/dist/internal/index.d.ts +1 -0
  4. package/dist/internal/index.d.ts.map +1 -1
  5. package/dist/internal/index.js +53 -97
  6. package/dist/internal/index.js.map +1 -1
  7. package/dist/introspection/index.js +2 -4
  8. package/dist/introspection/index.js.map +1 -1
  9. package/dist/react/index.d.ts +3 -4
  10. package/dist/react/index.d.ts.map +1 -1
  11. package/dist/react/index.js +17 -20
  12. package/dist/react/index.js.map +1 -1
  13. package/dist/react-devtools/index.js +6 -10
  14. package/dist/react-devtools/index.js.map +1 -1
  15. package/dist/realtime/index.js +2 -4
  16. package/dist/realtime/index.js.map +1 -1
  17. package/dist/realtime-client/index.js +4 -9
  18. package/dist/realtime-client/index.js.map +1 -1
  19. package/dist/realtime-react/index.d.ts +4 -4
  20. package/dist/realtime-react/index.d.ts.map +1 -1
  21. package/dist/realtime-react/index.js +16 -16
  22. package/dist/realtime-react/index.js.map +1 -1
  23. package/dist/realtime-server/index.js +8 -15
  24. package/dist/realtime-server/index.js.map +1 -1
  25. package/dist/realtime-testing/index.d.ts +3 -3
  26. package/dist/realtime-testing/index.d.ts.map +1 -1
  27. package/dist/realtime-testing/index.js +4 -2
  28. package/dist/realtime-testing/index.js.map +1 -1
  29. package/dist/transceivers/o-list/index.d.ts +6 -1
  30. package/dist/transceivers/o-list/index.d.ts.map +1 -1
  31. package/dist/transceivers/o-list/index.js +109 -42
  32. package/dist/transceivers/o-list/index.js.map +1 -1
  33. package/dist/transceivers/u-list/index.js +1 -2
  34. package/dist/transceivers/u-list/index.js.map +1 -1
  35. package/package.json +21 -20
  36. package/src/internal/atom/create-regular-atom.ts +1 -1
  37. package/src/internal/families/create-readonly-held-selector-family.ts +1 -1
  38. package/src/internal/families/create-readonly-pure-selector-family.ts +1 -1
  39. package/src/internal/families/create-regular-atom-family.ts +1 -1
  40. package/src/internal/families/create-writable-held-selector-family.ts +1 -1
  41. package/src/internal/families/create-writable-pure-selector-family.ts +1 -1
  42. package/src/internal/mutable/create-mutable-atom-family.ts +1 -1
  43. package/src/internal/mutable/create-mutable-atom.ts +1 -1
  44. package/src/internal/store/store.ts +3 -0
  45. package/src/react/store-context.tsx +2 -2
  46. package/src/react/use-i.ts +3 -3
  47. package/src/react/use-json.ts +2 -2
  48. package/src/react/use-loadable.ts +4 -4
  49. package/src/react/use-o.ts +4 -4
  50. package/src/react/use-tl.ts +6 -6
  51. package/src/realtime-testing/setup-realtime-test.tsx +5 -1
  52. package/src/transceivers/o-list/index.ts +1 -0
  53. package/src/transceivers/o-list/o-list-disposed-key-cleanup-effect.ts +153 -0
@@ -2,7 +2,7 @@ import type { MutableAtomFamilyToken, MutableAtomToken } from "atom.io"
2
2
  import type { AsJSON, Transceiver } from "atom.io/internal"
3
3
  import { findInStore, getJsonToken } from "atom.io/internal"
4
4
  import type { Canonical, Json } from "atom.io/json"
5
- import * as React from "react"
5
+ import { useContext } from "react"
6
6
 
7
7
  import { StoreContext } from "./store-context"
8
8
  import { useO } from "./use-o"
@@ -20,7 +20,7 @@ export function useJSON(
20
20
  token: MutableAtomFamilyToken<any, any> | MutableAtomToken<any>,
21
21
  key?: Canonical,
22
22
  ): Json.Serializable {
23
- const store = React.useContext(StoreContext)
23
+ const store = useContext(StoreContext)
24
24
  const stateToken: MutableAtomToken<any> =
25
25
  token.type === `mutable_atom_family` ? findInStore(store, token, key) : token
26
26
  const jsonToken = getJsonToken(store, stateToken)
@@ -3,7 +3,7 @@ import type { Loadable, ReadableFamilyToken, ReadableToken } from "atom.io"
3
3
  import { findInStore, type ReadableState, withdraw } from "atom.io/internal"
4
4
  import type { Canonical } from "atom.io/json"
5
5
  import { StoreContext, useO } from "atom.io/react"
6
- import React from "react"
6
+ import { useContext, useRef } from "react"
7
7
 
8
8
  export function useLoadable<T, E>(
9
9
  token: ReadableToken<Loadable<T>, any, E>,
@@ -32,7 +32,7 @@ export function useLoadable(
32
32
  | readonly [ReadableToken<any, any, any>, unknown]
33
33
  | readonly [ReadableToken<any, any, any>]
34
34
  ): `LOADING` | { loading: boolean; value: unknown; error?: unknown } {
35
- const store = React.useContext(StoreContext)
35
+ const store = useContext(StoreContext)
36
36
 
37
37
  let value: unknown
38
38
  let state: ReadableState<any, any>
@@ -65,12 +65,12 @@ export function useLoadable(
65
65
 
66
66
  const isErr = `catch` in state && state.catch.some((E) => value instanceof E)
67
67
 
68
- const wrapperRef = React.useRef<{
68
+ const wrapperRef = useRef<{
69
69
  loading: boolean
70
70
  value: unknown
71
71
  error?: unknown
72
72
  }>({ loading: false, value: null as unknown })
73
- const lastLoadedRef = React.useRef(
73
+ const lastLoadedRef = useRef(
74
74
  fallback ?? (value instanceof Promise ? `LOADING` : value),
75
75
  )
76
76
 
@@ -1,7 +1,7 @@
1
1
  import type { ReadableFamilyToken, ReadableToken } from "atom.io"
2
2
  import { getFromStore, subscribeToState } from "atom.io/internal"
3
3
  import type { Canonical } from "atom.io/json"
4
- import * as React from "react"
4
+ import { useContext, useId, useSyncExternalStore } from "react"
5
5
 
6
6
  import { parseStateOverloads } from "./parse-state-overloads"
7
7
  import { StoreContext } from "./store-context"
@@ -18,10 +18,10 @@ export function useO<T, K extends Canonical, E>(
18
18
  | [ReadableFamilyToken<T, K, E>, NoInfer<K>]
19
19
  | [ReadableToken<T, any, E>]
20
20
  ): E | T {
21
- const store = React.useContext(StoreContext)
21
+ const store = useContext(StoreContext)
22
22
  const token = parseStateOverloads(store, ...params)
23
- const id = React.useId()
24
- return React.useSyncExternalStore<E | T>(
23
+ const id = useId()
24
+ return useSyncExternalStore<E | T>(
25
25
  (dispatch) => subscribeToState(store, token, `use-o:${id}`, dispatch),
26
26
  () => getFromStore(store, token),
27
27
  () => getFromStore(store, token),
@@ -1,7 +1,7 @@
1
1
  import type { TimelineToken } from "atom.io"
2
2
  import { redo, undo } from "atom.io"
3
3
  import { subscribeToTimeline, withdraw } from "atom.io/internal"
4
- import * as React from "react"
4
+ import { useContext, useId, useRef, useSyncExternalStore } from "react"
5
5
 
6
6
  import { StoreContext } from "./store-context"
7
7
 
@@ -13,10 +13,10 @@ export type TimelineMeta = {
13
13
  }
14
14
 
15
15
  export function useTL(token: TimelineToken<any>): TimelineMeta {
16
- const store = React.useContext(StoreContext)
17
- const id = React.useId()
16
+ const store = useContext(StoreContext)
17
+ const id = useId()
18
18
  const timeline = withdraw(store, token)
19
- const tokenRef = React.useRef(token)
19
+ const tokenRef = useRef(token)
20
20
  const rebuildMeta = () => {
21
21
  return {
22
22
  at: timeline.at,
@@ -29,7 +29,7 @@ export function useTL(token: TimelineToken<any>): TimelineMeta {
29
29
  },
30
30
  }
31
31
  }
32
- const meta = React.useRef<TimelineMeta>(rebuildMeta())
32
+ const meta = useRef<TimelineMeta>(rebuildMeta())
33
33
  const retrieve = () => {
34
34
  if (
35
35
  meta.current.at !== timeline?.at ||
@@ -41,7 +41,7 @@ export function useTL(token: TimelineToken<any>): TimelineMeta {
41
41
  }
42
42
  return meta.current
43
43
  }
44
- return React.useSyncExternalStore<TimelineMeta>(
44
+ return useSyncExternalStore<TimelineMeta>(
45
45
  (dispatch) => subscribeToTimeline(store, token, `use-tl:${id}`, dispatch),
46
46
  retrieve,
47
47
  retrieve,
@@ -125,6 +125,7 @@ export const setupRealtimeTestServer = (
125
125
  {
126
126
  name: `SERVER-${testNumber}`,
127
127
  lifespan: options.immortal?.server ? `immortal` : `ephemeral`,
128
+ isProduction: false,
128
129
  },
129
130
  IMPLICIT.STORE,
130
131
  )
@@ -229,7 +230,10 @@ export const setupRealtimeTestClient = (
229
230
  const socket: ClientSocket = io(`http://localhost:${port}/`, {
230
231
  auth: { token: `test`, username: `${name}-${testNumber}` },
231
232
  })
232
- const silo = new AtomIO.Silo({ name, lifespan: `ephemeral` }, IMPLICIT.STORE)
233
+ const silo = new AtomIO.Silo(
234
+ { name, lifespan: `ephemeral`, isProduction: false },
235
+ IMPLICIT.STORE,
236
+ )
233
237
  silo.setState(RTC.myUsernameState, `${name}-${testNumber}`)
234
238
 
235
239
  const { document } = new Happy.Window()
@@ -1 +1,2 @@
1
1
  export * from "./o-list"
2
+ export * from "./o-list-disposed-key-cleanup-effect"
@@ -0,0 +1,153 @@
1
+ import type { AtomEffect } from "atom.io"
2
+ import { getFromStore, getUpdateToken, subscribeInStore } from "atom.io/internal"
3
+ import { type primitive, stringifyJson } from "atom.io/json"
4
+
5
+ import { OList } from "./o-list"
6
+
7
+ export function filterOutInPlace<T>(arr: T[], toRemove: T): T[] {
8
+ let writeIndex = 0
9
+
10
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
11
+ for (let readIndex = 0; readIndex < arr.length; readIndex++) {
12
+ if (toRemove !== arr[readIndex]) {
13
+ arr[writeIndex] = arr[readIndex]
14
+ writeIndex++
15
+ }
16
+ }
17
+
18
+ arr.length = writeIndex
19
+ return arr
20
+ }
21
+
22
+ export const oListDisposedKeyCleanupEffect: AtomEffect<OList<primitive>> = ({
23
+ token,
24
+ setSelf,
25
+ store,
26
+ }) => {
27
+ const disposalSubscriptions = new Map<primitive, () => void>()
28
+ const updateToken = getUpdateToken(token)
29
+
30
+ const addedValues = new Set<primitive>()
31
+ const removedValues = new Set<primitive>()
32
+ function updateSubscriptions() {
33
+ for (const addedValue of addedValues) {
34
+ const molecule = store.molecules.get(stringifyJson(addedValue))
35
+ if (molecule) {
36
+ disposalSubscriptions.set(
37
+ addedValue,
38
+ molecule.subject.subscribe(token.key, () => {
39
+ disposalSubscriptions.get(addedValue)?.()
40
+ disposalSubscriptions.delete(addedValue)
41
+ setSelf((self) => {
42
+ filterOutInPlace(self, addedValue)
43
+ return self
44
+ })
45
+ }),
46
+ )
47
+ } else {
48
+ store.logger.warn(
49
+ `❌`,
50
+ token.type,
51
+ token.key,
52
+ `Added "${addedValue}" to ${token.key} but it has not been allocated.`,
53
+ )
54
+ }
55
+ }
56
+ for (const removedValue of removedValues) {
57
+ if (disposalSubscriptions.has(removedValue)) {
58
+ disposalSubscriptions.get(removedValue)?.()
59
+ disposalSubscriptions.delete(removedValue)
60
+ }
61
+ }
62
+ }
63
+ subscribeInStore(
64
+ store,
65
+ updateToken,
66
+ function manageAutoDeletionTriggers({ newValue }) {
67
+ const currentList = getFromStore(store, token)
68
+ const unpacked = OList.unpackUpdate(newValue)
69
+ switch (unpacked.type) {
70
+ case `extend`:
71
+ case `reverse`:
72
+ case `sort`:
73
+ break // these don't change what values are present in the list
74
+
75
+ case `set`:
76
+ {
77
+ const { next } = unpacked
78
+ if (`prev` in unpacked && !currentList.includes(unpacked.prev)) {
79
+ removedValues.add(unpacked.prev)
80
+ }
81
+ if (!disposalSubscriptions.has(next)) {
82
+ addedValues.add(next)
83
+ }
84
+ }
85
+ break
86
+ case `truncate`:
87
+ {
88
+ for (const item of unpacked.items) {
89
+ if (!currentList.includes(item)) {
90
+ removedValues.add(item)
91
+ }
92
+ }
93
+ }
94
+ break
95
+ case `shift`:
96
+ case `pop`:
97
+ {
98
+ const { value } = unpacked
99
+ if (value !== undefined && !currentList.includes(value)) {
100
+ removedValues.add(value)
101
+ }
102
+ }
103
+ break
104
+ case `push`:
105
+ case `unshift`:
106
+ for (const item of unpacked.items) {
107
+ if (!disposalSubscriptions.has(item)) {
108
+ addedValues.add(item)
109
+ }
110
+ }
111
+ break
112
+ case `copyWithin`:
113
+ for (const item of unpacked.prev) {
114
+ if (!currentList.includes(item)) {
115
+ removedValues.add(item)
116
+ }
117
+ }
118
+ break
119
+ case `fill`:
120
+ {
121
+ const { value } = unpacked
122
+ if (value !== undefined) {
123
+ if (!disposalSubscriptions.has(value)) {
124
+ addedValues.add(value)
125
+ }
126
+ }
127
+ for (const item of unpacked.prev) {
128
+ if (!currentList.includes(item)) {
129
+ removedValues.add(item)
130
+ }
131
+ }
132
+ }
133
+ break
134
+ case `splice`:
135
+ for (const item of unpacked.deleted) {
136
+ if (!currentList.includes(item)) {
137
+ removedValues.add(item)
138
+ }
139
+ }
140
+ for (const addedItem of unpacked.items) {
141
+ if (!disposalSubscriptions.has(addedItem)) {
142
+ addedValues.add(addedItem)
143
+ }
144
+ }
145
+ break
146
+ }
147
+ updateSubscriptions()
148
+ addedValues.clear()
149
+ removedValues.clear()
150
+ },
151
+ `set-auto-deletion-triggers`,
152
+ )
153
+ }