atom.io 0.40.6 → 0.40.8

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 (186) hide show
  1. package/README.md +1 -1
  2. package/dist/data/index.d.ts +1 -1
  3. package/dist/data/index.js +1 -2
  4. package/dist/data/index.js.map +1 -1
  5. package/dist/employ-socket-D6wgByWh.js +12 -0
  6. package/dist/employ-socket-D6wgByWh.js.map +1 -0
  7. package/dist/eslint-plugin/index.js.map +1 -1
  8. package/dist/has-role-CMlaUlaf.js +1133 -0
  9. package/dist/has-role-CMlaUlaf.js.map +1 -0
  10. package/dist/internal/index.d.ts +248 -248
  11. package/dist/internal/index.d.ts.map +1 -1
  12. package/dist/internal/index.js +590 -1803
  13. package/dist/internal/index.js.map +1 -1
  14. package/dist/introspection/index.d.ts +1 -1
  15. package/dist/introspection/index.d.ts.map +1 -1
  16. package/dist/introspection/index.js +13 -32
  17. package/dist/introspection/index.js.map +1 -1
  18. package/dist/is-fn-DY1wZ-md.js +10 -0
  19. package/dist/is-fn-DY1wZ-md.js.map +1 -0
  20. package/dist/json/index.d.ts.map +1 -1
  21. package/dist/json/index.js.map +1 -1
  22. package/dist/main/index.d.ts +33 -33
  23. package/dist/main/index.d.ts.map +1 -1
  24. package/dist/main/index.js +3 -4
  25. package/dist/main/index.js.map +1 -1
  26. package/dist/mutex-store-CSvxY9i3.js +11 -0
  27. package/dist/mutex-store-CSvxY9i3.js.map +1 -0
  28. package/dist/react/index.d.ts +5 -5
  29. package/dist/react/index.d.ts.map +1 -1
  30. package/dist/react/index.js.map +1 -1
  31. package/dist/react-devtools/index.d.ts.map +1 -1
  32. package/dist/react-devtools/index.js +9 -11
  33. package/dist/react-devtools/index.js.map +1 -1
  34. package/dist/realtime/index.d.ts +7 -15
  35. package/dist/realtime/index.d.ts.map +1 -1
  36. package/dist/realtime/index.js +4 -35
  37. package/dist/realtime/index.js.map +1 -1
  38. package/dist/realtime-client/index.d.ts +6 -9
  39. package/dist/realtime-client/index.d.ts.map +1 -1
  40. package/dist/realtime-client/index.js +96 -88
  41. package/dist/realtime-client/index.js.map +1 -1
  42. package/dist/realtime-react/index.d.ts +17 -13
  43. package/dist/realtime-react/index.d.ts.map +1 -1
  44. package/dist/realtime-react/index.js +39 -50
  45. package/dist/realtime-react/index.js.map +1 -1
  46. package/dist/realtime-server/index.d.ts +83 -84
  47. package/dist/realtime-server/index.d.ts.map +1 -1
  48. package/dist/realtime-server/index.js +604 -543
  49. package/dist/realtime-server/index.js.map +1 -1
  50. package/dist/realtime-testing/index.d.ts +5 -4
  51. package/dist/realtime-testing/index.d.ts.map +1 -1
  52. package/dist/realtime-testing/index.js +35 -22
  53. package/dist/realtime-testing/index.js.map +1 -1
  54. package/dist/shared-room-store-BfW3nWif.js +31 -0
  55. package/dist/shared-room-store-BfW3nWif.js.map +1 -0
  56. package/dist/shared-room-store-D2o4ZLjC.d.ts +15 -0
  57. package/dist/shared-room-store-D2o4ZLjC.d.ts.map +1 -0
  58. package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
  59. package/dist/transceivers/set-rtx/index.js +4 -8
  60. package/dist/transceivers/set-rtx/index.js.map +1 -1
  61. package/dist/web/index.d.ts +3 -3
  62. package/dist/web/index.d.ts.map +1 -1
  63. package/dist/web/index.js +4 -3
  64. package/dist/web/index.js.map +1 -1
  65. package/package.json +13 -13
  66. package/src/internal/atom/create-regular-atom.ts +5 -4
  67. package/src/internal/atom/dispose-atom.ts +7 -2
  68. package/src/internal/atom/has-role.ts +3 -3
  69. package/src/internal/caching.ts +4 -2
  70. package/src/internal/families/create-readonly-held-selector-family.ts +2 -1
  71. package/src/internal/families/create-readonly-pure-selector-family.ts +5 -2
  72. package/src/internal/families/create-regular-atom-family.ts +2 -1
  73. package/src/internal/families/create-writable-held-selector-family.ts +2 -1
  74. package/src/internal/families/create-writable-pure-selector-family.ts +5 -2
  75. package/src/internal/families/dispose-from-store.ts +4 -4
  76. package/src/internal/families/find-in-store.ts +10 -10
  77. package/src/internal/families/get-family-of-token.ts +2 -2
  78. package/src/internal/families/index.ts +1 -0
  79. package/src/internal/families/mint-in-store.ts +54 -19
  80. package/src/internal/families/seek-in-store.ts +1 -1
  81. package/src/internal/get-state/get-fallback.ts +2 -2
  82. package/src/internal/get-state/get-from-store.ts +5 -5
  83. package/src/internal/get-state/read-or-compute-value.ts +1 -1
  84. package/src/internal/get-state/reduce-reference.ts +8 -6
  85. package/src/internal/index.ts +2 -220
  86. package/src/internal/molecule.ts +1 -2
  87. package/src/internal/mutable/create-mutable-atom-family.ts +3 -2
  88. package/src/internal/mutable/create-mutable-atom.ts +4 -2
  89. package/src/internal/mutable/get-json-family.ts +1 -1
  90. package/src/internal/mutable/get-update-family.ts +1 -1
  91. package/src/internal/mutable/tracker-family.ts +2 -1
  92. package/src/internal/mutable/tracker.ts +71 -59
  93. package/src/internal/safe-compute.ts +1 -1
  94. package/src/internal/selector/create-readonly-held-selector.ts +2 -1
  95. package/src/internal/selector/create-readonly-pure-selector.ts +2 -1
  96. package/src/internal/selector/create-writable-held-selector.ts +2 -1
  97. package/src/internal/selector/create-writable-pure-selector.ts +2 -1
  98. package/src/internal/selector/dispose-selector.ts +3 -2
  99. package/src/internal/selector/register-selector.ts +8 -5
  100. package/src/internal/selector/trace-selector-atoms.ts +2 -1
  101. package/src/internal/set-state/dispatch-state-update.ts +3 -2
  102. package/src/internal/set-state/evict-downstream.ts +1 -1
  103. package/src/internal/set-state/operate-on-store.ts +16 -22
  104. package/src/internal/set-state/reset-atom-or-selector.ts +5 -3
  105. package/src/internal/set-state/reset-in-store.ts +5 -5
  106. package/src/internal/set-state/set-atom-or-selector.ts +2 -2
  107. package/src/internal/set-state/set-atom.ts +4 -2
  108. package/src/internal/set-state/set-into-store.ts +21 -39
  109. package/src/internal/set-state/set-selector.ts +3 -2
  110. package/src/internal/state-types.ts +228 -0
  111. package/src/internal/store/deposit.ts +4 -4
  112. package/src/internal/store/index.ts +0 -1
  113. package/src/internal/store/store.ts +9 -9
  114. package/src/internal/store/withdraw.ts +4 -4
  115. package/src/internal/subscribe/recall-state.ts +1 -1
  116. package/src/internal/subscribe/subscribe-to-root-atoms.ts +1 -12
  117. package/src/internal/subscribe/subscribe-to-state.ts +9 -0
  118. package/src/internal/subscribe/subscribe-to-transaction.ts +3 -2
  119. package/src/internal/transaction/build-transaction.ts +3 -2
  120. package/src/internal/transaction/index.ts +1 -23
  121. package/src/internal/transaction/is-root-store.ts +4 -1
  122. package/src/internal/transaction/transaction-meta-progress.ts +22 -0
  123. package/src/main/atom.ts +1 -2
  124. package/src/main/find-state.ts +5 -5
  125. package/src/main/get-state.ts +4 -4
  126. package/src/main/realm.ts +2 -2
  127. package/src/main/set-state.ts +10 -10
  128. package/src/react/parse-state-overloads.ts +3 -3
  129. package/src/react/use-i.ts +6 -4
  130. package/src/react/use-loadable.ts +4 -10
  131. package/src/react/use-o.ts +6 -4
  132. package/src/react-devtools/store.ts +6 -6
  133. package/src/realtime/index.ts +1 -0
  134. package/src/realtime/mutex-store.ts +11 -0
  135. package/src/realtime/realtime-continuity.ts +1 -5
  136. package/src/realtime-client/index.ts +0 -1
  137. package/src/realtime-client/pull-atom-family-member.ts +14 -17
  138. package/src/realtime-client/pull-atom.ts +1 -1
  139. package/src/realtime-client/pull-mutable-atom-family-member.ts +16 -12
  140. package/src/realtime-client/pull-selector-family-member.ts +8 -35
  141. package/src/realtime-client/pull-selector-roots.ts +90 -0
  142. package/src/realtime-client/pull-selector.ts +2 -27
  143. package/src/realtime-client/push-state.ts +33 -5
  144. package/src/realtime-client/realtime-client-stores/client-main-store.ts +2 -5
  145. package/src/realtime-react/index.ts +2 -2
  146. package/src/realtime-react/realtime-context.tsx +9 -5
  147. package/src/realtime-react/use-pull-atom-family-member.ts +2 -3
  148. package/src/realtime-react/use-pull-mutable-family-member.ts +2 -3
  149. package/src/realtime-react/use-pull-selector-family-member.ts +5 -6
  150. package/src/realtime-react/use-push.ts +7 -3
  151. package/src/realtime-react/use-realtime-service.ts +11 -11
  152. package/src/realtime-react/use-single-effect.ts +11 -14
  153. package/src/realtime-server/{realtime-server-stores/server-sync-store.ts → continuity/continuity-store.ts} +2 -27
  154. package/src/realtime-server/continuity/provide-continuity.ts +50 -0
  155. package/src/realtime-server/continuity/{subscribe-to-continuity-actions.ts → provide-outcomes.ts} +15 -13
  156. package/src/realtime-server/continuity/{subscribe-to-continuity-perpectives.ts → provide-perspectives.ts} +10 -8
  157. package/src/realtime-server/continuity/{prepare-to-send-initial-payload.ts → provide-startup-payloads.ts} +6 -4
  158. package/src/realtime-server/continuity/receive-action-requests.ts +68 -0
  159. package/src/realtime-server/continuity/track-acknowledgements.ts +46 -0
  160. package/src/realtime-server/employ-socket.ts +14 -0
  161. package/src/realtime-server/index.ts +3 -22
  162. package/src/realtime-server/ipc-sockets/child-socket.ts +125 -66
  163. package/src/realtime-server/ipc-sockets/custom-socket.ts +16 -14
  164. package/src/realtime-server/ipc-sockets/parent-socket.ts +98 -69
  165. package/src/realtime-server/realtime-family-provider.ts +78 -29
  166. package/src/realtime-server/realtime-mutable-family-provider.ts +80 -31
  167. package/src/realtime-server/realtime-mutable-provider.ts +30 -22
  168. package/src/realtime-server/realtime-server-stores/index.ts +0 -2
  169. package/src/realtime-server/realtime-server-stores/server-room-external-store.ts +77 -36
  170. package/src/realtime-server/realtime-server-stores/server-user-store.ts +12 -1
  171. package/src/realtime-server/realtime-state-provider.ts +30 -29
  172. package/src/realtime-server/realtime-state-receiver.ts +62 -16
  173. package/src/realtime-server/server-config.ts +8 -0
  174. package/src/realtime-server/socket-interface.ts +14 -0
  175. package/src/realtime-testing/setup-realtime-test.tsx +70 -31
  176. package/src/web/index.ts +1 -1
  177. package/src/web/{persist-sync.ts → storage-sync.ts} +5 -2
  178. package/src/internal/store/mint-or-counterfeit.ts +0 -108
  179. package/src/realtime-client/server-action.ts +0 -23
  180. package/src/realtime-react/on-mount.ts +0 -5
  181. package/src/realtime-react/use-server-action.ts +0 -19
  182. package/src/realtime-server/continuity/prepare-to-serve-transaction-request.ts +0 -59
  183. package/src/realtime-server/continuity/prepare-to-sync-realtime-continuity.ts +0 -145
  184. package/src/realtime-server/continuity/prepare-to-track-client-acknowledgement.ts +0 -41
  185. package/src/realtime-server/realtime-action-receiver.ts +0 -40
  186. package/src/realtime-server/realtime-server-stores/server-room-external-actions.ts +0 -79
