@oxyhq/services 27.1.3 → 29.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 (387) hide show
  1. package/LICENSE +202 -661
  2. package/NOTICE +16 -0
  3. package/README.md +67 -2
  4. package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt +178 -0
  5. package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionProvider.kt +99 -0
  6. package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionStore.kt +115 -0
  7. package/expo-module.config.json +1 -0
  8. package/lib/commonjs/index.js +78 -16
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/ui/boot/runProviderColdBoot.js +7 -0
  11. package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
  12. package/lib/commonjs/ui/client.js +12 -7
  13. package/lib/commonjs/ui/client.js.map +1 -1
  14. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +10 -27
  15. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
  16. package/lib/commonjs/ui/components/OxyAuthChooser.js +132 -44
  17. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  18. package/lib/commonjs/ui/components/OxyProvider.js +4 -2
  19. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  20. package/lib/commonjs/ui/components/SurfaceScreen.js +31 -3
  21. package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
  22. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +209 -84
  23. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  24. package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +25 -7
  25. package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
  26. package/lib/commonjs/ui/components/authChooser/primitives.js +35 -22
  27. package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
  28. package/lib/commonjs/ui/components/authChooser/signInProgress.js +5 -0
  29. package/lib/commonjs/ui/components/authChooser/signInProgress.js.map +1 -1
  30. package/lib/commonjs/ui/components/authChooser/types.js +11 -0
  31. package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
  32. package/lib/commonjs/ui/context/OxyContext.js +257 -306
  33. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  34. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +27 -37
  35. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  36. package/lib/commonjs/ui/context/oxyContextHelpers.js +0 -29
  37. package/lib/commonjs/ui/context/oxyContextHelpers.js.map +1 -1
  38. package/lib/commonjs/ui/context/useOxyAccountGraph.js +6 -17
  39. package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
  40. package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +42 -0
  41. package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -0
  42. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +0 -19
  43. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  44. package/lib/commonjs/ui/hooks/useDeviceSwitcher.js +57 -0
  45. package/lib/commonjs/ui/hooks/useDeviceSwitcher.js.map +1 -0
  46. package/lib/commonjs/ui/hooks/useI18n.js +22 -3
  47. package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
  48. package/lib/commonjs/ui/hooks/useSessionManagement.js +46 -153
  49. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  50. package/lib/commonjs/ui/index.js +12 -7
  51. package/lib/commonjs/ui/index.js.map +1 -1
  52. package/lib/commonjs/ui/navigation/routes.js +85 -73
  53. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  54. package/lib/commonjs/ui/navigation/surfaces.js +8 -7
  55. package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
  56. package/lib/commonjs/ui/runtime/createOxyRuntime.js +433 -0
  57. package/lib/commonjs/ui/runtime/createOxyRuntime.js.map +1 -0
  58. package/lib/commonjs/ui/runtime/index.js +63 -0
  59. package/lib/commonjs/ui/runtime/index.js.map +1 -0
  60. package/lib/commonjs/ui/runtime/runtimeContext.js +83 -0
  61. package/lib/commonjs/ui/runtime/runtimeContext.js.map +1 -0
  62. package/lib/commonjs/ui/runtime/types.js +6 -0
  63. package/lib/commonjs/ui/runtime/types.js.map +1 -0
  64. package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js +57 -0
  65. package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js.map +1 -0
  66. package/lib/commonjs/ui/screens/AccountMembersScreen.js +3 -4
  67. package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
  68. package/lib/commonjs/ui/screens/CreateAccountScreen.js +3 -12
  69. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  70. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +6 -5
  71. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  72. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +4 -4
  73. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  74. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +1 -1
  75. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +4 -2
  76. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  77. package/lib/commonjs/ui/server.js +1 -2
  78. package/lib/commonjs/ui/server.js.map +1 -1
  79. package/lib/commonjs/ui/session/authStore.js +45 -9
  80. package/lib/commonjs/ui/session/authStore.js.map +1 -1
  81. package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js +306 -0
  82. package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js.map +1 -0
  83. package/lib/commonjs/ui/stores/authStore.js +154 -55
  84. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  85. package/lib/commonjs/ui/stores/followTargetStore.js +27 -0
  86. package/lib/commonjs/ui/stores/followTargetStore.js.map +1 -1
  87. package/lib/commonjs/ui/utils/avatarUtils.js +2 -36
  88. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  89. package/lib/module/index.js +20 -9
  90. package/lib/module/index.js.map +1 -1
  91. package/lib/module/ui/boot/runProviderColdBoot.js +7 -0
  92. package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
  93. package/lib/module/ui/client.js +1 -2
  94. package/lib/module/ui/client.js.map +1 -1
  95. package/lib/module/ui/components/OxyAccountDialogScreen.js +10 -27
  96. package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
  97. package/lib/module/ui/components/OxyAuthChooser.js +134 -44
  98. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  99. package/lib/module/ui/components/OxyProvider.js +5 -3
  100. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  101. package/lib/module/ui/components/SurfaceScreen.js +33 -5
  102. package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
  103. package/lib/module/ui/components/authChooser/AccountsMenuView.js +210 -85
  104. package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  105. package/lib/module/ui/components/authChooser/SignInEntryView.js +25 -7
  106. package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
  107. package/lib/module/ui/components/authChooser/primitives.js +35 -22
  108. package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
  109. package/lib/module/ui/components/authChooser/signInProgress.js +5 -0
  110. package/lib/module/ui/components/authChooser/signInProgress.js.map +1 -1
  111. package/lib/module/ui/components/authChooser/types.js +11 -0
  112. package/lib/module/ui/components/authChooser/types.js.map +1 -1
  113. package/lib/module/ui/context/OxyContext.js +251 -303
  114. package/lib/module/ui/context/OxyContext.js.map +1 -1
  115. package/lib/module/ui/context/hooks/useAuthOperations.js +27 -37
  116. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  117. package/lib/module/ui/context/oxyContextHelpers.js +0 -28
  118. package/lib/module/ui/context/oxyContextHelpers.js.map +1 -1
  119. package/lib/module/ui/context/useOxyAccountGraph.js +6 -17
  120. package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
  121. package/lib/module/ui/hooks/accountDialogSnapshot.js +38 -0
  122. package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -0
  123. package/lib/module/ui/hooks/mutations/useAccountMutations.js +0 -19
  124. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  125. package/lib/module/ui/hooks/useDeviceSwitcher.js +53 -0
  126. package/lib/module/ui/hooks/useDeviceSwitcher.js.map +1 -0
  127. package/lib/module/ui/hooks/useI18n.js +24 -4
  128. package/lib/module/ui/hooks/useI18n.js.map +1 -1
  129. package/lib/module/ui/hooks/useSessionManagement.js +47 -155
  130. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  131. package/lib/module/ui/index.js +1 -2
  132. package/lib/module/ui/index.js.map +1 -1
  133. package/lib/module/ui/navigation/routes.js +84 -72
  134. package/lib/module/ui/navigation/routes.js.map +1 -1
  135. package/lib/module/ui/navigation/surfaces.js +6 -6
  136. package/lib/module/ui/navigation/surfaces.js.map +1 -1
  137. package/lib/module/ui/runtime/createOxyRuntime.js +430 -0
  138. package/lib/module/ui/runtime/createOxyRuntime.js.map +1 -0
  139. package/lib/module/ui/runtime/index.js +15 -0
  140. package/lib/module/ui/runtime/index.js.map +1 -0
  141. package/lib/module/ui/runtime/runtimeContext.js +75 -0
  142. package/lib/module/ui/runtime/runtimeContext.js.map +1 -0
  143. package/lib/module/ui/runtime/types.js +4 -0
  144. package/lib/module/ui/runtime/types.js.map +1 -0
  145. package/lib/module/ui/runtime/useRuntimeSnapshot.js +52 -0
  146. package/lib/module/ui/runtime/useRuntimeSnapshot.js.map +1 -0
  147. package/lib/module/ui/screens/AccountMembersScreen.js +4 -5
  148. package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
  149. package/lib/module/ui/screens/CreateAccountScreen.js +4 -13
  150. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  151. package/lib/module/ui/screens/trust/TrustCenterScreen.js +6 -5
  152. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  153. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +5 -5
  154. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  155. package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
  156. package/lib/module/ui/screens/trust/TrustRulesScreen.js +4 -2
  157. package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
  158. package/lib/module/ui/server.js +0 -1
  159. package/lib/module/ui/server.js.map +1 -1
  160. package/lib/module/ui/session/authStore.js +46 -11
  161. package/lib/module/ui/session/authStore.js.map +1 -1
  162. package/lib/module/ui/session/sharedDeviceCredentialStore.js +302 -0
  163. package/lib/module/ui/session/sharedDeviceCredentialStore.js.map +1 -0
  164. package/lib/module/ui/stores/authStore.js +154 -55
  165. package/lib/module/ui/stores/authStore.js.map +1 -1
  166. package/lib/module/ui/stores/followTargetStore.js +25 -0
  167. package/lib/module/ui/stores/followTargetStore.js.map +1 -1
  168. package/lib/module/ui/utils/avatarUtils.js +2 -35
  169. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  170. package/lib/typescript/commonjs/index.d.ts +6 -4
  171. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  172. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/ui/client.d.ts +1 -2
  174. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  176. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  177. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  178. package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
  179. package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
  180. package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
  181. package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +3 -1
  182. package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +18 -6
  184. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
  185. package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts.map +1 -1
  186. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +33 -1
  187. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
  188. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +35 -4
  189. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  190. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +8 -12
  191. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  192. package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts +0 -2
  193. package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +9 -1
  195. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  196. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +1 -3
  197. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  198. package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts +15 -0
  199. package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
  200. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts +49 -0
  202. package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
  203. package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts +13 -0
  204. package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
  205. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts +23 -18
  206. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/ui/index.d.ts +1 -2
  208. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +15 -1
  210. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  211. package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
  212. package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts +144 -0
  213. package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts.map +1 -0
  214. package/lib/typescript/commonjs/ui/runtime/index.d.ts +16 -0
  215. package/lib/typescript/commonjs/ui/runtime/index.d.ts.map +1 -0
  216. package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts +43 -0
  217. package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts.map +1 -0
  218. package/lib/typescript/commonjs/ui/runtime/types.d.ts +89 -0
  219. package/lib/typescript/commonjs/ui/runtime/types.d.ts.map +1 -0
  220. package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
  221. package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
  222. package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  223. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  224. package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  225. package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
  226. package/lib/typescript/commonjs/ui/server.d.ts +0 -1
  227. package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
  228. package/lib/typescript/commonjs/ui/session/authStore.d.ts +60 -9
  229. package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
  230. package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
  231. package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
  232. package/lib/typescript/commonjs/ui/stores/authStore.d.ts +49 -5
  233. package/lib/typescript/commonjs/ui/stores/authStore.d.ts.map +1 -1
  234. package/lib/typescript/commonjs/ui/stores/followTargetStore.d.ts +8 -1
  235. package/lib/typescript/commonjs/ui/stores/followTargetStore.d.ts.map +1 -1
  236. package/lib/typescript/commonjs/ui/types/navigation.d.ts +26 -0
  237. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  238. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts +2 -10
  239. package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
  240. package/lib/typescript/module/index.d.ts +6 -4
  241. package/lib/typescript/module/index.d.ts.map +1 -1
  242. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  243. package/lib/typescript/module/ui/client.d.ts +1 -2
  244. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  245. package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  246. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  247. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  248. package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
  249. package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
  250. package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
  251. package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +3 -1
  252. package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
  253. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +18 -6
  254. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
  255. package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts.map +1 -1
  256. package/lib/typescript/module/ui/components/authChooser/types.d.ts +33 -1
  257. package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
  258. package/lib/typescript/module/ui/context/OxyContext.d.ts +35 -4
  259. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  260. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +8 -12
  261. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  262. package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts +0 -2
  263. package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts.map +1 -1
  264. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +9 -1
  265. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  266. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +1 -3
  267. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  268. package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts +15 -0
  269. package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
  270. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  271. package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts +49 -0
  272. package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
  273. package/lib/typescript/module/ui/hooks/useI18n.d.ts +13 -0
  274. package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
  275. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts +23 -18
  276. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  277. package/lib/typescript/module/ui/index.d.ts +1 -2
  278. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  279. package/lib/typescript/module/ui/navigation/routes.d.ts +15 -1
  280. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  281. package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
  282. package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts +144 -0
  283. package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts.map +1 -0
  284. package/lib/typescript/module/ui/runtime/index.d.ts +16 -0
  285. package/lib/typescript/module/ui/runtime/index.d.ts.map +1 -0
  286. package/lib/typescript/module/ui/runtime/runtimeContext.d.ts +43 -0
  287. package/lib/typescript/module/ui/runtime/runtimeContext.d.ts.map +1 -0
  288. package/lib/typescript/module/ui/runtime/types.d.ts +89 -0
  289. package/lib/typescript/module/ui/runtime/types.d.ts.map +1 -0
  290. package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
  291. package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
  292. package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
  293. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  294. package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  295. package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
  296. package/lib/typescript/module/ui/server.d.ts +0 -1
  297. package/lib/typescript/module/ui/server.d.ts.map +1 -1
  298. package/lib/typescript/module/ui/session/authStore.d.ts +60 -9
  299. package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
  300. package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
  301. package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
  302. package/lib/typescript/module/ui/stores/authStore.d.ts +49 -5
  303. package/lib/typescript/module/ui/stores/authStore.d.ts.map +1 -1
  304. package/lib/typescript/module/ui/stores/followTargetStore.d.ts +8 -1
  305. package/lib/typescript/module/ui/stores/followTargetStore.d.ts.map +1 -1
  306. package/lib/typescript/module/ui/types/navigation.d.ts +26 -0
  307. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  308. package/lib/typescript/module/ui/utils/avatarUtils.d.ts +2 -10
  309. package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
  310. package/package.json +8 -8
  311. package/plugins/withSharedDeviceSessionProvider.js +102 -0
  312. package/plugins/withSharedDeviceSessionReader.js +57 -0
  313. package/src/__tests__/esmSourcePurity.test.ts +61 -0
  314. package/src/index.ts +36 -9
  315. package/src/ui/boot/runProviderColdBoot.ts +7 -0
  316. package/src/ui/client.ts +1 -2
  317. package/src/ui/components/OxyAccountDialogScreen.tsx +10 -29
  318. package/src/ui/components/OxyAuthChooser.tsx +149 -50
  319. package/src/ui/components/OxyProvider.tsx +7 -5
  320. package/src/ui/components/SurfaceScreen.tsx +28 -4
  321. package/src/ui/components/__tests__/followTargetButton.test.ts +39 -0
  322. package/src/ui/components/authChooser/AccountsMenuView.tsx +246 -96
  323. package/src/ui/components/authChooser/SignInEntryView.tsx +31 -8
  324. package/src/ui/components/authChooser/primitives.tsx +37 -23
  325. package/src/ui/components/authChooser/signInProgress.ts +4 -0
  326. package/src/ui/components/authChooser/types.ts +34 -1
  327. package/src/ui/context/OxyContext.tsx +269 -324
  328. package/src/ui/context/hooks/useAuthOperations.ts +32 -35
  329. package/src/ui/context/oxyContextHelpers.ts +0 -30
  330. package/src/ui/context/oxyContextTypes.ts +9 -1
  331. package/src/ui/context/useOxyAccountGraph.ts +5 -19
  332. package/src/ui/hooks/accountDialogSnapshot.ts +37 -0
  333. package/src/ui/hooks/mutations/useAccountMutations.ts +0 -19
  334. package/src/ui/hooks/queries/__tests__/userCache.test.ts +12 -1
  335. package/src/ui/hooks/useDeviceSwitcher.ts +110 -0
  336. package/src/ui/hooks/useI18n.ts +23 -3
  337. package/src/ui/hooks/useSessionManagement.ts +62 -211
  338. package/src/ui/index.ts +1 -2
  339. package/src/ui/navigation/routes.ts +85 -75
  340. package/src/ui/navigation/surfaces.ts +6 -9
  341. package/src/ui/runtime/createOxyRuntime.ts +623 -0
  342. package/src/ui/runtime/index.ts +34 -0
  343. package/src/ui/runtime/runtimeContext.tsx +111 -0
  344. package/src/ui/runtime/types.ts +110 -0
  345. package/src/ui/runtime/useRuntimeSnapshot.ts +52 -0
  346. package/src/ui/screens/AccountMembersScreen.tsx +6 -5
  347. package/src/ui/screens/CreateAccountScreen.tsx +3 -16
  348. package/src/ui/screens/trust/TrustCenterScreen.tsx +7 -7
  349. package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +4 -5
  350. package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
  351. package/src/ui/screens/trust/TrustRulesScreen.tsx +3 -2
  352. package/src/ui/server.ts +0 -1
  353. package/src/ui/session/__tests__/authStore.test.ts +90 -0
  354. package/src/ui/session/__tests__/sharedDeviceCredentialStore.test.ts +289 -0
  355. package/src/ui/session/authStore.ts +78 -9
  356. package/src/ui/session/sharedDeviceCredentialStore.ts +318 -0
  357. package/src/ui/stores/authStore.ts +171 -43
  358. package/src/ui/stores/followTargetStore.ts +24 -1
  359. package/src/ui/types/navigation.ts +26 -0
  360. package/src/ui/utils/avatarUtils.ts +2 -37
  361. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +0 -64
  362. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +0 -1
  363. package/lib/commonjs/ui/screens/trust/trustTier.js +0 -23
  364. package/lib/commonjs/ui/screens/trust/trustTier.js.map +0 -1
  365. package/lib/commonjs/ui/stores/accountStore.js +0 -221
  366. package/lib/commonjs/ui/stores/accountStore.js.map +0 -1
  367. package/lib/module/ui/hooks/useSwitchableAccounts.js +0 -60
  368. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +0 -1
  369. package/lib/module/ui/screens/trust/trustTier.js +0 -19
  370. package/lib/module/ui/screens/trust/trustTier.js.map +0 -1
  371. package/lib/module/ui/stores/accountStore.js +0 -213
  372. package/lib/module/ui/stores/accountStore.js.map +0 -1
  373. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +0 -23
  374. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
  375. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +0 -9
  376. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +0 -1
  377. package/lib/typescript/commonjs/ui/stores/accountStore.d.ts +0 -27
  378. package/lib/typescript/commonjs/ui/stores/accountStore.d.ts.map +0 -1
  379. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +0 -23
  380. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
  381. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +0 -9
  382. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +0 -1
  383. package/lib/typescript/module/ui/stores/accountStore.d.ts +0 -27
  384. package/lib/typescript/module/ui/stores/accountStore.d.ts.map +0 -1
  385. package/src/ui/hooks/useSwitchableAccounts.ts +0 -75
  386. package/src/ui/screens/trust/trustTier.ts +0 -20
  387. package/src/ui/stores/accountStore.ts +0 -264
