@oxyhq/services 13.3.0 → 14.0.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 (251) hide show
  1. package/lib/commonjs/index.js +11 -17
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/AccountMenu.js +103 -65
  4. package/lib/commonjs/ui/components/AccountMenu.js.map +1 -1
  5. package/lib/commonjs/ui/components/AccountSwitcher.js +84 -126
  6. package/lib/commonjs/ui/components/AccountSwitcher.js.map +1 -1
  7. package/lib/commonjs/ui/components/ProfileMenu.js +140 -153
  8. package/lib/commonjs/ui/components/ProfileMenu.js.map +1 -1
  9. package/lib/commonjs/ui/components/SignInModal.js +4 -4
  10. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  11. package/lib/commonjs/ui/context/OxyContext.js +506 -466
  12. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  13. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +59 -57
  14. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  15. package/lib/commonjs/ui/context/inSessionTokenRefresh.js +23 -42
  16. package/lib/commonjs/ui/context/inSessionTokenRefresh.js.map +1 -1
  17. package/lib/commonjs/ui/context/silentSessionRestore.js +12 -21
  18. package/lib/commonjs/ui/context/silentSessionRestore.js.map +1 -1
  19. package/lib/commonjs/ui/hooks/useAuth.js +4 -3
  20. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  21. package/lib/commonjs/ui/hooks/useOxyAuthSession.js +12 -7
  22. package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +1 -1
  23. package/lib/commonjs/ui/hooks/useSessionManagement.js +17 -65
  24. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  25. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +288 -0
  26. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -0
  27. package/lib/commonjs/ui/hooks/useWebSSO.js +12 -191
  28. package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -1
  29. package/lib/commonjs/ui/index.js.map +1 -1
  30. package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -2
  31. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  32. package/lib/commonjs/ui/session/createSessionClient.js +28 -0
  33. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -0
  34. package/lib/commonjs/ui/session/index.js +52 -0
  35. package/lib/commonjs/ui/session/index.js.map +1 -0
  36. package/lib/commonjs/ui/session/projectSessionState.js +86 -0
  37. package/lib/commonjs/ui/session/projectSessionState.js.map +1 -0
  38. package/lib/commonjs/ui/session/sessionClientHost.js +30 -0
  39. package/lib/commonjs/ui/session/sessionClientHost.js.map +1 -0
  40. package/lib/commonjs/ui/session/tokenTransport.js +77 -0
  41. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -0
  42. package/lib/commonjs/ui/utils/activeAuthuser.js +35 -79
  43. package/lib/commonjs/ui/utils/activeAuthuser.js.map +1 -1
  44. package/lib/commonjs/ui/utils/sessionHelpers.js +1 -51
  45. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  46. package/lib/commonjs/utils/deviceFlowSignIn.js +70 -15
  47. package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
  48. package/lib/module/index.js +5 -5
  49. package/lib/module/index.js.map +1 -1
  50. package/lib/module/ui/components/AccountMenu.js +103 -65
  51. package/lib/module/ui/components/AccountMenu.js.map +1 -1
  52. package/lib/module/ui/components/AccountSwitcher.js +85 -127
  53. package/lib/module/ui/components/AccountSwitcher.js.map +1 -1
  54. package/lib/module/ui/components/ProfileMenu.js +143 -156
  55. package/lib/module/ui/components/ProfileMenu.js.map +1 -1
  56. package/lib/module/ui/components/SignInModal.js +4 -4
  57. package/lib/module/ui/components/SignInModal.js.map +1 -1
  58. package/lib/module/ui/context/OxyContext.js +510 -470
  59. package/lib/module/ui/context/OxyContext.js.map +1 -1
  60. package/lib/module/ui/context/hooks/useAuthOperations.js +61 -60
  61. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  62. package/lib/module/ui/context/inSessionTokenRefresh.js +24 -43
  63. package/lib/module/ui/context/inSessionTokenRefresh.js.map +1 -1
  64. package/lib/module/ui/context/silentSessionRestore.js +12 -20
  65. package/lib/module/ui/context/silentSessionRestore.js.map +1 -1
  66. package/lib/module/ui/hooks/useAuth.js +4 -3
  67. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  68. package/lib/module/ui/hooks/useOxyAuthSession.js +12 -7
  69. package/lib/module/ui/hooks/useOxyAuthSession.js.map +1 -1
  70. package/lib/module/ui/hooks/useSessionManagement.js +18 -66
  71. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  72. package/lib/module/ui/hooks/useSwitchableAccounts.js +283 -0
  73. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -0
  74. package/lib/module/ui/hooks/useWebSSO.js +13 -192
  75. package/lib/module/ui/hooks/useWebSSO.js.map +1 -1
  76. package/lib/module/ui/index.js +0 -1
  77. package/lib/module/ui/index.js.map +1 -1
  78. package/lib/module/ui/screens/OxyAuthScreen.js +2 -2
  79. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  80. package/lib/module/ui/session/createSessionClient.js +25 -0
  81. package/lib/module/ui/session/createSessionClient.js.map +1 -0
  82. package/lib/module/ui/session/index.js +17 -0
  83. package/lib/module/ui/session/index.js.map +1 -0
  84. package/lib/module/ui/session/projectSessionState.js +79 -0
  85. package/lib/module/ui/session/projectSessionState.js.map +1 -0
  86. package/lib/module/ui/session/sessionClientHost.js +26 -0
  87. package/lib/module/ui/session/sessionClientHost.js.map +1 -0
  88. package/lib/module/ui/session/tokenTransport.js +74 -0
  89. package/lib/module/ui/session/tokenTransport.js.map +1 -0
  90. package/lib/module/ui/utils/activeAuthuser.js +34 -76
  91. package/lib/module/ui/utils/activeAuthuser.js.map +1 -1
  92. package/lib/module/ui/utils/sessionHelpers.js +0 -49
  93. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  94. package/lib/module/utils/deviceFlowSignIn.js +71 -16
  95. package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
  96. package/lib/typescript/commonjs/index.d.ts +2 -3
  97. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +4 -2
  99. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +7 -8
  101. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +12 -9
  103. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +16 -13
  105. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +27 -3
  107. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts +22 -17
  109. package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts +12 -18
  111. package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
  113. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +1 -1
  115. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +2 -2
  117. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +162 -0
  120. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +13 -52
  122. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +15 -0
  125. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -0
  126. package/lib/typescript/commonjs/ui/session/index.d.ts +15 -0
  127. package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts +45 -0
  129. package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts +14 -0
  131. package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +24 -0
  133. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -0
  134. package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts +21 -29
  135. package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +0 -19
  137. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +51 -19
  139. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
  140. package/lib/typescript/module/index.d.ts +2 -3
  141. package/lib/typescript/module/index.d.ts.map +1 -1
  142. package/lib/typescript/module/ui/components/AccountMenu.d.ts +4 -2
  143. package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +1 -1
  144. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +7 -8
  145. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +1 -1
  146. package/lib/typescript/module/ui/components/ProfileMenu.d.ts +12 -9
  147. package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/context/OxyContext.d.ts +16 -13
  149. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  150. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +27 -3
  151. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts +22 -17
  153. package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
  154. package/lib/typescript/module/ui/context/silentSessionRestore.d.ts +12 -18
  155. package/lib/typescript/module/ui/context/silentSessionRestore.d.ts.map +1 -1
  156. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
  157. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  158. package/lib/typescript/module/ui/hooks/useAuth.d.ts +1 -1
  159. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  160. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +2 -2
  161. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +1 -1
  162. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  163. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +162 -0
  164. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -0
  165. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +13 -52
  166. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -1
  167. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  168. package/lib/typescript/module/ui/session/createSessionClient.d.ts +15 -0
  169. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -0
  170. package/lib/typescript/module/ui/session/index.d.ts +15 -0
  171. package/lib/typescript/module/ui/session/index.d.ts.map +1 -0
  172. package/lib/typescript/module/ui/session/projectSessionState.d.ts +45 -0
  173. package/lib/typescript/module/ui/session/projectSessionState.d.ts.map +1 -0
  174. package/lib/typescript/module/ui/session/sessionClientHost.d.ts +14 -0
  175. package/lib/typescript/module/ui/session/sessionClientHost.d.ts.map +1 -0
  176. package/lib/typescript/module/ui/session/tokenTransport.d.ts +24 -0
  177. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -0
  178. package/lib/typescript/module/ui/utils/activeAuthuser.d.ts +21 -29
  179. package/lib/typescript/module/ui/utils/activeAuthuser.d.ts.map +1 -1
  180. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +0 -19
  181. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
  182. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +51 -19
  183. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
  184. package/package.json +4 -4
  185. package/src/index.ts +9 -9
  186. package/src/ui/components/AccountMenu.tsx +117 -70
  187. package/src/ui/components/AccountSwitcher.tsx +88 -134
  188. package/src/ui/components/ProfileMenu.tsx +158 -162
  189. package/src/ui/components/SignInModal.tsx +5 -5
  190. package/src/ui/context/OxyContext.tsx +560 -475
  191. package/src/ui/context/hooks/useAuthOperations.ts +72 -63
  192. package/src/ui/context/inSessionTokenRefresh.ts +23 -42
  193. package/src/ui/context/silentSessionRestore.ts +12 -26
  194. package/src/ui/hooks/useAuth.ts +4 -3
  195. package/src/ui/hooks/useOxyAuthSession.ts +13 -8
  196. package/src/ui/hooks/useSessionManagement.ts +16 -87
  197. package/src/ui/hooks/useSwitchableAccounts.ts +397 -0
  198. package/src/ui/hooks/useWebSSO.ts +13 -227
  199. package/src/ui/index.ts +0 -1
  200. package/src/ui/screens/OxyAuthScreen.tsx +2 -2
  201. package/src/ui/session/__tests__/createSessionClient.test.ts +41 -0
  202. package/src/ui/session/__tests__/projectSessionState.test.ts +114 -0
  203. package/src/ui/session/__tests__/sessionClientHost.test.ts +43 -0
  204. package/src/ui/session/__tests__/tokenTransport.test.ts +142 -0
  205. package/src/ui/session/createSessionClient.ts +21 -0
  206. package/src/ui/session/index.ts +19 -0
  207. package/src/ui/session/projectSessionState.ts +85 -0
  208. package/src/ui/session/sessionClientHost.ts +27 -0
  209. package/src/ui/session/tokenTransport.ts +76 -0
  210. package/src/ui/utils/activeAuthuser.ts +34 -76
  211. package/src/ui/utils/sessionHelpers.ts +0 -66
  212. package/src/utils/__tests__/deviceFlowSignIn.test.ts +243 -34
  213. package/src/utils/deviceFlowSignIn.ts +98 -21
  214. package/lib/commonjs/ui/components/accountMenuRows.js +0 -33
  215. package/lib/commonjs/ui/components/accountMenuRows.js.map +0 -1
  216. package/lib/commonjs/ui/hooks/useDeviceAccounts.js +0 -280
  217. package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +0 -1
  218. package/lib/commonjs/ui/hooks/useSessionSocket.js +0 -203
  219. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +0 -1
  220. package/lib/commonjs/utils/silentGuardKey.js +0 -54
  221. package/lib/commonjs/utils/silentGuardKey.js.map +0 -1
  222. package/lib/module/ui/components/accountMenuRows.js +0 -29
  223. package/lib/module/ui/components/accountMenuRows.js.map +0 -1
  224. package/lib/module/ui/hooks/useDeviceAccounts.js +0 -276
  225. package/lib/module/ui/hooks/useDeviceAccounts.js.map +0 -1
  226. package/lib/module/ui/hooks/useSessionSocket.js +0 -199
  227. package/lib/module/ui/hooks/useSessionSocket.js.map +0 -1
  228. package/lib/module/utils/silentGuardKey.js +0 -49
  229. package/lib/module/utils/silentGuardKey.js.map +0 -1
  230. package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts +0 -34
  231. package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts.map +0 -1
  232. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts +0 -133
  233. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +0 -1
  234. package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts +0 -14
  235. package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +0 -1
  236. package/lib/typescript/commonjs/utils/silentGuardKey.d.ts +0 -31
  237. package/lib/typescript/commonjs/utils/silentGuardKey.d.ts.map +0 -1
  238. package/lib/typescript/module/ui/components/accountMenuRows.d.ts +0 -34
  239. package/lib/typescript/module/ui/components/accountMenuRows.d.ts.map +0 -1
  240. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts +0 -133
  241. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +0 -1
  242. package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts +0 -14
  243. package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +0 -1
  244. package/lib/typescript/module/utils/silentGuardKey.d.ts +0 -31
  245. package/lib/typescript/module/utils/silentGuardKey.d.ts.map +0 -1
  246. package/src/ui/components/accountMenuRows.ts +0 -48
  247. package/src/ui/hooks/useDeviceAccounts.ts +0 -344
  248. package/src/ui/hooks/useSessionSocket.ts +0 -233
  249. package/src/ui/utils/__tests__/activeAuthuser.test.ts +0 -75
  250. package/src/utils/__tests__/silentGuardKey.test.ts +0 -82
  251. package/src/utils/silentGuardKey.ts +0 -46