@@ -0,0 +1,90 @@
1
+ import type { AtomToken, SelectorToken } from "atom.io"
2
+ import type { Store } from "atom.io/internal"
3
+ import { getFamilyOfToken, subscribeToState } from "atom.io/internal"
4
+ import { parseJson } from "atom.io/json"
5
+ import type { Socket } from "socket.io-client"
6
+
7
+ import { pullAtom } from "./pull-atom"
8
+ import { pullAtomFamilyMember } from "./pull-atom-family-member"
9
+ import { pullMutableAtom } from "./pull-mutable-atom"
10
+ import { pullMutableAtomFamilyMember } from "./pull-mutable-atom-family-member"
11
+
12
+ export function pullSelectorRoots(
13
+ store: Store,
14
+ socket: Socket,
15
+ selectorToken: SelectorToken<any>,
16
+ ): () => void {
17
+ const atomSubscriptions = new Map<string, () => void>()
18
+ const clearAtomSubscriptions = () => {
19
+ for (const [, unsub] of atomSubscriptions) unsub()
20
+ atomSubscriptions.clear()
21
+ }
22
+
23
+ const start = () => {
24
+ const atomKeys = store.selectorAtoms.getRelatedKeys(selectorToken.key)
25
+ if (atomKeys) {
26
+ for (const [atomKey, unsub] of atomSubscriptions) {
27
+ if (!atomKeys.has(atomKey)) {
28
+ unsub()
29
+ atomSubscriptions.delete(atomKey)
30
+ }
31
+ }
32
+
33
+ for (const atomKey of atomKeys) {
34
+ if (atomSubscriptions.has(atomKey)) {
35
+ continue
36
+ }
37
+ const atom = store.atoms.get(atomKey) as AtomToken<any, any>
38
+ switch (atom.type) {
39
+ case `atom`: {
40
+ if (atom.family) {
41
+ const { subKey: serializedSubKey } = atom.family
42
+ const subKey = parseJson(serializedSubKey)
43
+ const family = getFamilyOfToken(store, atom)
44
+ atomSubscriptions.set(
45
+ atomKey,
46
+ pullAtomFamilyMember(store, socket, family, subKey),
47
+ )
48
+ } else {
49
+ atomSubscriptions.set(atomKey, pullAtom(store, socket, atom))
50
+ }
51
+ break
52
+ }
53
+ case `mutable_atom`: {
54
+ if (atom.family) {
55
+ const { subKey: serializedSubKey } = atom.family
56
+ const subKey = parseJson(serializedSubKey)
57
+ const family = getFamilyOfToken(store, atom)
58
+ atomSubscriptions.set(
59
+ atomKey,
60
+ pullMutableAtomFamilyMember(store, socket, family, subKey),
61
+ )
62
+ } else {
63
+ atomSubscriptions.set(
64
+ atomKey,
65
+ pullMutableAtom(store, socket, atom),
66
+ )
67
+ }
68
+ break
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ const unsubFromSelector = subscribeToState(
76
+ store,
77
+ selectorToken,
78
+ `pull-watches-dependencies`,
79
+ () => {
80
+ start()
81
+ },
82
+ )
83
+
84
+ start()
85
+
86
+ return () => {
87
+ clearAtomSubscriptions()
88
+ unsubFromSelector()
89
+ }
90
+ }
@@ -2,37 +2,12 @@ import type * as AtomIO from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
3
  import type { Socket } from "socket.io-client"
4
4
 
5
- import { pullAtom } from "./pull-atom"
6
- import { pullMutableAtom } from "./pull-mutable-atom"
5
+ import { pullSelectorRoots } from "./pull-selector-roots"
7
6
 
8
7
  export function pullSelector<T>(
9
8
  store: Store,
10
9
  socket: Socket,
11
10
  token: AtomIO.SelectorToken<T>,
12
11
  ): () => void {
13
- const atomKeys = store.selectorAtoms.getRelatedKeys(token.key)
14
- const unsubscribes: Array<() => void> = []
15
- if (atomKeys) {
16
- for (const atomKey of atomKeys) {
17
- const atom = store.atoms.get(atomKey)
18
- if (!atom) {
19
- continue
20
- }
21
- switch (atom.type) {
22
- case `atom`: {
23
- unsubscribes.push(pullAtom(store, socket, atom))
24
- break
25
- }
26
- case `mutable_atom`: {
27
- unsubscribes.push(pullMutableAtom(store, socket, atom))
28
- break
29
- }
30
- }
31
- }
32
- }
33
- return () => {
34
- for (const unsubscribe of unsubscribes) {
35
- unsubscribe()
36
- }
37
- }
12
+ return pullSelectorRoots(store, socket, token)
38
13
  }
@@ -1,7 +1,9 @@
1
1
  import type { WritableToken } from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
- import { subscribeToState } from "atom.io/internal"
3
+ import { setIntoStore, subscribeToState } from "atom.io/internal"
4
4
  import type { Json } from "atom.io/json"
5
+ import { mutexAtoms } from "atom.io/realtime/mutex-store"
6
+ import { employSocket } from "atom.io/realtime-server/employ-socket"
5
7
  import type { Socket } from "socket.io-client"
6
8
 
7
9
  export function pushState<J extends Json.Serializable>(
@@ -9,12 +11,38 @@ export function pushState<J extends Json.Serializable>(
9
11
  socket: Socket,
10
12
  token: WritableToken<J>,
11
13
  ): () => void {
12
- socket.emit(`claim:${token.key}`)
13
- subscribeToState(store, token, `push`, ({ newValue }) => {
14
+ const publish = (newValue: J) => {
14
15
  socket.emit(`pub:${token.key}`, newValue)
15
- })
16
+ }
17
+
18
+ const subscriptions = new Set<() => void>()
19
+ const clearSubscriptions = () => {
20
+ for (const unsub of subscriptions) unsub()
21
+ subscriptions.clear()
22
+ }
23
+
24
+ const init = () => {
25
+ subscriptions.add(
26
+ employSocket(socket, `claim-result:${token.key}`, (success: boolean) => {
27
+ if (!success) return
28
+
29
+ clearSubscriptions()
30
+ setIntoStore(store, mutexAtoms, token.key, true)
31
+ subscriptions.add(
32
+ subscribeToState(store, token, `push`, ({ newValue }) => {
33
+ publish(newValue)
34
+ }),
35
+ )
36
+ }),
37
+ )
38
+
39
+ socket.emit(`claim:${token.key}`)
40
+ }
41
+
42
+ init()
43
+
16
44
  return () => {
17
- socket.off(`pub:${token.key}`)
45
+ clearSubscriptions()
18
46
  socket.emit(`unclaim:${token.key}`)
19
47
  }
20
48
  }
@@ -1,5 +1,5 @@
1
1
  import * as AtomIO from "atom.io"
2
- import { persistSync } from "atom.io/web"
2
+ import { storageSync } from "atom.io/web"
3
3
 
4
4
  export const myIdState__INTERNAL: AtomIO.RegularAtomToken<string | undefined> =
5
5
  AtomIO.atom<string | undefined>({
@@ -16,8 +16,5 @@ export const myUsernameState: AtomIO.RegularAtomToken<string | null> =
16
16
  AtomIO.atom<string | null>({
17
17
  key: `myName`,
18
18
  default: null,
19
- effects:
20
- typeof window === `undefined`
21
- ? []
22
- : [persistSync(window.localStorage, JSON, `myUsername`)],
19
+ effects: [storageSync(globalThis.localStorage, JSON, `myUsername`)],
23
20
  })
@@ -1,4 +1,3 @@
1
- export * from "./on-mount"
2
1
  export * from "./realtime-context"
3
2
  export * from "./use-pull-atom"
4
3
  export * from "./use-pull-atom-family-member"
@@ -7,5 +6,6 @@ export * from "./use-pull-mutable-family-member"
7
6
  export * from "./use-pull-selector"
8
7
  export * from "./use-pull-selector-family-member"
9
8
  export * from "./use-push"
10
- export * from "./use-server-action"
9
+ export * from "./use-realtime-service"
10
+ export * from "./use-single-effect"
11
11
  export * from "./use-sync-continuity"
@@ -3,12 +3,14 @@ import * as RTC from "atom.io/realtime-client"
3
3
  import * as React from "react"
4
4
  import type { Socket } from "socket.io-client"
5
5
 
6
+ export type RealtimeServiceCounter = {
7
+ consumerCount: number
8
+ dispose: () => void
9
+ }
10
+
6
11
  export type RealtimeReactStore = {
7
12
  socket: Socket | null
8
- services: Map<
9
- string,
10
- [consumerCount: number, dispose: (() => void) | undefined]
11
- > | null
13
+ services: Map<string, RealtimeServiceCounter> | null
12
14
  }
13
15
 
14
16
  export const RealtimeContext: React.Context<RealtimeReactStore> =
@@ -21,7 +23,9 @@ export const RealtimeProvider: React.FC<{
21
23
  children: React.ReactNode
22
24
  socket: Socket | null
23
25
  }> = ({ children, socket }) => {
24
- const services = React.useRef(new Map<string, [number, () => void]>()).current
26
+ const services = React.useRef(
27
+ new Map<string, RealtimeServiceCounter>(),
28
+ ).current
25
29
  const setMyId = useI(RTC.myIdState__INTERNAL)
26
30
  React.useEffect(() => {
27
31
  setMyId(socket?.id)
@@ -10,12 +10,11 @@ import { useRealtimeService } from "./use-realtime-service"
10
10
  export function usePullAtomFamilyMember<
11
11
  J extends Json.Serializable,
12
12
  K extends Canonical,
13
- Key extends K,
14
- >(family: AtomIO.RegularAtomFamilyToken<J, K>, subKey: Key): J {
13
+ >(family: AtomIO.RegularAtomFamilyToken<J, K>, subKey: NoInfer<K>): J {
15
14
  const store = React.useContext(StoreContext)
16
15
  const token = findInStore(store, family, subKey)
17
16
  useRealtimeService(`pull:${token.key}`, (socket) =>
18
- RTC.pullAtomFamilyMember(store, socket, token),
17
+ RTC.pullAtomFamilyMember(store, socket, family, subKey),
19
18
  )
20
19
  return useO(token)
21
20
  }
@@ -11,12 +11,11 @@ import { useRealtimeService } from "./use-realtime-service"
11
11
  export function usePullMutableAtomFamilyMember<
12
12
  T extends Transceiver<any, any, any>,
13
13
  K extends Canonical,
14
- Key extends K,
15
- >(familyToken: AtomIO.MutableAtomFamilyToken<T, K>, key: Key): T {
14
+ >(familyToken: AtomIO.MutableAtomFamilyToken<T, K>, key: NoInfer<K>): T {
16
15
  const store = React.useContext(StoreContext)
17
16
  const token = findInStore(store, familyToken, key)
18
17
  useRealtimeService(`pull:${token.key}`, (socket) =>
19
- RTC.pullMutableAtomFamilyMember(store, socket, token),
18
+ RTC.pullMutableAtomFamilyMember(store, socket, familyToken, key),
20
19
  )
21
20
  return useO(token)
22
21
  }
@@ -7,15 +7,14 @@ import * as React from "react"
7
7
 
8
8
  import { useRealtimeService } from "./use-realtime-service"
9
9
 
10
- export function usePullSelectorFamilyMember<
11
- T,
12
- K extends Canonical,
13
- Key extends K,
14
- >(familyToken: AtomIO.SelectorFamilyToken<T, K>, key: Key): T {
10
+ export function usePullSelectorFamilyMember<T, K extends Canonical>(
11
+ familyToken: AtomIO.SelectorFamilyToken<T, K>,
12
+ key: NoInfer<K>,
13
+ ): T {
15
14
  const store = React.useContext(StoreContext)
16
15
  const token = findInStore(store, familyToken, key)
17
16
  useRealtimeService(`pull:${token.key}`, (socket) =>
18
- RTC.pullSelectorFamilyMember(store, socket, token),
17
+ RTC.pullSelectorFamilyMember(store, socket, familyToken, key),
19
18
  )
20
19
 
21
20
  return useO(token)
@@ -1,6 +1,7 @@
1
1
  import type * as AtomIO from "atom.io"
2
2
  import type { Json } from "atom.io/json"
3
- import { StoreContext, useI } from "atom.io/react"
3
+ import { StoreContext, useI, useO } from "atom.io/react"
4
+ import * as RT from "atom.io/realtime"
4
5
  import * as RTC from "atom.io/realtime-client"
5
6
  import * as React from "react"
6
7
 
@@ -8,10 +9,13 @@ import { useRealtimeService } from "./use-realtime-service"
8
9
 
9
10
  export function usePush<J extends Json.Serializable>(
10
11
  token: AtomIO.WritableToken<J>,
11
- ): <New extends J>(next: New | ((old: J) => New)) => void {
12
+ ): (<New extends J>(next: New | ((old: J) => New)) => void) | null {
12
13
  const store = React.useContext(StoreContext)
13
14
  useRealtimeService(`push:${token.key}`, (socket) =>
14
15
  RTC.pushState(store, socket, token),
15
16
  )
16
- return useI(token)
17
+ const mutex = useO(RT.mutexAtoms, token.key)
18
+ const setter = useI(token)
19
+
20
+ return mutex ? setter : null
17
21
  }
@@ -1,31 +1,31 @@
1
1
  import * as React from "react"
2
2
  import type { Socket } from "socket.io-client"
3
3
 
4
- import { onMount } from "./on-mount"
5
4
  import { RealtimeContext } from "./realtime-context"
5
+ import { useSingleEffect } from "./use-single-effect"
6
6
 
7
7
  export function useRealtimeService(
8
8
  key: string,
9
- create: (socket: Socket) => (() => void) | undefined,
9
+ create: (socket: Socket) => () => void,
10
10
  ): void {
11
11
  const { socket, services } = React.useContext(RealtimeContext)
12
- onMount(() => {
12
+ useSingleEffect(() => {
13
13
  let service = services?.get(key)
14
14
  if (service) {
15
- service[0]++
16
- } else {
17
- const dispose = socket ? create(socket) : undefined
18
- service = [1, dispose]
15
+ ++service.consumerCount
16
+ } else if (socket) {
17
+ const dispose = create(socket)
18
+ service = { consumerCount: 1, dispose }
19
19
  services?.set(key, service)
20
20
  }
21
21
  return () => {
22
22
  if (service) {
23
- service[0]--
24
- if (service[0] === 0) {
25
- service[1]?.()
23
+ --service.consumerCount
24
+ if (service.consumerCount === 0) {
25
+ service.dispose?.()
26
26
  services?.delete(key)
27
27
  }
28
28
  }
29
29
  }
30
- })
30
+ }, [socket, key])
31
31
  }
@@ -1,28 +1,25 @@
1
1
  /** biome-ignore-all lint/correctness/useHookAtTopLevel: intentional */
2
2
 
3
+ import { isFn } from "atom.io/internal/is-fn"
3
4
  import * as React from "react"
4
5
 
5
- // @ts-expect-error this is a safe way to check a property on the global object
6
- const { NODE_ENV } = globalThis[`env`] ?? {}
7
- const IN_DEV = NODE_ENV === `development`
8
-
9
- function noop() {}
10
-
11
6
  export function useSingleEffect(
12
7
  effect: () => (() => void) | undefined | void,
13
8
  deps: unknown[],
14
9
  ): void {
15
- if (IN_DEV) {
16
- const cleanup = React.useRef<() => void>(noop)
10
+ const globalEnv = (globalThis as unknown as { env: any })[`env`]
11
+ const isInDev = globalEnv?.NODE_ENV === `development`
12
+ if (isInDev) {
13
+ const cleanupRef = React.useRef<boolean | (() => void)>(false)
17
14
  React.useEffect(() => {
18
- let dispose = cleanup.current
19
- if (dispose === noop) {
20
- dispose = effect() ?? noop
21
- cleanup.current = dispose
15
+ let cleanupFn = cleanupRef.current
16
+ if (cleanupFn === false) {
17
+ cleanupFn = effect() ?? true
18
+ cleanupRef.current = cleanupFn
22
19
  } else {
23
20
  return () => {
24
- dispose()
25
- cleanup.current = noop
21
+ if (isFn(cleanupFn)) cleanupFn()
22
+ cleanupRef.current = false
26
23
  }
27
24
  }
28
25
  }, deps)
@@ -5,15 +5,7 @@ import type {
5
5
  } from "atom.io"
6
6
  import { atomFamily } from "atom.io"
7
7
 
8
- import type { UserKey } from "./server-user-store"
9
-
10
- // export const completeUpdateAtoms = atomFamily<
11
- // TransactionUpdate<any> | null,
12
- // string
13
- // >({
14
- // key: `completeUpdate`,
15
- // default: null,
16
- // })
8
+ import type { UserKey } from "../realtime-server-stores/server-user-store"
17
9
 
18
10
  export function redactTransactionUpdateContent(
19
11
  visibleStateKeys: string[],
@@ -67,29 +59,12 @@ export const redactorAtoms: RegularAtomFamilyToken<
67
59
  key: `redactor`,
68
60
  default: { occlude: (updates) => updates },
69
61
  })
70
- // export const redactedUpdateSelectors = selectorFamily<
71
- // TransactionUpdate<any> | null,
72
- // [transactionKey: string, updateId: string]
73
- // >({
74
- // key: `redactedUpdate`,
75
- // get:
76
- // ([transactionKey, updateId]) =>
77
- // ({ get, find }) => {
78
- // const update = get(find(completeUpdateAtoms, updateId))
79
- // const { filter } = get(find(transactionRedactorAtoms, transactionKey))
80
-
81
- // if (update && filter) {
82
- // return { ...update, updates: filter(update.updates) }
83
- // }
84
- // return null
85
- // },
86
- // })
87
62
 
88
63
  export type ContinuitySyncTransactionUpdate = Pick<
89
64
  TransactionOutcomeEvent<any>,
90
65
  `epoch` | `id` | `output` | `subEvents` | `token`
91
66
  >
92
- export const userUnacknowledgedQueues: RegularAtomFamilyToken<
67
+ export const unacknowledgedUpdatesAtoms: RegularAtomFamilyToken<
93
68
  ContinuitySyncTransactionUpdate[],
94
69
  UserKey
95
70
  > = atomFamily<ContinuitySyncTransactionUpdate[], UserKey>({
@@ -0,0 +1,50 @@
1
+ import { getFromStore, IMPLICIT } from "atom.io/internal"
2
+ import type { Json } from "atom.io/json"
3
+ import type { ContinuityToken } from "atom.io/realtime"
4
+
5
+ import type { ServerConfig, UserKey } from ".."
6
+ import { unacknowledgedUpdatesAtoms } from "./continuity-store"
7
+ import { provideOutcomes } from "./provide-outcomes"
8
+ import { providePerspectives } from "./provide-perspectives"
9
+ import { provideStartupPayloads } from "./provide-startup-payloads"
10
+ import { receiveActionRequests } from "./receive-action-requests"
11
+ import { trackAcknowledgements } from "./track-acknowledgements"
12
+
13
+ export type ProvideContinuity = (
14
+ continuity: ContinuityToken,
15
+ userKey: UserKey,
16
+ ) => () => void
17
+ export function prepareToProvideContinuity({
18
+ socket,
19
+ store = IMPLICIT.STORE,
20
+ }: ServerConfig): ProvideContinuity {
21
+ return function syncRealtimeContinuity(continuity, userKey) {
22
+ const continuityKey = continuity.key
23
+
24
+ const unacknowledgedUpdates = getFromStore(
25
+ store,
26
+ unacknowledgedUpdatesAtoms,
27
+ userKey,
28
+ )
29
+ for (const unacknowledgedUpdate of unacknowledgedUpdates) {
30
+ socket.emit(
31
+ `tx-new:${continuityKey}`,
32
+ unacknowledgedUpdate as Json.Serializable,
33
+ )
34
+ }
35
+
36
+ const subscriptions = new Set<() => void>()
37
+ const clearSubscriptions = () => {
38
+ for (const unsubscribe of subscriptions) unsubscribe()
39
+ subscriptions.clear()
40
+ }
41
+
42
+ subscriptions.add(providePerspectives(store, socket, continuity, userKey))
43
+ subscriptions.add(provideOutcomes(store, socket, continuity, userKey))
44
+ subscriptions.add(provideStartupPayloads(store, socket, continuity, userKey))
45
+ subscriptions.add(receiveActionRequests(store, socket, continuity, userKey))
46
+ subscriptions.add(trackAcknowledgements(store, socket, continuity, userKey))
47
+
48
+ return clearSubscriptions
49
+ }
50
+ }
@@ -12,24 +12,24 @@ import type { ContinuityToken } from "atom.io/realtime"
12
12
  import type { Socket, UserKey } from ".."
13
13
  import {
14
14
  redactTransactionUpdateContent,
15
- userUnacknowledgedQueues,
16
- } from "../realtime-server-stores"
15
+ unacknowledgedUpdatesAtoms,
16
+ } from "./continuity-store"
17
17
 
18
- export function subscribeToContinuityActions(
18
+ export function provideOutcomes(
19
19
  store: Store,
20
+ socket: Socket,
20
21
  continuity: ContinuityToken,
21
22
  userKey: UserKey,
22
- socket: Socket | null,
23
- ): (() => void)[] {
23
+ ): () => void {
24
24
  const continuityKey = continuity.key
25
- const unsubscribeFunctions: (() => void)[] = []
25
+ const unsubscribeFunctions = new Set<() => void>()
26
26
 
27
27
  for (const transaction of continuity.actions) {
28
28
  const unsubscribeFromTransaction = subscribeToTransaction(
29
29
  store,
30
30
  transaction,
31
31
  `sync-continuity:${continuityKey}:${userKey}`,
32
- (update) => {
32
+ (outcomes) => {
33
33
  try {
34
34
  const visibleKeys = continuity.globals
35
35
  .map((atom) => {
@@ -59,13 +59,13 @@ export function subscribeToContinuityActions(
59
59
  )
60
60
  const redactedUpdates = redactTransactionUpdateContent(
61
61
  visibleKeys,
62
- update.subEvents,
62
+ outcomes.subEvents,
63
63
  )
64
64
  const redactedUpdate = {
65
- ...update,
65
+ ...outcomes,
66
66
  updates: redactedUpdates,
67
67
  }
68
- setIntoStore(store, userUnacknowledgedQueues, userKey, (updates) => {
68
+ setIntoStore(store, unacknowledgedUpdatesAtoms, userKey, (updates) => {
69
69
  if (redactedUpdate) {
70
70
  updates.push(redactedUpdate)
71
71
  updates.sort((a, b) => a.epoch - b.epoch)
@@ -81,7 +81,7 @@ export function subscribeToContinuityActions(
81
81
  return updates
82
82
  })
83
83
 
84
- socket?.emit(
84
+ socket.emit(
85
85
  `tx-new:${continuityKey}`,
86
86
  redactedUpdate as Json.Serializable,
87
87
  )
@@ -98,7 +98,9 @@ export function subscribeToContinuityActions(
98
98
  }
99
99
  },
100
100
  )
101
- unsubscribeFunctions.push(unsubscribeFromTransaction)
101
+ unsubscribeFunctions.add(unsubscribeFromTransaction)
102
+ }
103
+ return () => {
104
+ for (const unsubscribe of unsubscribeFunctions) unsubscribe()
102
105
  }
103
- return unsubscribeFunctions
104
106
  }
@@ -10,14 +10,14 @@ import type { ContinuityToken } from "atom.io/realtime"
10
10
  import type { Socket } from ".."
11
11
  import type { UserKey } from "../realtime-server-stores"
12
12
 
13
- export function subscribeToContinuityPerspectives(
13
+ export function providePerspectives(
14
14
  store: Store,
15
+ socket: Socket,
15
16
  continuity: ContinuityToken,
16
17
  userKey: UserKey,
17
- socket: Socket | null,
18
- ): (() => void)[] {
18
+ ): () => void {
19
19
  const continuityKey = continuity.key
20
- const unsubFns: (() => void)[] = []
20
+ const unsubFns = new Set<() => void>()
21
21
  for (const perspective of continuity.perspectives) {
22
22
  const { viewAtoms } = perspective
23
23
  const userViewState = findInStore(store, viewAtoms, userKey)
@@ -47,14 +47,16 @@ export function subscribeToContinuityPerspectives(
47
47
  { oldKeys, newKeys, revealed, concealed },
48
48
  )
49
49
  if (revealed.length > 0) {
50
- socket?.emit(`reveal:${continuityKey}`, revealed)
50
+ socket.emit(`reveal:${continuityKey}`, revealed)
51
51
  }
52
52
  if (concealed && concealed.length > 0) {
53
- socket?.emit(`conceal:${continuityKey}`, concealed)
53
+ socket.emit(`conceal:${continuityKey}`, concealed)
54
54
  }
55
55
  },
56
56
  )
57
- unsubFns.push(unsubscribeFromUserView)
57
+ unsubFns.add(unsubscribeFromUserView)
58
+ }
59
+ return () => {
60
+ for (const unsubscribe of unsubFns) unsubscribe()
58
61
  }
59
- return unsubFns
60
62
  }
@@ -9,15 +9,16 @@ import type { Json } from "atom.io/json"
9
9
  import type { ContinuityToken } from "atom.io/realtime"
10
10
 
11
11
  import type { Socket, UserKey } from ".."
12
+ import { employSocket } from "../employ-socket"
12
13
 
13
- export function prepareToSendInitialPayload(
14
+ export function provideStartupPayloads(
14
15
  store: Store,
16
+ socket: Socket,
15
17
  continuity: ContinuityToken,
16
18
  userKey: UserKey,
17
- socket: Socket | null,
18
19
  ): () => void {
19
20
  const continuityKey = continuity.key
20
- return function sendInitialPayload(): void {
21
+ function sendInitialPayload(): void {
21
22
  const initialPayload: Json.Serializable[] = []
22
23
  for (const atom of continuity.globals) {
23
24
  const resourceToken =
@@ -49,6 +50,7 @@ export function prepareToSendInitialPayload(
49
50
  ? (store.transactionMeta.epoch.get(continuityKey) ?? null)
50
51
  : null
51
52
 
52
- socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
53
+ socket.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
53
54
  }
55
+ return employSocket(socket, `get:${continuityKey}`, sendInitialPayload)
54
56
  }