@@ -0,0 +1,623 @@
1
+ import type { DeviceDirectory, DeviceSessionState } from '@oxyhq/contracts';
2
+ import type { ClientSession, DeviceContext, User } from '@oxyhq/core';
3
+ import {
4
+ accountIdsOf,
5
+ activeSessionIdOf,
6
+ activeUserOf,
7
+ deviceStateToClientSessions,
8
+ mergeSessions as mergeSessionLists,
9
+ normalizeAndSortSessions,
10
+ normalizeUserIdentity,
11
+ resolveActiveContext,
12
+ sessionsArraysEqual,
13
+ } from '@oxyhq/core';
14
+ import type { IdentitySessionBinding } from '../session/identityBinding';
15
+ import type { OxyRuntimeSnapshot, OxyRuntimeStatus, OxyTokenStatus } from './types';
16
+
17
+ /**
18
+ * A change of SUBJECT — the account the session acts as.
19
+ *
20
+ * `previous === null` is an initial sign-in (nothing account-scoped is cached
21
+ * yet); `next === null` is a sign-out; both non-null and different is a switch,
22
+ * the only case that must invalidate a warm cache.
23
+ */
24
+ export interface SubjectTransition {
25
+ previous: string | null;
26
+ next: string | null;
27
+ }
28
+
29
+ /**
30
+ * The three seams the runtime reads, declared STRUCTURALLY rather than as the
31
+ * concrete `OxyServices` / `SessionClient` / `SessionClientHost` types.
32
+ *
33
+ * The real classes satisfy them, so nothing changes at the call site — but a
34
+ * test can hand the runtime three plain objects instead of casting a stub
35
+ * through `unknown`, which is how a stub silently drifts from the interface it
36
+ * pretends to implement.
37
+ */
38
+ export interface RuntimeClient {
39
+ getAccessToken(): string | null;
40
+ getUsersByIds(ids: string[]): Promise<User[]>;
41
+ }
42
+
43
+ export interface RuntimeSessionClient {
44
+ getState(): DeviceSessionState | null;
45
+ getDirectory(): DeviceDirectory | null;
46
+ subscribe(listener: (state: DeviceSessionState | null) => void): () => void;
47
+ refreshDirectory(): Promise<void>;
48
+ activateContext(contextId: string): Promise<void>;
49
+ signOutContext(contextId: string): Promise<void>;
50
+ signOutPrincipal(principalId: string): Promise<void>;
51
+ }
52
+
53
+ export interface RuntimeSessionHost {
54
+ setCurrentAccountId(accountId: string | null): void;
55
+ }
56
+
57
+ export interface OxyRuntimeConfig {
58
+ oxyServices: RuntimeClient;
59
+ sessionClient: RuntimeSessionClient;
60
+ sessionClientHost: RuntimeSessionHost;
61
+ /** `null` for every account-mode runtime; the pin binding in identity mode. */
62
+ identity: IdentitySessionBinding | null;
63
+
64
+ /**
65
+ * Reset account-scoped caches. Called BETWEEN the bearer commit and the
66
+ * snapshot publish, which is the whole point of routing every fact change
67
+ * through one transition (ADR 0002's ordering invariant): a consumer woken by
68
+ * a switch must never read a cache entry belonging to the previous subject.
69
+ */
70
+ onSubjectChange?: (transition: SubjectTransition) => void;
71
+
72
+ /**
73
+ * The device reports zero accounts. Local UI teardown only — the durable
74
+ * device credential is the caller's decision, gated on a `request`-origin
75
+ * verdict.
76
+ */
77
+ onDeviceEmpty: () => Promise<void>;
78
+
79
+ /** Persist the projected session-id list (display/bookkeeping storage). */
80
+ onSessionsProjected?: (sessionIds: string[]) => void;
81
+
82
+ /**
83
+ * Identity mode only: the pinned account is unknown or has left this device's
84
+ * session set, so the session must be re-derived from the local identity key.
85
+ * Projecting anything here would mean projecting somebody else's account.
86
+ */
87
+ onIdentityUnbound?: (binding: IdentitySessionBinding, revision: number) => void;
88
+
89
+ logger: (message: string, error?: unknown) => void;
90
+ }
91
+
92
+ /**
93
+ * The headless session runtime (ADR 0004).
94
+ *
95
+ * It owns the session facts and the ORDER in which they become visible. React
96
+ * is an adapter: the provider hands components this object and they subscribe
97
+ * with selectors, so an unrelated change (a locale, a dialog opening) cannot
98
+ * rebuild an auth consumer's view.
99
+ */
100
+ export interface OxyRuntime {
101
+ getSnapshot(): OxyRuntimeSnapshot;
102
+ subscribe(listener: () => void): () => void;
103
+
104
+ /** Begin projecting `SessionClient` transitions. Returns the disposer. */
105
+ start(): () => void;
106
+
107
+ /**
108
+ * Project the client's current device state onto the snapshot. Idempotent and
109
+ * revision-guarded: a projection whose profile fetch was overtaken by a
110
+ * fresher state discards itself rather than publishing a stale subject.
111
+ */
112
+ reconcileFromClient(): Promise<void>;
113
+
114
+ /** Read the device directory and publish it (opts this runtime into ADR 0002). */
115
+ refreshDirectory(): Promise<void>;
116
+
117
+ /**
118
+ * Make one `principal acting as account` context active (ADR 0002).
119
+ *
120
+ * `contextId`, never an account id: on a device holding two people the same
121
+ * organization is reachable through both, and an account id cannot name which
122
+ * route to take.
123
+ */
124
+ activateContext(contextId: string): Promise<void>;
125
+
126
+ /**
127
+ * Remove ONE `principal → account` pair. The same account reached through a
128
+ * second person is a different session with a different audit actor, and it
129
+ * stays.
130
+ *
131
+ * Context ids are NOT stable across a removal: the server offers the pair
132
+ * again under a NEW id on the next directory read, so nothing may cache one
133
+ * across this call.
134
+ */
135
+ signOutContext(contextId: string): Promise<void>;
136
+
137
+ /** Remove ONE PERSON and every context they reach, and nobody else's. */
138
+ signOutPrincipal(principalId: string): Promise<void>;
139
+
140
+ /**
141
+ * Run several fact changes as ONE transition.
142
+ *
143
+ * A session commit sets the optimistic session mirror, the active session id
144
+ * and the account. Left as three transitions those are three publishes, and
145
+ * — worse — the account is compared against a different baseline each time,
146
+ * so an account-scoped cache reset could run against a half-applied session.
147
+ * Batched, subscribers are woken once, after all of it, with the reset having
148
+ * run exactly once in between.
149
+ */
150
+ batch(mutate: () => void): void;
151
+
152
+ setAccount(user: User): void;
153
+ setLoading(loading: boolean): void;
154
+ setError(error: string | null): void;
155
+ setStorageReady(ready: boolean): void;
156
+ setTokenReady(ready: boolean): void;
157
+ setHasAccessToken(has: boolean): void;
158
+ setSwitching(switching: boolean): void;
159
+ markAuthResolved(): void;
160
+
161
+ /**
162
+ * Merge an optimistic local session mirror so the UI moves before the server
163
+ * round-trip. The authoritative projection overwrites it a moment later; both
164
+ * write the SAME field, so there is still exactly one owner.
165
+ */
166
+ mergeSessions(incoming: ClientSession[], options?: { merge?: boolean; preserveSessionIds?: string[] }): void;
167
+ setActiveSessionId(sessionId: string | null): void;
168
+
169
+ /** Local teardown: drop the session projection without touching the server. */
170
+ clearSession(): void;
171
+ }
172
+
173
+ interface RuntimeFacts {
174
+ deviceState: DeviceSessionState | null;
175
+ directory: DeviceDirectory | null;
176
+ profiles: Map<string, User>;
177
+ sessions: ClientSession[];
178
+ activeSessionId: string | null;
179
+ account: User | null;
180
+ pinnedAccountId: string | null;
181
+ isLoading: boolean;
182
+ error: string | null;
183
+ storageReady: boolean;
184
+ tokenReady: boolean;
185
+ hasAccessToken: boolean;
186
+ authResolved: boolean;
187
+ switching: boolean;
188
+ }
189
+
190
+ const EMPTY_SESSIONS: ClientSession[] = [];
191
+
192
+ function deriveStatus(facts: RuntimeFacts): OxyRuntimeStatus {
193
+ if (!facts.authResolved) {
194
+ return 'booting';
195
+ }
196
+ if (facts.account !== null) {
197
+ return 'authenticated';
198
+ }
199
+ return facts.error !== null ? 'error' : 'signed_out';
200
+ }
201
+
202
+ function deriveTokenStatus(facts: RuntimeFacts): OxyTokenStatus {
203
+ if (facts.hasAccessToken && facts.tokenReady) {
204
+ return 'ready';
205
+ }
206
+ if (facts.account !== null || facts.hasAccessToken) {
207
+ return 'refreshing';
208
+ }
209
+ return 'missing';
210
+ }
211
+
212
+ /**
213
+ * The actor behind the active context, resolved to a full `User`.
214
+ *
215
+ * Falls back to the subject: on a device with no directory, and on any
216
+ * non-delegated context, the two ARE the same person, and saying so is more
217
+ * useful than a `null` every caller would have to special-case.
218
+ */
219
+ function derivePrincipal(
220
+ activeContext: DeviceContext | null,
221
+ profiles: Map<string, User>,
222
+ account: User | null,
223
+ ): User | null {
224
+ if (activeContext === null) {
225
+ return account;
226
+ }
227
+ return profiles.get(activeContext.actor.userId) ?? account;
228
+ }
229
+
230
+ export function createOxyRuntime(config: OxyRuntimeConfig): OxyRuntime {
231
+ const {
232
+ oxyServices,
233
+ sessionClient,
234
+ sessionClientHost,
235
+ identity,
236
+ onSubjectChange,
237
+ onDeviceEmpty,
238
+ onSessionsProjected,
239
+ onIdentityUnbound,
240
+ logger,
241
+ } = config;
242
+
243
+ const facts: RuntimeFacts = {
244
+ deviceState: null,
245
+ directory: null,
246
+ profiles: new Map(),
247
+ sessions: EMPTY_SESSIONS,
248
+ activeSessionId: null,
249
+ account: null,
250
+ pinnedAccountId: null,
251
+ isLoading: false,
252
+ error: null,
253
+ storageReady: false,
254
+ tokenReady: true,
255
+ hasAccessToken: Boolean(oxyServices.getAccessToken()),
256
+ authResolved: false,
257
+ switching: false,
258
+ };
259
+
260
+ const listeners = new Set<() => void>();
261
+ let snapshot = buildSnapshot();
262
+ /**
263
+ * Depth of the current transition. Nested mutations publish ONCE, at the
264
+ * outermost boundary — a projection that sets sessions, the active id and the
265
+ * account must not wake React three times with two of them half-applied.
266
+ */
267
+ let depth = 0;
268
+ let dirty = false;
269
+ /** The subject as it stood when the OUTERMOST transition opened. */
270
+ let subjectAtEntry: string | null = null;
271
+
272
+ function buildSnapshot(): OxyRuntimeSnapshot {
273
+ const activeContext = resolveActiveContext(facts.directory);
274
+ return Object.freeze({
275
+ status: deriveStatus(facts),
276
+ device: facts.directory,
277
+ deviceState: facts.deviceState,
278
+ activeContext,
279
+ principal: derivePrincipal(activeContext, facts.profiles, facts.account),
280
+ account: facts.account,
281
+ tokenStatus: deriveTokenStatus(facts),
282
+ authResolved: facts.authResolved,
283
+ switching: facts.switching,
284
+ error: facts.error === null ? null : { message: facts.error, code: 'oxy_runtime_error' },
285
+ sessions: facts.sessions,
286
+ activeSessionId: facts.activeSessionId,
287
+ isLoading: facts.isLoading,
288
+ storageReady: facts.storageReady,
289
+ hasAccessToken: facts.hasAccessToken,
290
+ tokenReady: facts.tokenReady,
291
+ });
292
+ }
293
+
294
+ function notify(): void {
295
+ for (const listener of [...listeners]) {
296
+ try {
297
+ listener();
298
+ } catch (error) {
299
+ logger('An OxyRuntime subscriber threw', error);
300
+ }
301
+ }
302
+ }
303
+
304
+ /**
305
+ * The ONE ordered transition. Every fact change goes through here, so the
306
+ * sequence ADR 0002 mandates holds for a local switch, a socket push, a cold
307
+ * boot and a cross-tab wake alike:
308
+ *
309
+ * the bearer is already committed (SessionClient awaits its mint before it
310
+ * calls us) → reset account-scoped caches → publish the snapshot → notify.
311
+ *
312
+ * Publishing before the reset would let a woken component read a cache entry
313
+ * belonging to the previous subject while holding the new subject's bearer,
314
+ * which is the account-switch race in its quietest form: no error, just the
315
+ * wrong person's data.
316
+ */
317
+ function transition(mutate: () => void): void {
318
+ if (depth === 0) {
319
+ subjectAtEntry = facts.account?.id ?? null;
320
+ }
321
+ depth += 1;
322
+ try {
323
+ mutate();
324
+ } finally {
325
+ depth -= 1;
326
+ }
327
+ if (depth > 0) {
328
+ // A nested transition publishes nothing and fires no reset: the subject
329
+ // is compared once, at the outermost boundary, so a projection that
330
+ // touches the account twice cannot reset the caches twice.
331
+ return;
332
+ }
333
+ const nextSubject = facts.account?.id ?? null;
334
+ if (nextSubject !== subjectAtEntry) {
335
+ dirty = true;
336
+ onSubjectChange?.({ previous: subjectAtEntry, next: nextSubject });
337
+ }
338
+ if (!dirty) {
339
+ return;
340
+ }
341
+ dirty = false;
342
+ snapshot = buildSnapshot();
343
+ notify();
344
+ }
345
+
346
+ /** Mark the snapshot stale from inside a transition. */
347
+ function touch(): void {
348
+ dirty = true;
349
+ }
350
+
351
+ function setSessions(next: ClientSession[]): void {
352
+ if (sessionsArraysEqual(facts.sessions, next)) {
353
+ return;
354
+ }
355
+ facts.sessions = next;
356
+ touch();
357
+ }
358
+
359
+ function setActiveSessionIdFact(sessionId: string | null): void {
360
+ if (facts.activeSessionId === sessionId) {
361
+ return;
362
+ }
363
+ facts.activeSessionId = sessionId;
364
+ touch();
365
+ }
366
+
367
+ /**
368
+ * The one place a `User` becomes THE subject — and the one place identity is
369
+ * normalised, so a profile arriving as `_id` from one lane and `id` from
370
+ * another can never produce two accounts that are the same person.
371
+ */
372
+ function setAccountFact(user: User | null): void {
373
+ if (user === null) {
374
+ if (facts.account === null) {
375
+ return;
376
+ }
377
+ facts.account = null;
378
+ touch();
379
+ return;
380
+ }
381
+ if (facts.account === user) {
382
+ return;
383
+ }
384
+ facts.account = normalizeUserIdentity(user);
385
+ touch();
386
+ }
387
+
388
+ // ── Projection ───────────────────────────────────────────────────────────
389
+
390
+ async function reconcileFromClient(): Promise<void> {
391
+ const state = sessionClient.getState();
392
+ if (state === null) {
393
+ return;
394
+ }
395
+ if (state.accounts.length === 0) {
396
+ sessionClientHost.setCurrentAccountId(null);
397
+ await onDeviceEmpty();
398
+ return;
399
+ }
400
+ // Capture the revision this projection is for, then bail after the async
401
+ // profile fetch if a fresher state has landed — last-writer-wins, so two
402
+ // overlapping projections can never publish in the wrong order.
403
+ const capturedRevision = state.revision;
404
+ // Resolve the pin BEFORE the fetch (memoised: one storage + keychain read
405
+ // per boot) so the first pass over a freshly applied state is already bound.
406
+ // Deferring it would let a sibling app's switch render once as this
407
+ // client's user before the pin corrected it.
408
+ const pinnedAccountId = identity ? await identity.ensurePinnedAccountId() : null;
409
+ const ids = accountIdsOf(state);
410
+ let users: User[] = [];
411
+ try {
412
+ users = ids.length > 0 ? await oxyServices.getUsersByIds(ids) : [];
413
+ } catch (fetchError) {
414
+ logger('Failed to resolve account profiles during the device projection', fetchError);
415
+ return;
416
+ }
417
+ const latest = sessionClient.getState();
418
+ if (!latest || latest.revision !== capturedRevision) {
419
+ return;
420
+ }
421
+ if (identity && (pinnedAccountId === null || activeSessionIdOf(latest, pinnedAccountId) === null)) {
422
+ // Either no verified pin, or the pinned account left this device's session
423
+ // set. Leave the current projection untouched and re-derive from the key.
424
+ onIdentityUnbound?.(identity, latest.revision);
425
+ return;
426
+ }
427
+ const directory = sessionClient.getDirectory();
428
+ transition(() => {
429
+ facts.pinnedAccountId = pinnedAccountId;
430
+ if (facts.deviceState !== latest) {
431
+ facts.deviceState = latest;
432
+ touch();
433
+ }
434
+ if (facts.directory !== directory) {
435
+ facts.directory = directory;
436
+ touch();
437
+ }
438
+ for (const resolved of users) {
439
+ facts.profiles.set(resolved.id, resolved);
440
+ }
441
+ const projected = normalizeAndSortSessions(
442
+ deviceStateToClientSessions(latest, facts.profiles, pinnedAccountId),
443
+ facts.activeSessionId,
444
+ false,
445
+ );
446
+ setSessions(projected);
447
+ onSessionsProjected?.(projected.map((session) => session.sessionId));
448
+ setActiveSessionIdFact(activeSessionIdOf(latest, pinnedAccountId));
449
+ const activeUser = activeUserOf(latest, facts.profiles, pinnedAccountId);
450
+ if (activeUser) {
451
+ setAccountFact(activeUser);
452
+ }
453
+ });
454
+ // The host's current account feeds the socket handshake and the bearer's
455
+ // token-account comparisons, so it tracks the PIN — never the account
456
+ // another app switched the device to.
457
+ sessionClientHost.setCurrentAccountId(pinnedAccountId ?? latest.activeAccountId);
458
+ }
459
+
460
+ return {
461
+ getSnapshot: () => snapshot,
462
+
463
+ subscribe(listener) {
464
+ listeners.add(listener);
465
+ return () => {
466
+ listeners.delete(listener);
467
+ };
468
+ },
469
+
470
+ start() {
471
+ return sessionClient.subscribe(() => {
472
+ void reconcileFromClient().catch((error) => {
473
+ logger('Failed to project a device-state transition', error);
474
+ });
475
+ });
476
+ },
477
+
478
+ reconcileFromClient,
479
+
480
+ batch(mutate) {
481
+ transition(mutate);
482
+ },
483
+
484
+ async activateContext(contextId) {
485
+ // `switching` is published BEFORE the request so a switcher can disable
486
+ // its rows for the whole round trip, and cleared in `finally` so a
487
+ // refused activation does not strand it on.
488
+ this.setSwitching(true);
489
+ try {
490
+ await sessionClient.activateContext(contextId);
491
+ await reconcileFromClient();
492
+ } finally {
493
+ this.setSwitching(false);
494
+ }
495
+ },
496
+
497
+ async signOutContext(contextId) {
498
+ await sessionClient.signOutContext(contextId);
499
+ await reconcileFromClient();
500
+ },
501
+
502
+ async signOutPrincipal(principalId) {
503
+ await sessionClient.signOutPrincipal(principalId);
504
+ await reconcileFromClient();
505
+ },
506
+
507
+ async refreshDirectory() {
508
+ await sessionClient.refreshDirectory();
509
+ const directory = sessionClient.getDirectory();
510
+ transition(() => {
511
+ if (facts.directory === directory) {
512
+ return;
513
+ }
514
+ facts.directory = directory;
515
+ touch();
516
+ });
517
+ },
518
+
519
+ setAccount(user) {
520
+ transition(() => {
521
+ setAccountFact(user);
522
+ if (facts.isLoading) {
523
+ facts.isLoading = false;
524
+ touch();
525
+ }
526
+ if (facts.error !== null) {
527
+ facts.error = null;
528
+ touch();
529
+ }
530
+ facts.profiles.set(user.id, user);
531
+ });
532
+ },
533
+
534
+ setLoading(loading) {
535
+ transition(() => {
536
+ if (facts.isLoading === loading) return;
537
+ facts.isLoading = loading;
538
+ touch();
539
+ });
540
+ },
541
+
542
+ setError(error) {
543
+ transition(() => {
544
+ if (facts.error === error) return;
545
+ facts.error = error;
546
+ touch();
547
+ });
548
+ },
549
+
550
+ setStorageReady(ready) {
551
+ transition(() => {
552
+ if (facts.storageReady === ready) return;
553
+ facts.storageReady = ready;
554
+ touch();
555
+ });
556
+ },
557
+
558
+ setTokenReady(ready) {
559
+ transition(() => {
560
+ if (facts.tokenReady === ready) return;
561
+ facts.tokenReady = ready;
562
+ touch();
563
+ });
564
+ },
565
+
566
+ setHasAccessToken(has) {
567
+ transition(() => {
568
+ if (facts.hasAccessToken === has) return;
569
+ facts.hasAccessToken = has;
570
+ touch();
571
+ });
572
+ },
573
+
574
+ setSwitching(switching) {
575
+ transition(() => {
576
+ if (facts.switching === switching) return;
577
+ facts.switching = switching;
578
+ touch();
579
+ });
580
+ },
581
+
582
+ markAuthResolved() {
583
+ transition(() => {
584
+ if (facts.authResolved) return;
585
+ facts.authResolved = true;
586
+ facts.tokenReady = true;
587
+ touch();
588
+ });
589
+ },
590
+
591
+ mergeSessions(incoming, options = {}) {
592
+ transition(() => {
593
+ const processed = options.merge
594
+ ? mergeSessionLists(facts.sessions, incoming, facts.activeSessionId, false)
595
+ : normalizeAndSortSessions(incoming, facts.activeSessionId, false);
596
+ onSessionsProjected?.([
597
+ ...processed.map((session) => session.sessionId),
598
+ ...(options.preserveSessionIds ?? []),
599
+ ]);
600
+ setSessions(processed);
601
+ });
602
+ },
603
+
604
+ setActiveSessionId(sessionId) {
605
+ transition(() => {
606
+ setActiveSessionIdFact(sessionId);
607
+ });
608
+ },
609
+
610
+ clearSession() {
611
+ transition(() => {
612
+ setSessions(EMPTY_SESSIONS);
613
+ setActiveSessionIdFact(null);
614
+ setAccountFact(null);
615
+ facts.deviceState = null;
616
+ facts.directory = null;
617
+ facts.profiles = new Map();
618
+ facts.pinnedAccountId = null;
619
+ touch();
620
+ });
621
+ },
622
+ };
623
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The headless session runtime (ADR 0004).
3
+ *
4
+ * `createOxyRuntime` owns the session facts and the order in which they become
5
+ * visible; `useRuntimeSnapshot` / `useRuntimeSelector` are the React adapter
6
+ * over it. Nothing here imports React except the adapter, and nothing in the
7
+ * runtime reads React state — which is what lets the provider hold ONE stable
8
+ * reference and stop rebuilding a 58-field context value on every keystroke.
9
+ */
10
+ export { createOxyRuntime } from './createOxyRuntime';
11
+ export type {
12
+ OxyRuntime,
13
+ OxyRuntimeConfig,
14
+ RuntimeClient,
15
+ RuntimeSessionClient,
16
+ RuntimeSessionHost,
17
+ SubjectTransition,
18
+ } from './createOxyRuntime';
19
+ export { useRuntimeSnapshot, useRuntimeSelector } from './useRuntimeSnapshot';
20
+ export {
21
+ OxyRuntimeHandleProvider,
22
+ OxyRuntimeMissingError,
23
+ useActiveAccount,
24
+ useDeviceDirectory,
25
+ useOxyRuntime,
26
+ useOxySnapshot,
27
+ } from './runtimeContext';
28
+ export type { ActiveAccount } from './runtimeContext';
29
+ export type {
30
+ OxyRuntimeError,
31
+ OxyRuntimeSnapshot,
32
+ OxyRuntimeStatus,
33
+ OxyTokenStatus,
34
+ } from './types';