@@ -1,203 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.useSessionSocket = useSessionSocket;
7
- var _react = require("react");
8
- var _socket = _interopRequireDefault(require("socket.io-client"));
9
- var _bloom = require("@oxyhq/bloom");
10
- var _core = require("@oxyhq/core");
11
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
- const debug = (0, _core.createDebugLogger)('SessionSocket');
13
-
14
- /**
15
- * Server-emitted event types on the `session_update` channel.
16
- *
17
- * `session_removed`, `device_removed`, `sessions_removed` are the ONLY
18
- * events that may sign the user out. Anything else (e.g. `session_created`
19
- * fired right after a successful sign-in) MUST NOT trigger sign-out — see
20
- * the comment on the `default` branch below.
21
- */
22
-
23
- function useSessionSocket({
24
- userId,
25
- activeSessionId,
26
- currentDeviceId,
27
- refreshSessions,
28
- clearSessionState,
29
- baseURL,
30
- getAccessToken,
31
- onRemoteSignOut,
32
- onSessionRemoved
33
- }) {
34
- const socketRef = (0, _react.useRef)(null);
35
-
36
- // Store callbacks in refs to avoid reconnecting when they change
37
- const refreshSessionsRef = (0, _react.useRef)(refreshSessions);
38
- const clearSessionStateRef = (0, _react.useRef)(clearSessionState);
39
- const onRemoteSignOutRef = (0, _react.useRef)(onRemoteSignOut);
40
- const onSessionRemovedRef = (0, _react.useRef)(onSessionRemoved);
41
- const activeSessionIdRef = (0, _react.useRef)(activeSessionId);
42
- const currentDeviceIdRef = (0, _react.useRef)(currentDeviceId);
43
- const getAccessTokenRef = (0, _react.useRef)(getAccessToken);
44
-
45
- // Update refs when callbacks change
46
- (0, _react.useEffect)(() => {
47
- refreshSessionsRef.current = refreshSessions;
48
- clearSessionStateRef.current = clearSessionState;
49
- onRemoteSignOutRef.current = onRemoteSignOut;
50
- onSessionRemovedRef.current = onSessionRemoved;
51
- activeSessionIdRef.current = activeSessionId;
52
- currentDeviceIdRef.current = currentDeviceId;
53
- getAccessTokenRef.current = getAccessToken;
54
- }, [refreshSessions, clearSessionState, onRemoteSignOut, onSessionRemoved, activeSessionId, currentDeviceId, getAccessToken]);
55
- (0, _react.useEffect)(() => {
56
- if (!userId || !baseURL) {
57
- // Clean up if userId or baseURL becomes invalid
58
- if (socketRef.current) {
59
- socketRef.current.disconnect();
60
- socketRef.current = null;
61
- }
62
- return;
63
- }
64
-
65
- // Disconnect previous socket if switching users
66
- if (socketRef.current) {
67
- socketRef.current.disconnect();
68
- socketRef.current = null;
69
- }
70
-
71
- // Connect with auth token; use callback so reconnections get a fresh token
72
- socketRef.current = (0, _socket.default)(baseURL, {
73
- transports: ['websocket'],
74
- auth: cb => {
75
- const token = getAccessTokenRef.current();
76
- cb({
77
- token: token ?? ''
78
- });
79
- }
80
- });
81
- const socket = socketRef.current;
82
-
83
- // Server auto-joins the user to `user:<userId>` room on connection
84
- const handleConnect = () => {
85
- debug.log('Socket connected:', socket.id);
86
- };
87
- const refreshSessionsSafe = () => {
88
- refreshSessionsRef.current().catch(error => {
89
- // Refresh errors shouldn't break the socket handler. They happen
90
- // routinely when sessions are removed mid-fetch — log and move on.
91
- if (__DEV__) {
92
- _core.logger.debug('Failed to refresh sessions after session_update', {
93
- component: 'useSessionSocket'
94
- }, error);
95
- }
96
- });
97
- };
98
- const triggerLocalSignOut = async (toastMessage, errorContext) => {
99
- if (onRemoteSignOutRef.current) {
100
- onRemoteSignOutRef.current();
101
- } else {
102
- _bloom.toast.info(toastMessage);
103
- }
104
- // Clear local state since the server has already removed the session.
105
- // Await so storage cleanup completes before any subsequent navigation.
106
- try {
107
- await clearSessionStateRef.current();
108
- } catch (error) {
109
- if (__DEV__) {
110
- _core.logger.error(`Failed to clear session state after ${errorContext}`, error instanceof Error ? error : new Error(String(error)), {
111
- component: 'useSessionSocket'
112
- });
113
- }
114
- }
115
- };
116
- const handleSessionUpdate = async data => {
117
- debug.log('Received session_update:', data);
118
- const currentActiveSessionId = activeSessionIdRef.current;
119
- const currentDeviceIdValue = currentDeviceIdRef.current;
120
-
121
- // Strict whitelist. Every event type that may sign the user out must
122
- // appear in the switch. Anything unknown falls through to `default`,
123
- // which is intentionally a no-op for session lifecycle — it only logs
124
- // in dev. This guards against future server-side event additions
125
- // accidentally triggering sign-out.
126
- switch (data.type) {
127
- case 'session_removed':
128
- {
129
- if (data.sessionId && onSessionRemovedRef.current) {
130
- onSessionRemovedRef.current(data.sessionId);
131
- }
132
- if (data.sessionId && data.sessionId === currentActiveSessionId) {
133
- await triggerLocalSignOut('You have been signed out remotely.', 'session_removed');
134
- } else {
135
- refreshSessionsSafe();
136
- }
137
- break;
138
- }
139
- case 'device_removed':
140
- {
141
- if (data.sessionIds && onSessionRemovedRef.current) {
142
- for (const sessionId of data.sessionIds) {
143
- onSessionRemovedRef.current(sessionId);
144
- }
145
- }
146
- if (data.deviceId && currentDeviceIdValue && data.deviceId === currentDeviceIdValue) {
147
- await triggerLocalSignOut('This device has been removed. You have been signed out.', 'device_removed');
148
- } else {
149
- refreshSessionsSafe();
150
- }
151
- break;
152
- }
153
- case 'sessions_removed':
154
- {
155
- if (data.sessionIds && onSessionRemovedRef.current) {
156
- for (const sessionId of data.sessionIds) {
157
- onSessionRemovedRef.current(sessionId);
158
- }
159
- }
160
- if (data.sessionIds && currentActiveSessionId && data.sessionIds.includes(currentActiveSessionId)) {
161
- await triggerLocalSignOut('You have been signed out remotely.', 'sessions_removed');
162
- } else {
163
- refreshSessionsSafe();
164
- }
165
- break;
166
- }
167
- case 'session_created':
168
- case 'session_update':
169
- {
170
- // Lifecycle event for the current user. Just resync the sessions
171
- // list — never sign out. A prior implementation compared
172
- // `data.sessionId === currentActiveSessionId` and signed the user
173
- // out if true, which was catastrophic: a
174
- // `session_created` event fired immediately after a successful
175
- // sign-in carries the user's NEW (now-active) session id, which
176
- // matched and triggered an instant remote sign-out toast on every
177
- // login. Whitelist explicitly; never fall through.
178
- refreshSessionsSafe();
179
- break;
180
- }
181
- default:
182
- {
183
- if (__DEV__) {
184
- _core.logger.warn('Unknown session socket event type', {
185
- component: 'useSessionSocket',
186
- type: data.type
187
- });
188
- }
189
- break;
190
- }
191
- }
192
- };
193
- socket.on('connect', handleConnect);
194
- socket.on('session_update', handleSessionUpdate);
195
- return () => {
196
- socket.off('connect', handleConnect);
197
- socket.off('session_update', handleSessionUpdate);
198
- socket.disconnect();
199
- socketRef.current = null;
200
- };
201
- }, [userId, baseURL]); // Only depend on userId and baseURL - callbacks are in refs
202
- }
203
- //# sourceMappingURL=useSessionSocket.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_react","require","_socket","_interopRequireDefault","_bloom","_core","e","__esModule","default","debug","createDebugLogger","useSessionSocket","userId","activeSessionId","currentDeviceId","refreshSessions","clearSessionState","baseURL","getAccessToken","onRemoteSignOut","onSessionRemoved","socketRef","useRef","refreshSessionsRef","clearSessionStateRef","onRemoteSignOutRef","onSessionRemovedRef","activeSessionIdRef","currentDeviceIdRef","getAccessTokenRef","useEffect","current","disconnect","io","transports","auth","cb","token","socket","handleConnect","log","id","refreshSessionsSafe","catch","error","__DEV__","logger","component","triggerLocalSignOut","toastMessage","errorContext","toast","info","Error","String","handleSessionUpdate","data","currentActiveSessionId","currentDeviceIdValue","type","sessionId","sessionIds","deviceId","includes","warn","on","off"],"sourceRoot":"../../../../src","sources":["ui/hooks/useSessionSocket.ts"],"mappings":"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","ignoreList":[]}
@@ -1,54 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.buildSilentGuardKey = buildSilentGuardKey;
7
- exports.safeWindowOrigin = safeWindowOrigin;
8
- /**
9
- * Shared, pure helpers for building the `origin|baseURL` signature used as the
10
- * module-level run-once guard key for cold-boot silent-SSO probes
11
- * (`silentColdBootKey` in `OxyContext`, `ssoSignature` in `useWebSSO`).
12
- *
13
- * NATIVE SAFETY (the bug this fixes): React Native aliases a global `window`
14
- * (it points at the JS global object), so `typeof window !== 'undefined'` is
15
- * `true` on native — but `window.location` is `undefined`. Reading
16
- * `window.location.origin` after only a `typeof window` check therefore throws
17
- * `TypeError: Cannot read property 'origin' of undefined` on native. Because
18
- * the key is built UNCONDITIONALLY at the top of the cold-boot path (before its
19
- * try/catch), that throw escaped session restore entirely and broke
20
- * cross-session restore on native. Both prior copies of the guard had the same
21
- * insufficient `typeof window` check and were prone to drift, so the read is
22
- * consolidated here behind a guard that also verifies `window.location`.
23
- */
24
-
25
- /**
26
- * Read `window.location.origin` safely on every platform.
27
- *
28
- * Returns the browser origin on web, and the sentinel `'no-origin'` anywhere
29
- * `window.location` is absent (React Native, SSR/Node). Never throws.
30
- */
31
- function safeWindowOrigin() {
32
- if (typeof window !== 'undefined' && typeof window.location !== 'undefined') {
33
- return window.location.origin;
34
- }
35
- return 'no-origin';
36
- }
37
-
38
- /**
39
- * Build the stable `origin|baseURL` signature for the silent-SSO run-once
40
- * guard. Two providers pointed at the same API from the same origin share one
41
- * attempt. `getBaseURL` is invoked defensively (it may be absent or throw on a
42
- * partially-initialised client); any failure degrades to an empty baseURL.
43
- */
44
- function buildSilentGuardKey(getBaseURL) {
45
- const origin = safeWindowOrigin();
46
- let baseURL = '';
47
- try {
48
- baseURL = getBaseURL?.() ?? '';
49
- } catch {
50
- baseURL = '';
51
- }
52
- return `${origin}|${baseURL}`;
53
- }
54
- //# sourceMappingURL=silentGuardKey.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["safeWindowOrigin","window","location","origin","buildSilentGuardKey","getBaseURL","baseURL"],"sourceRoot":"../../../src","sources":["utils/silentGuardKey.ts"],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,gBAAgBA,CAAA,EAAW;EACzC,IAAI,OAAOC,MAAM,KAAK,WAAW,IAAI,OAAOA,MAAM,CAACC,QAAQ,KAAK,WAAW,EAAE;IAC3E,OAAOD,MAAM,CAACC,QAAQ,CAACC,MAAM;EAC/B;EACA,OAAO,WAAW;AACpB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,mBAAmBA,CAACC,UAAqC,EAAU;EACjF,MAAMF,MAAM,GAAGH,gBAAgB,CAAC,CAAC;EACjC,IAAIM,OAAO,GAAG,EAAE;EAChB,IAAI;IACFA,OAAO,GAAGD,UAAU,GAAG,CAAC,IAAI,EAAE;EAChC,CAAC,CAAC,MAAM;IACNC,OAAO,GAAG,EAAE;EACd;EACA,OAAO,GAAGH,MAAM,IAAIG,OAAO,EAAE;AAC/B","ignoreList":[]}
@@ -1,29 +0,0 @@
1
- "use strict";
2
-
3
- /**
4
- * Pure builder for `AccountMenu` rows. Extracted so the multi-account display
5
- * logic can be unit-tested without rendering React Native.
6
- *
7
- * Maps each {@link DeviceAccount} (sourced from `useDeviceAccounts()`, which
8
- * hydrates EVERY account with real name/email/avatar/color from the shared
9
- * apex `refresh-all` path or the local fallback) into an `AccountRow`.
10
- *
11
- * `secondary` is the account's real email when present; otherwise it falls
12
- * back to the `@handle` line. A missing email is NEVER synthesized into a fake
13
- * `username@oxy.so` — the device-account layer already resolved `email` to the
14
- * real value or the `@handle` fallback.
15
- */
16
- export function buildAccountRows({
17
- accounts
18
- }) {
19
- return accounts.map(account => ({
20
- sessionId: account.sessionId,
21
- authuser: account.authuser,
22
- isActive: account.isCurrent,
23
- displayName: account.displayName,
24
- secondary: account.email,
25
- avatarUri: account.avatarUrl,
26
- user: account.user
27
- }));
28
- }
29
- //# sourceMappingURL=accountMenuRows.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["buildAccountRows","accounts","map","account","sessionId","authuser","isActive","isCurrent","displayName","secondary","email","avatarUri","avatarUrl","user"],"sourceRoot":"../../../../src","sources":["ui/components/accountMenuRows.ts"],"mappings":";;AAsBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,gBAAgBA,CAAC;EAC7BC;AACmB,CAAC,EAAgB;EACpC,OAAOA,QAAQ,CAACC,GAAG,CAAEC,OAAsB,KAAkB;IACzDC,SAAS,EAAED,OAAO,CAACC,SAAS;IAC5BC,QAAQ,EAAEF,OAAO,CAACE,QAAQ;IAC1BC,QAAQ,EAAEH,OAAO,CAACI,SAAS;IAC3BC,WAAW,EAAEL,OAAO,CAACK,WAAW;IAChCC,SAAS,EAAEN,OAAO,CAACO,KAAK;IACxBC,SAAS,EAAER,OAAO,CAACS,SAAS;IAC5BC,IAAI,EAAEV,OAAO,CAACU;EAClB,CAAC,CAAC,CAAC;AACP","ignoreList":[]}
@@ -1,276 +0,0 @@
1
- "use strict";
2
-
3
- import { useMemo } from 'react';
4
- import { useQuery } from '@tanstack/react-query';
5
- import { getAccountDisplayName, getAccountFallbackHandle } from '@oxyhq/core';
6
- import { useOxy } from "../context/OxyContext.js";
7
- import { useI18n } from "./useI18n.js";
8
- import { queryKeys } from "./queries/queryKeys.js";
9
-
10
- /**
11
- * The per-account user shape carried by a {@link DeviceAccount}. Either:
12
- * - the minimal projection returned by `POST /auth/refresh-all`
13
- * ({@link RefreshAllAccountUser}, the shared apex path), or
14
- * - the SDK's canonical {@link User} document (the active row on the local
15
- * fallback path, which already has the full user loaded in `useOxy()`).
16
- *
17
- * Both satisfy `getAccountDisplayName`'s `DisplayNameUserShape`, so display
18
- * resolution is uniform across paths.
19
- */
20
-
21
- /**
22
- * One signed-in account on this device, fully hydrated for the account
23
- * chooser. Unlike the old `AccountMenu` behaviour (which only carried the
24
- * ACTIVE session's user), EVERY entry here carries real per-account
25
- * `displayName` / `email` / `avatarUrl` / `color`.
26
- */
27
-
28
- /**
29
- * Resolve which entries are the current account, robustly.
30
- *
31
- * Primary signal: `entry.sessionId === activeSessionId`. On the web shared-apex
32
- * path, `refreshAllSessions()` returns SERVER-side session ids that may not
33
- * equal the locally-stored `activeSessionId` (a different storage namespace /
34
- * server perspective), so a pure `sessionId` match can flag NO row as current —
35
- * the bug that surfaced as "Not signed in" even while authenticated.
36
- *
37
- * Fallbacks, applied only when the `sessionId` match found nothing AND the user
38
- * is authenticated:
39
- * 1. Match the live `user.id` against each entry's per-account user id.
40
- * 2. If still nothing and there is exactly one account, mark that one current.
41
- *
42
- * At most ONE entry is ever marked current.
43
- *
44
- * Pure & side-effect free so it is unit-testable without rendering React.
45
- */
46
- export function markCurrentAccount(accounts, activeSessionId, liveUserId, isAuthenticated) {
47
- const bySession = accounts.map(account => ({
48
- ...account,
49
- isCurrent: Boolean(activeSessionId) && account.sessionId === activeSessionId
50
- }));
51
- if (bySession.some(account => account.isCurrent) || !isAuthenticated) {
52
- return bySession;
53
- }
54
-
55
- // No row matched by session id: fall back to the live user's id, marking at
56
- // most the FIRST matching entry current (never more than one).
57
- if (liveUserId) {
58
- let matched = false;
59
- const byUser = bySession.map(account => {
60
- if (!matched && account.user.id === liveUserId) {
61
- matched = true;
62
- return {
63
- ...account,
64
- isCurrent: true
65
- };
66
- }
67
- return account;
68
- });
69
- if (matched) {
70
- return byUser;
71
- }
72
- }
73
-
74
- // Authenticated, nothing matched, but there is exactly one account → it must
75
- // be the current one (single-account shared-apex / cross-domain case).
76
- if (bySession.length === 1) {
77
- return [{
78
- ...bySession[0],
79
- isCurrent: true
80
- }];
81
- }
82
- return bySession;
83
- }
84
-
85
- /**
86
- * Resolve every account signed in on this device for the unified account
87
- * switcher, with real per-account name / email / avatar / color.
88
- *
89
- * ## Data sources (web vs native)
90
- *
91
- * - **Web on a `*.oxy.so` host** → `oxyServices.refreshAllSessions()` returns
92
- * the full apex device-account list (identical to what `auth.oxy.so` sees,
93
- * because the `Domain=oxy.so` refresh cookies reach `api.oxy.so`). Used
94
- * directly — this is the `fromSharedApex: true` path.
95
- * - **Cross-domain web (non-`oxy.so` apex)** OR **native (no browser cookies)**
96
- * → `refreshAllSessions()` yields `{ accounts: [] }` (the apex cookies never
97
- * reach the request: cross-domain by `Domain`, native by having no cookie
98
- * jar at all; a 401 is also normalised to `{ accounts: [] }` inside the
99
- * core mixin). In that case we FALL BACK to the SDK's local multi-account
100
- * list from `useOxy()` (`sessions` + `activeSessionId` + the active `user`).
101
- * This is the `fromSharedApex: false` path.
102
- *
103
- * The fallback decision is purely data-driven: **if `refreshAllSessions()`
104
- * returns >0 accounts, use the shared path; otherwise fall back to local
105
- * `useOxy()` sessions.** No host sniffing is needed — the cookie scoping does
106
- * the discrimination for us, and the same code path works on native (where the
107
- * fetch returns `{ accounts: [] }`).
108
- *
109
- * ## Why React Query (not a `useRef`/`useState` start-once like the auth app)
110
- *
111
- * The auth app's `use-device-accounts.ts` hand-rolls a `startedRef` because it
112
- * has no React Query. The SDK does. `refreshAllSessions()` ROTATES single-use
113
- * refresh cookies on every call, so it must run AT MOST ONCE per page load:
114
- * we model that with `staleTime: Infinity` + `gcTime: Infinity` +
115
- * `refetchOnWindowFocus/Reconnect/Mount: false` + `retry: false`. React Query
116
- * dedupes concurrent mounts and caches the single result for the page's
117
- * lifetime — the exact "run once" guarantee the auth app documents, but
118
- * without the manual ref/state machinery.
119
- *
120
- * ## Validation note (intentionally NO zod re-parse)
121
- *
122
- * The auth app's hook calls `fetch` directly, so IT must `safeParse` the wire
123
- * response with `@oxyhq/contracts`. This hook calls
124
- * `oxyServices.refreshAllSessions()`, whose core mixin ALREADY validates and
125
- * normalises the wire response (skips entries missing required fields,
126
- * normalises `authuser` null→0, builds the `RefreshAllAccountUser` shape) — the
127
- * mixin is the single source of truth. Re-validating the SDK's own already-typed
128
- * output here would be redundant double-validation, so we do not re-parse.
129
- *
130
- * ## Error handling
131
- *
132
- * `refreshAllSessions()` already maps 401/404/abort to `{ accounts: [] }`
133
- * internally. Any OTHER failure (network, 5xx) propagates as a thrown error and
134
- * is surfaced by React Query (`query.isError`) — never swallowed. Callers that
135
- * don't care can ignore it; the hook still returns the local fallback list so
136
- * the chooser stays usable.
137
- */
138
- export function useDeviceAccounts() {
139
- const {
140
- oxyServices,
141
- sessions,
142
- activeSessionId,
143
- user,
144
- isAuthenticated
145
- } = useOxy();
146
- const {
147
- locale
148
- } = useI18n();
149
-
150
- // Stable, per-API-origin query key. `refreshAllSessions` resolves against
151
- // the session base url derived from `getBaseURL()`, so keying on it scopes
152
- // the cached result to the API the provider is pointed at.
153
- const baseURL = oxyServices.getBaseURL();
154
- const query = useQuery({
155
- queryKey: [...queryKeys.accounts.all, 'deviceAccounts', baseURL],
156
- queryFn: () => oxyServices.refreshAllSessions(),
157
- // Only attempt the shared apex path while signed in. When logged out
158
- // there is nothing to enumerate and the fallback (also empty) is used.
159
- enabled: isAuthenticated,
160
- // Single-use cookie rotation → run at most once per page load.
161
- staleTime: Number.POSITIVE_INFINITY,
162
- gcTime: Number.POSITIVE_INFINITY,
163
- refetchOnWindowFocus: false,
164
- refetchOnReconnect: false,
165
- refetchOnMount: false,
166
- retry: false
167
- });
168
- const sharedAccounts = query.data?.accounts ?? [];
169
- const fromSharedApex = sharedAccounts.length > 0;
170
- const accounts = useMemo(() => {
171
- const resolveAvatarUrl = avatar => avatar ? oxyServices.getFileDownloadUrl(avatar, 'thumb') : undefined;
172
-
173
- // Build a single current-account row from the live `useOxy().user`. Used
174
- // as a last-resort fallback when neither the shared-apex probe nor the
175
- // local session store yielded a row but the user IS authenticated (e.g.
176
- // a cross-domain host where the apex probe returned empty before the
177
- // local session store hydrated). The signed-in user must ALWAYS be
178
- // represented. Email is the REAL email or the `@handle` line — never a
179
- // synthesized `username@oxy.so`.
180
- const liveUserRow = () => {
181
- if (!isAuthenticated || !user || !activeSessionId) {
182
- return [];
183
- }
184
- const displayName = getAccountDisplayName(user, locale);
185
- const handle = getAccountFallbackHandle(user);
186
- const secondaryHandle = handle ? `@${handle}` : null;
187
- return [{
188
- sessionId: activeSessionId,
189
- authuser: undefined,
190
- isCurrent: true,
191
- displayName,
192
- email: user.email ?? secondaryHandle,
193
- avatarUrl: resolveAvatarUrl(user.avatar),
194
- color: user.color ?? null,
195
- user
196
- }];
197
- };
198
- let built;
199
- if (fromSharedApex) {
200
- // Shared apex path: every entry carries a real per-account user.
201
- built = sharedAccounts.flatMap(entry => {
202
- if (!entry.user) {
203
- return [];
204
- }
205
- const accountUser = entry.user;
206
- const displayName = getAccountDisplayName(accountUser, locale);
207
- const handle = getAccountFallbackHandle(accountUser);
208
- const email = entry.user.email ?? null;
209
- const secondaryHandle = handle ? `@${handle}` : null;
210
- return [{
211
- sessionId: entry.sessionId,
212
- authuser: entry.authuser,
213
- // Provisional; finalised by `markCurrentAccount` below so the
214
- // shared-apex path is robust to server/local session-id skew.
215
- isCurrent: false,
216
- displayName,
217
- // Real email, or null (NEVER synthesized). The UI uses the
218
- // `@handle` line only when email is genuinely absent.
219
- email: email ?? secondaryHandle,
220
- avatarUrl: resolveAvatarUrl(entry.user.avatar),
221
- color: entry.user.color ?? null,
222
- user: accountUser
223
- }];
224
- });
225
- } else {
226
- // Local fallback path: build from the SDK's multi-session store. The
227
- // active session row gets the full loaded `user`; inactive fallback
228
- // rows carry only what the `ClientSession` exposes (no synthesized
229
- // identity — they show the active user's data only when active).
230
- built = (sessions ?? []).flatMap(session => {
231
- const isCurrent = session.sessionId === activeSessionId;
232
- if (!isCurrent || !user) {
233
- return [];
234
- }
235
- const accountUser = user;
236
- const displayName = getAccountDisplayName(accountUser, locale);
237
- const handle = getAccountFallbackHandle(accountUser);
238
- const email = user.email ?? null;
239
- const secondaryHandle = handle ? `@${handle}` : null;
240
- return [{
241
- sessionId: session.sessionId,
242
- authuser: session.authuser,
243
- isCurrent,
244
- displayName,
245
- email: email ?? secondaryHandle,
246
- avatarUrl: resolveAvatarUrl(user.avatar),
247
- color: user.color ?? null,
248
- user: accountUser
249
- }];
250
- });
251
- }
252
-
253
- // Robust current-account detection: tolerate server/local session-id
254
- // skew on the shared-apex path by falling back to the live user's id and
255
- // the single-account heuristic.
256
- const flagged = markCurrentAccount(built, activeSessionId, user?.id ?? null, isAuthenticated);
257
-
258
- // The signed-in user must ALWAYS be represented. If detection produced
259
- // an empty list yet the user is authenticated, synthesize a single
260
- // current row from the live `useOxy().user`.
261
- if (flagged.length === 0) {
262
- return liveUserRow();
263
- }
264
- return flagged;
265
- }, [fromSharedApex, sharedAccounts, sessions, activeSessionId, user, isAuthenticated, locale, oxyServices]);
266
- return {
267
- accounts,
268
- // `isLoading` only reflects the shared probe while it's the relevant
269
- // source. Once we know we're on the fallback (probe settled with 0
270
- // accounts) the local list is synchronously available.
271
- isLoading: isAuthenticated && query.isLoading,
272
- currentSessionId: activeSessionId ?? null,
273
- fromSharedApex
274
- };
275
- }
276
- //# sourceMappingURL=useDeviceAccounts.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["useMemo","useQuery","getAccountDisplayName","getAccountFallbackHandle","useOxy","useI18n","queryKeys","markCurrentAccount","accounts","activeSessionId","liveUserId","isAuthenticated","bySession","map","account","isCurrent","Boolean","sessionId","some","matched","byUser","user","id","length","useDeviceAccounts","oxyServices","sessions","locale","baseURL","getBaseURL","query","queryKey","all","queryFn","refreshAllSessions","enabled","staleTime","Number","POSITIVE_INFINITY","gcTime","refetchOnWindowFocus","refetchOnReconnect","refetchOnMount","retry","sharedAccounts","data","fromSharedApex","resolveAvatarUrl","avatar","getFileDownloadUrl","undefined","liveUserRow","displayName","handle","secondaryHandle","authuser","email","avatarUrl","color","built","flatMap","entry","accountUser","session","flagged","isLoading","currentSessionId"],"sourceRoot":"../../../../src","sources":["ui/hooks/useDeviceAccounts.ts"],"mappings":"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","ignoreList":[]}