@oxy.so/core 1.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 (673) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +221 -0
  4. package/dist/cjs/.tsbuildinfo +1 -0
  5. package/dist/cjs/HttpService.js +1270 -0
  6. package/dist/cjs/OxyServices.base.js +390 -0
  7. package/dist/cjs/OxyServices.errors.js +95 -0
  8. package/dist/cjs/OxyServices.js +72 -0
  9. package/dist/cjs/boot/sessionColdBoot.js +395 -0
  10. package/dist/cjs/constants/version.js +16 -0
  11. package/dist/cjs/crypto/aead.js +79 -0
  12. package/dist/cjs/crypto/ecdh.js +35 -0
  13. package/dist/cjs/crypto/identityMarker.js +255 -0
  14. package/dist/cjs/crypto/kdf.js +39 -0
  15. package/dist/cjs/crypto/keyManager.js +2234 -0
  16. package/dist/cjs/crypto/polyfill.js +131 -0
  17. package/dist/cjs/crypto/recoveryPhrase.js +242 -0
  18. package/dist/cjs/crypto/signatureService.js +273 -0
  19. package/dist/cjs/i18n/accountCategoryLabels.js +44 -0
  20. package/dist/cjs/i18n/accountRoleLabels.js +27 -0
  21. package/dist/cjs/i18n/index.js +90 -0
  22. package/dist/cjs/i18n/locales/ar-SA.json +282 -0
  23. package/dist/cjs/i18n/locales/ca-ES.json +282 -0
  24. package/dist/cjs/i18n/locales/de-DE.json +282 -0
  25. package/dist/cjs/i18n/locales/en-US.json +2161 -0
  26. package/dist/cjs/i18n/locales/es-ES.json +2161 -0
  27. package/dist/cjs/i18n/locales/fr-FR.json +282 -0
  28. package/dist/cjs/i18n/locales/it-IT.json +282 -0
  29. package/dist/cjs/i18n/locales/ja-JP.json +282 -0
  30. package/dist/cjs/i18n/locales/ko-KR.json +282 -0
  31. package/dist/cjs/i18n/locales/locales/ar-SA.json +282 -0
  32. package/dist/cjs/i18n/locales/locales/ca-ES.json +282 -0
  33. package/dist/cjs/i18n/locales/locales/de-DE.json +282 -0
  34. package/dist/cjs/i18n/locales/locales/en-US.json +2161 -0
  35. package/dist/cjs/i18n/locales/locales/es-ES.json +2161 -0
  36. package/dist/cjs/i18n/locales/locales/fr-FR.json +282 -0
  37. package/dist/cjs/i18n/locales/locales/it-IT.json +282 -0
  38. package/dist/cjs/i18n/locales/locales/ja-JP.json +282 -0
  39. package/dist/cjs/i18n/locales/locales/ko-KR.json +282 -0
  40. package/dist/cjs/i18n/locales/locales/pt-PT.json +282 -0
  41. package/dist/cjs/i18n/locales/locales/zh-CN.json +282 -0
  42. package/dist/cjs/i18n/locales/pt-PT.json +282 -0
  43. package/dist/cjs/i18n/locales/zh-CN.json +282 -0
  44. package/dist/cjs/i18n/reputationCategoryLabels.js +20 -0
  45. package/dist/cjs/i18n/trustTierLabels.js +19 -0
  46. package/dist/cjs/index.js +498 -0
  47. package/dist/cjs/inference/OxyInferenceClient.js +544 -0
  48. package/dist/cjs/logger/index.js +149 -0
  49. package/dist/cjs/mixins/OxyServices.accounts.js +555 -0
  50. package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
  51. package/dist/cjs/mixins/OxyServices.appData.js +108 -0
  52. package/dist/cjs/mixins/OxyServices.assets.js +764 -0
  53. package/dist/cjs/mixins/OxyServices.auth.js +1254 -0
  54. package/dist/cjs/mixins/OxyServices.chains.js +73 -0
  55. package/dist/cjs/mixins/OxyServices.civic.js +611 -0
  56. package/dist/cjs/mixins/OxyServices.connectedApps.js +78 -0
  57. package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
  58. package/dist/cjs/mixins/OxyServices.deviceBoot.js +191 -0
  59. package/dist/cjs/mixins/OxyServices.deviceTransfer.js +314 -0
  60. package/dist/cjs/mixins/OxyServices.devices.js +116 -0
  61. package/dist/cjs/mixins/OxyServices.features.js +335 -0
  62. package/dist/cjs/mixins/OxyServices.followGraph.js +221 -0
  63. package/dist/cjs/mixins/OxyServices.identity.js +461 -0
  64. package/dist/cjs/mixins/OxyServices.identityBackup.js +172 -0
  65. package/dist/cjs/mixins/OxyServices.inference.js +59 -0
  66. package/dist/cjs/mixins/OxyServices.language.js +125 -0
  67. package/dist/cjs/mixins/OxyServices.links.js +72 -0
  68. package/dist/cjs/mixins/OxyServices.location.js +43 -0
  69. package/dist/cjs/mixins/OxyServices.nodes.js +175 -0
  70. package/dist/cjs/mixins/OxyServices.notifications.js +63 -0
  71. package/dist/cjs/mixins/OxyServices.payment.js +158 -0
  72. package/dist/cjs/mixins/OxyServices.privacy.js +199 -0
  73. package/dist/cjs/mixins/OxyServices.reputation.js +289 -0
  74. package/dist/cjs/mixins/OxyServices.security.js +89 -0
  75. package/dist/cjs/mixins/OxyServices.store.js +266 -0
  76. package/dist/cjs/mixins/OxyServices.topics.js +137 -0
  77. package/dist/cjs/mixins/OxyServices.user.js +902 -0
  78. package/dist/cjs/mixins/OxyServices.utility.js +1098 -0
  79. package/dist/cjs/mixins/index.js +135 -0
  80. package/dist/cjs/mixins/mixinHelpers.js +53 -0
  81. package/dist/cjs/models/Topic.js +16 -0
  82. package/dist/cjs/models/interfaces.js +20 -0
  83. package/dist/cjs/models/session.js +2 -0
  84. package/dist/cjs/server/auth.js +167 -0
  85. package/dist/cjs/server/capabilityTicket.js +192 -0
  86. package/dist/cjs/server/cors.js +225 -0
  87. package/dist/cjs/server/index.js +97 -0
  88. package/dist/cjs/server/rateLimit.js +207 -0
  89. package/dist/cjs/server/safeFetch.js +458 -0
  90. package/dist/cjs/server/securityHeaders.js +377 -0
  91. package/dist/cjs/server/userInvalidation.js +156 -0
  92. package/dist/cjs/server/verifySecret.js +50 -0
  93. package/dist/cjs/session/SessionClient.js +865 -0
  94. package/dist/cjs/session/accountDelegationAccess.js +48 -0
  95. package/dist/cjs/session/accountDialogController.js +1131 -0
  96. package/dist/cjs/session/accountSwitchTargets.js +81 -0
  97. package/dist/cjs/session/authStateStore.js +404 -0
  98. package/dist/cjs/session/createSessionClient.js +32 -0
  99. package/dist/cjs/session/deviceDirectory.js +143 -0
  100. package/dist/cjs/session/deviceSwitcherRows.js +76 -0
  101. package/dist/cjs/session/identityPin.js +256 -0
  102. package/dist/cjs/session/identitySession.js +135 -0
  103. package/dist/cjs/session/projectSessionState.js +121 -0
  104. package/dist/cjs/session/refresh.js +398 -0
  105. package/dist/cjs/session/sessionClientHost.js +34 -0
  106. package/dist/cjs/session/sharedDeviceCredential.js +247 -0
  107. package/dist/cjs/session/socketLoader.js +55 -0
  108. package/dist/cjs/shared/utils/colorUtils.js +153 -0
  109. package/dist/cjs/shared/utils/errorUtils.js +183 -0
  110. package/dist/cjs/shared/utils/index.js +49 -0
  111. package/dist/cjs/shared/utils/networkUtils.js +183 -0
  112. package/dist/cjs/shared/utils/themeUtils.js +106 -0
  113. package/dist/cjs/utils/accountCacheSweep.js +80 -0
  114. package/dist/cjs/utils/accountUtils.js +140 -0
  115. package/dist/cjs/utils/activityId.js +6 -0
  116. package/dist/cjs/utils/apiUtils.js +91 -0
  117. package/dist/cjs/utils/asyncUtils.js +222 -0
  118. package/dist/cjs/utils/authHelpers.js +120 -0
  119. package/dist/cjs/utils/authWebUrl.js +19 -0
  120. package/dist/cjs/utils/avatarUtils.js +29 -0
  121. package/dist/cjs/utils/cache.js +235 -0
  122. package/dist/cjs/utils/cacheKey.js +85 -0
  123. package/dist/cjs/utils/coldBoot.js +122 -0
  124. package/dist/cjs/utils/commonsApproval.js +31 -0
  125. package/dist/cjs/utils/commonsDelivery.js +59 -0
  126. package/dist/cjs/utils/deviceManager.js +174 -0
  127. package/dist/cjs/utils/displayNamePolicyRanges.generated.js +60 -0
  128. package/dist/cjs/utils/edgeRegion.js +6 -0
  129. package/dist/cjs/utils/errorUtils.js +243 -0
  130. package/dist/cjs/utils/identityCacheSweep.js +97 -0
  131. package/dist/cjs/utils/languageUtils.js +224 -0
  132. package/dist/cjs/utils/oauthPkce.js +311 -0
  133. package/dist/cjs/utils/officialOrigins.js +64 -0
  134. package/dist/cjs/utils/oxyServiceEnvironment.js +19 -0
  135. package/dist/cjs/utils/platform.js +119 -0
  136. package/dist/cjs/utils/profileLinks.js +63 -0
  137. package/dist/cjs/utils/redactUrl.js +29 -0
  138. package/dist/cjs/utils/registrableApex.js +45 -0
  139. package/dist/cjs/utils/requestUtils.js +209 -0
  140. package/dist/cjs/utils/sessionUtils.js +181 -0
  141. package/dist/cjs/utils/textNormalization.js +158 -0
  142. package/dist/cjs/utils/userHandle.js +37 -0
  143. package/dist/cjs/utils/userIdentity.js +50 -0
  144. package/dist/cjs/utils/validationUtils.js +370 -0
  145. package/dist/cjs/utils/webauthnOrigin.js +51 -0
  146. package/dist/esm/.tsbuildinfo +1 -0
  147. package/dist/esm/HttpService.js +1266 -0
  148. package/dist/esm/OxyServices.base.js +386 -0
  149. package/dist/esm/OxyServices.errors.js +88 -0
  150. package/dist/esm/OxyServices.js +66 -0
  151. package/dist/esm/boot/sessionColdBoot.js +392 -0
  152. package/dist/esm/constants/version.js +13 -0
  153. package/dist/esm/crypto/aead.js +74 -0
  154. package/dist/esm/crypto/ecdh.js +32 -0
  155. package/dist/esm/crypto/identityMarker.js +248 -0
  156. package/dist/esm/crypto/kdf.js +36 -0
  157. package/dist/esm/crypto/keyManager.js +2227 -0
  158. package/dist/esm/crypto/polyfill.js +129 -0
  159. package/dist/esm/crypto/recoveryPhrase.js +238 -0
  160. package/dist/esm/crypto/signatureService.js +269 -0
  161. package/dist/esm/i18n/accountCategoryLabels.js +37 -0
  162. package/dist/esm/i18n/accountRoleLabels.js +20 -0
  163. package/dist/esm/i18n/index.js +83 -0
  164. package/dist/esm/i18n/locales/ar-SA.json +282 -0
  165. package/dist/esm/i18n/locales/ca-ES.json +282 -0
  166. package/dist/esm/i18n/locales/de-DE.json +282 -0
  167. package/dist/esm/i18n/locales/en-US.json +2161 -0
  168. package/dist/esm/i18n/locales/es-ES.json +2161 -0
  169. package/dist/esm/i18n/locales/fr-FR.json +282 -0
  170. package/dist/esm/i18n/locales/it-IT.json +282 -0
  171. package/dist/esm/i18n/locales/ja-JP.json +282 -0
  172. package/dist/esm/i18n/locales/ko-KR.json +282 -0
  173. package/dist/esm/i18n/locales/locales/ar-SA.json +282 -0
  174. package/dist/esm/i18n/locales/locales/ca-ES.json +282 -0
  175. package/dist/esm/i18n/locales/locales/de-DE.json +282 -0
  176. package/dist/esm/i18n/locales/locales/en-US.json +2161 -0
  177. package/dist/esm/i18n/locales/locales/es-ES.json +2161 -0
  178. package/dist/esm/i18n/locales/locales/fr-FR.json +282 -0
  179. package/dist/esm/i18n/locales/locales/it-IT.json +282 -0
  180. package/dist/esm/i18n/locales/locales/ja-JP.json +282 -0
  181. package/dist/esm/i18n/locales/locales/ko-KR.json +282 -0
  182. package/dist/esm/i18n/locales/locales/pt-PT.json +282 -0
  183. package/dist/esm/i18n/locales/locales/zh-CN.json +282 -0
  184. package/dist/esm/i18n/locales/pt-PT.json +282 -0
  185. package/dist/esm/i18n/locales/zh-CN.json +282 -0
  186. package/dist/esm/i18n/reputationCategoryLabels.js +13 -0
  187. package/dist/esm/i18n/trustTierLabels.js +12 -0
  188. package/dist/esm/index.js +254 -0
  189. package/dist/esm/inference/OxyInferenceClient.js +538 -0
  190. package/dist/esm/logger/index.js +140 -0
  191. package/dist/esm/mixins/OxyServices.accounts.js +543 -0
  192. package/dist/esm/mixins/OxyServices.analytics.js +46 -0
  193. package/dist/esm/mixins/OxyServices.appData.js +103 -0
  194. package/dist/esm/mixins/OxyServices.assets.js +761 -0
  195. package/dist/esm/mixins/OxyServices.auth.js +1247 -0
  196. package/dist/esm/mixins/OxyServices.chains.js +70 -0
  197. package/dist/esm/mixins/OxyServices.civic.js +605 -0
  198. package/dist/esm/mixins/OxyServices.connectedApps.js +75 -0
  199. package/dist/esm/mixins/OxyServices.contacts.js +47 -0
  200. package/dist/esm/mixins/OxyServices.deviceBoot.js +186 -0
  201. package/dist/esm/mixins/OxyServices.deviceTransfer.js +311 -0
  202. package/dist/esm/mixins/OxyServices.devices.js +113 -0
  203. package/dist/esm/mixins/OxyServices.features.js +332 -0
  204. package/dist/esm/mixins/OxyServices.followGraph.js +218 -0
  205. package/dist/esm/mixins/OxyServices.identity.js +457 -0
  206. package/dist/esm/mixins/OxyServices.identityBackup.js +169 -0
  207. package/dist/esm/mixins/OxyServices.inference.js +56 -0
  208. package/dist/esm/mixins/OxyServices.language.js +122 -0
  209. package/dist/esm/mixins/OxyServices.links.js +69 -0
  210. package/dist/esm/mixins/OxyServices.location.js +40 -0
  211. package/dist/esm/mixins/OxyServices.nodes.js +172 -0
  212. package/dist/esm/mixins/OxyServices.notifications.js +60 -0
  213. package/dist/esm/mixins/OxyServices.payment.js +155 -0
  214. package/dist/esm/mixins/OxyServices.privacy.js +196 -0
  215. package/dist/esm/mixins/OxyServices.reputation.js +286 -0
  216. package/dist/esm/mixins/OxyServices.security.js +86 -0
  217. package/dist/esm/mixins/OxyServices.store.js +263 -0
  218. package/dist/esm/mixins/OxyServices.topics.js +134 -0
  219. package/dist/esm/mixins/OxyServices.user.js +899 -0
  220. package/dist/esm/mixins/OxyServices.utility.js +1095 -0
  221. package/dist/esm/mixins/index.js +132 -0
  222. package/dist/esm/mixins/mixinHelpers.js +48 -0
  223. package/dist/esm/models/Topic.js +13 -0
  224. package/dist/esm/models/interfaces.js +17 -0
  225. package/dist/esm/models/session.js +1 -0
  226. package/dist/esm/server/auth.js +154 -0
  227. package/dist/esm/server/capabilityTicket.js +183 -0
  228. package/dist/esm/server/cors.js +220 -0
  229. package/dist/esm/server/index.js +47 -0
  230. package/dist/esm/server/rateLimit.js +201 -0
  231. package/dist/esm/server/safeFetch.js +447 -0
  232. package/dist/esm/server/securityHeaders.js +364 -0
  233. package/dist/esm/server/userInvalidation.js +152 -0
  234. package/dist/esm/server/verifySecret.js +47 -0
  235. package/dist/esm/session/SessionClient.js +861 -0
  236. package/dist/esm/session/accountDelegationAccess.js +45 -0
  237. package/dist/esm/session/accountDialogController.js +1126 -0
  238. package/dist/esm/session/accountSwitchTargets.js +77 -0
  239. package/dist/esm/session/authStateStore.js +398 -0
  240. package/dist/esm/session/createSessionClient.js +29 -0
  241. package/dist/esm/session/deviceDirectory.js +135 -0
  242. package/dist/esm/session/deviceSwitcherRows.js +72 -0
  243. package/dist/esm/session/identityPin.js +249 -0
  244. package/dist/esm/session/identitySession.js +131 -0
  245. package/dist/esm/session/projectSessionState.js +114 -0
  246. package/dist/esm/session/refresh.js +390 -0
  247. package/dist/esm/session/sessionClientHost.js +31 -0
  248. package/dist/esm/session/sharedDeviceCredential.js +239 -0
  249. package/dist/esm/session/socketLoader.js +19 -0
  250. package/dist/esm/shared/utils/colorUtils.js +143 -0
  251. package/dist/esm/shared/utils/errorUtils.js +170 -0
  252. package/dist/esm/shared/utils/index.js +15 -0
  253. package/dist/esm/shared/utils/networkUtils.js +173 -0
  254. package/dist/esm/shared/utils/themeUtils.js +98 -0
  255. package/dist/esm/utils/accountCacheSweep.js +75 -0
  256. package/dist/esm/utils/accountUtils.js +131 -0
  257. package/dist/esm/utils/activityId.js +2 -0
  258. package/dist/esm/utils/apiUtils.js +84 -0
  259. package/dist/esm/utils/asyncUtils.js +207 -0
  260. package/dist/esm/utils/authHelpers.js +111 -0
  261. package/dist/esm/utils/authWebUrl.js +16 -0
  262. package/dist/esm/utils/avatarUtils.js +26 -0
  263. package/dist/esm/utils/cache.js +227 -0
  264. package/dist/esm/utils/cacheKey.js +80 -0
  265. package/dist/esm/utils/coldBoot.js +119 -0
  266. package/dist/esm/utils/commonsApproval.js +27 -0
  267. package/dist/esm/utils/commonsDelivery.js +54 -0
  268. package/dist/esm/utils/deviceManager.js +170 -0
  269. package/dist/esm/utils/displayNamePolicyRanges.generated.js +57 -0
  270. package/dist/esm/utils/edgeRegion.js +2 -0
  271. package/dist/esm/utils/errorUtils.js +232 -0
  272. package/dist/esm/utils/identityCacheSweep.js +92 -0
  273. package/dist/esm/utils/languageUtils.js +212 -0
  274. package/dist/esm/utils/oauthPkce.js +297 -0
  275. package/dist/esm/utils/officialOrigins.js +59 -0
  276. package/dist/esm/utils/oxyServiceEnvironment.js +16 -0
  277. package/dist/esm/utils/platform.js +110 -0
  278. package/dist/esm/utils/profileLinks.js +60 -0
  279. package/dist/esm/utils/redactUrl.js +26 -0
  280. package/dist/esm/utils/registrableApex.js +42 -0
  281. package/dist/esm/utils/requestUtils.js +203 -0
  282. package/dist/esm/utils/sessionUtils.js +171 -0
  283. package/dist/esm/utils/textNormalization.js +154 -0
  284. package/dist/esm/utils/userHandle.js +33 -0
  285. package/dist/esm/utils/userIdentity.js +45 -0
  286. package/dist/esm/utils/validationUtils.js +350 -0
  287. package/dist/esm/utils/webauthnOrigin.js +48 -0
  288. package/dist/types/.tsbuildinfo +1 -0
  289. package/dist/types/HttpService.d.ts +413 -0
  290. package/dist/types/OxyServices.base.d.ts +187 -0
  291. package/dist/types/OxyServices.d.ts +139 -0
  292. package/dist/types/OxyServices.errors.d.ts +84 -0
  293. package/dist/types/boot/sessionColdBoot.d.ts +140 -0
  294. package/dist/types/constants/version.d.ts +13 -0
  295. package/dist/types/crypto/aead.d.ts +56 -0
  296. package/dist/types/crypto/ecdh.d.ts +29 -0
  297. package/dist/types/crypto/identityMarker.d.ts +94 -0
  298. package/dist/types/crypto/kdf.d.ts +25 -0
  299. package/dist/types/crypto/keyManager.d.ts +582 -0
  300. package/dist/types/crypto/polyfill.d.ts +25 -0
  301. package/dist/types/crypto/recoveryPhrase.d.ts +173 -0
  302. package/dist/types/crypto/signatureService.d.ts +136 -0
  303. package/dist/types/i18n/accountCategoryLabels.d.ts +34 -0
  304. package/dist/types/i18n/accountRoleLabels.d.ts +10 -0
  305. package/dist/types/i18n/index.d.ts +3 -0
  306. package/dist/types/i18n/reputationCategoryLabels.d.ts +10 -0
  307. package/dist/types/i18n/trustTierLabels.d.ts +9 -0
  308. package/dist/types/index.d.ts +145 -0
  309. package/dist/types/inference/OxyInferenceClient.d.ts +342 -0
  310. package/dist/types/logger/index.d.ts +104 -0
  311. package/dist/types/mixins/OxyServices.accounts.d.ts +868 -0
  312. package/dist/types/mixins/OxyServices.analytics.d.ts +72 -0
  313. package/dist/types/mixins/OxyServices.appData.d.ts +108 -0
  314. package/dist/types/mixins/OxyServices.assets.d.ts +329 -0
  315. package/dist/types/mixins/OxyServices.auth.d.ts +908 -0
  316. package/dist/types/mixins/OxyServices.chains.d.ts +156 -0
  317. package/dist/types/mixins/OxyServices.civic.d.ts +510 -0
  318. package/dist/types/mixins/OxyServices.connectedApps.d.ts +169 -0
  319. package/dist/types/mixins/OxyServices.contacts.d.ts +100 -0
  320. package/dist/types/mixins/OxyServices.deviceBoot.d.ts +185 -0
  321. package/dist/types/mixins/OxyServices.deviceTransfer.d.ts +149 -0
  322. package/dist/types/mixins/OxyServices.devices.d.ts +103 -0
  323. package/dist/types/mixins/OxyServices.features.d.ts +250 -0
  324. package/dist/types/mixins/OxyServices.followGraph.d.ts +223 -0
  325. package/dist/types/mixins/OxyServices.identity.d.ts +351 -0
  326. package/dist/types/mixins/OxyServices.identityBackup.d.ts +129 -0
  327. package/dist/types/mixins/OxyServices.inference.d.ts +95 -0
  328. package/dist/types/mixins/OxyServices.language.d.ts +87 -0
  329. package/dist/types/mixins/OxyServices.links.d.ts +104 -0
  330. package/dist/types/mixins/OxyServices.location.d.ts +70 -0
  331. package/dist/types/mixins/OxyServices.nodes.d.ts +198 -0
  332. package/dist/types/mixins/OxyServices.notifications.d.ts +143 -0
  333. package/dist/types/mixins/OxyServices.payment.d.ts +117 -0
  334. package/dist/types/mixins/OxyServices.privacy.d.ts +146 -0
  335. package/dist/types/mixins/OxyServices.reputation.d.ts +202 -0
  336. package/dist/types/mixins/OxyServices.security.d.ts +84 -0
  337. package/dist/types/mixins/OxyServices.store.d.ts +334 -0
  338. package/dist/types/mixins/OxyServices.topics.d.ts +110 -0
  339. package/dist/types/mixins/OxyServices.user.d.ts +558 -0
  340. package/dist/types/mixins/OxyServices.utility.d.ts +393 -0
  341. package/dist/types/mixins/index.d.ts +87 -0
  342. package/dist/types/mixins/mixinHelpers.d.ts +31 -0
  343. package/dist/types/models/Topic.d.ts +42 -0
  344. package/dist/types/models/interfaces.d.ts +675 -0
  345. package/dist/types/models/session.d.ts +51 -0
  346. package/dist/types/server/auth.d.ts +130 -0
  347. package/dist/types/server/capabilityTicket.d.ts +41 -0
  348. package/dist/types/server/cors.d.ts +98 -0
  349. package/dist/types/server/index.d.ts +35 -0
  350. package/dist/types/server/rateLimit.d.ts +40 -0
  351. package/dist/types/server/safeFetch.d.ts +137 -0
  352. package/dist/types/server/securityHeaders.d.ts +221 -0
  353. package/dist/types/server/userInvalidation.d.ts +114 -0
  354. package/dist/types/server/verifySecret.d.ts +29 -0
  355. package/dist/types/session/SessionClient.d.ts +418 -0
  356. package/dist/types/session/accountDelegationAccess.d.ts +40 -0
  357. package/dist/types/session/accountDialogController.d.ts +648 -0
  358. package/dist/types/session/accountSwitchTargets.d.ts +70 -0
  359. package/dist/types/session/authStateStore.d.ts +153 -0
  360. package/dist/types/session/createSessionClient.d.ts +30 -0
  361. package/dist/types/session/deviceDirectory.d.ts +182 -0
  362. package/dist/types/session/deviceSwitcherRows.d.ts +92 -0
  363. package/dist/types/session/identityPin.d.ts +87 -0
  364. package/dist/types/session/identitySession.d.ts +105 -0
  365. package/dist/types/session/projectSessionState.d.ts +74 -0
  366. package/dist/types/session/refresh.d.ts +179 -0
  367. package/dist/types/session/sessionClientHost.d.ts +18 -0
  368. package/dist/types/session/sharedDeviceCredential.d.ts +202 -0
  369. package/dist/types/session/socketLoader.d.ts +11 -0
  370. package/dist/types/shared/utils/colorUtils.d.ts +104 -0
  371. package/dist/types/shared/utils/errorUtils.d.ts +97 -0
  372. package/dist/types/shared/utils/index.d.ts +13 -0
  373. package/dist/types/shared/utils/networkUtils.d.ts +139 -0
  374. package/dist/types/shared/utils/themeUtils.d.ts +90 -0
  375. package/dist/types/utils/accountCacheSweep.d.ts +75 -0
  376. package/dist/types/utils/accountUtils.d.ts +100 -0
  377. package/dist/types/utils/activityId.d.ts +2 -0
  378. package/dist/types/utils/apiUtils.d.ts +95 -0
  379. package/dist/types/utils/asyncUtils.d.ts +62 -0
  380. package/dist/types/utils/authHelpers.d.ts +58 -0
  381. package/dist/types/utils/authWebUrl.d.ts +16 -0
  382. package/dist/types/utils/avatarUtils.d.ts +16 -0
  383. package/dist/types/utils/cache.d.ts +130 -0
  384. package/dist/types/utils/cacheKey.d.ts +66 -0
  385. package/dist/types/utils/coldBoot.d.ts +131 -0
  386. package/dist/types/utils/commonsApproval.d.ts +13 -0
  387. package/dist/types/utils/commonsDelivery.d.ts +105 -0
  388. package/dist/types/utils/deviceManager.d.ts +65 -0
  389. package/dist/types/utils/displayNamePolicyRanges.generated.d.ts +57 -0
  390. package/dist/types/utils/edgeRegion.d.ts +2 -0
  391. package/dist/types/utils/errorUtils.d.ts +125 -0
  392. package/dist/types/utils/identityCacheSweep.d.ts +80 -0
  393. package/dist/types/utils/languageUtils.d.ts +100 -0
  394. package/dist/types/utils/oauthPkce.d.ts +157 -0
  395. package/dist/types/utils/officialOrigins.d.ts +13 -0
  396. package/dist/types/utils/oxyServiceEnvironment.d.ts +17 -0
  397. package/dist/types/utils/platform.d.ts +49 -0
  398. package/dist/types/utils/profileLinks.d.ts +44 -0
  399. package/dist/types/utils/redactUrl.d.ts +17 -0
  400. package/dist/types/utils/registrableApex.d.ts +27 -0
  401. package/dist/types/utils/requestUtils.d.ts +123 -0
  402. package/dist/types/utils/sessionUtils.d.ts +54 -0
  403. package/dist/types/utils/textNormalization.d.ts +103 -0
  404. package/dist/types/utils/userHandle.d.ts +26 -0
  405. package/dist/types/utils/userIdentity.d.ts +21 -0
  406. package/dist/types/utils/validationUtils.d.ts +268 -0
  407. package/dist/types/utils/webauthnOrigin.d.ts +32 -0
  408. package/package.json +168 -0
  409. package/src/HttpService.ts +1553 -0
  410. package/src/OxyServices.base.ts +478 -0
  411. package/src/OxyServices.errors.ts +122 -0
  412. package/src/OxyServices.ts +172 -0
  413. package/src/__tests__/authSocket.test.ts +96 -0
  414. package/src/__tests__/httpServiceActivityId.test.ts +49 -0
  415. package/src/__tests__/httpServiceAuthSelfAwait.test.ts +108 -0
  416. package/src/__tests__/httpServiceCache.test.ts +318 -0
  417. package/src/__tests__/httpServiceCsrf.test.ts +235 -0
  418. package/src/__tests__/httpServiceFormEncoded.test.ts +142 -0
  419. package/src/__tests__/httpServiceQueueDeadlock.test.ts +128 -0
  420. package/src/__tests__/httpServiceUnwrapEnvelope.test.ts +115 -0
  421. package/src/__tests__/inSessionRefresh.test.ts +260 -0
  422. package/src/__tests__/linkedClient.test.ts +370 -0
  423. package/src/__tests__/parseHttpErrorBody.test.ts +116 -0
  424. package/src/__tests__/serverValueImportsDeclared.test.ts +120 -0
  425. package/src/__tests__/userIdentity.test.ts +143 -0
  426. package/src/boot/__tests__/sessionColdBoot.identity.test.ts +393 -0
  427. package/src/boot/__tests__/sessionColdBoot.sharedDevice.test.ts +325 -0
  428. package/src/boot/__tests__/sessionColdBoot.test.ts +570 -0
  429. package/src/boot/sessionColdBoot.ts +529 -0
  430. package/src/constants/version.ts +15 -0
  431. package/src/crypto/__tests__/backupMaterial.test.ts +86 -0
  432. package/src/crypto/__tests__/cryptoPrimitives.test.ts +226 -0
  433. package/src/crypto/__tests__/identityMocks.ts +125 -0
  434. package/src/crypto/__tests__/keyManager.atomicity.test.ts +272 -0
  435. package/src/crypto/__tests__/keyManager.cacheSafety.test.ts +175 -0
  436. package/src/crypto/__tests__/keyManager.identityStatus.test.ts +217 -0
  437. package/src/crypto/__tests__/keyManager.recoveryLadder.test.ts +179 -0
  438. package/src/crypto/__tests__/keyManager.recoveryMnemonic.test.ts +138 -0
  439. package/src/crypto/__tests__/keyManager.storageMigration.test.ts +227 -0
  440. package/src/crypto/__tests__/keyManager.test.ts +329 -0
  441. package/src/crypto/__tests__/polyfill.test.ts +109 -0
  442. package/src/crypto/__tests__/recoveryPhrase.test.ts +61 -0
  443. package/src/crypto/__tests__/scopedSeed.test.ts +126 -0
  444. package/src/crypto/__tests__/signChallengeShared.test.ts +63 -0
  445. package/src/crypto/__tests__/signedRecord.test.ts +230 -0
  446. package/src/crypto/aead.ts +97 -0
  447. package/src/crypto/ecdh.ts +37 -0
  448. package/src/crypto/identityMarker.ts +291 -0
  449. package/src/crypto/kdf.ts +43 -0
  450. package/src/crypto/keyManager.ts +2607 -0
  451. package/src/crypto/polyfill.ts +143 -0
  452. package/src/crypto/recoveryPhrase.ts +337 -0
  453. package/src/crypto/signatureService.ts +350 -0
  454. package/src/i18n/__tests__/accountCategoryLabels.test.ts +62 -0
  455. package/src/i18n/__tests__/accountRoleLabels.test.ts +54 -0
  456. package/src/i18n/__tests__/reputationCategoryLabels.test.ts +56 -0
  457. package/src/i18n/__tests__/trustTierLabels.test.ts +47 -0
  458. package/src/i18n/accountCategoryLabels.ts +44 -0
  459. package/src/i18n/accountRoleLabels.ts +26 -0
  460. package/src/i18n/index.ts +88 -0
  461. package/src/i18n/locales/ar-SA.json +282 -0
  462. package/src/i18n/locales/ca-ES.json +282 -0
  463. package/src/i18n/locales/de-DE.json +282 -0
  464. package/src/i18n/locales/en-US.json +2161 -0
  465. package/src/i18n/locales/es-ES.json +2161 -0
  466. package/src/i18n/locales/fr-FR.json +282 -0
  467. package/src/i18n/locales/it-IT.json +282 -0
  468. package/src/i18n/locales/ja-JP.json +282 -0
  469. package/src/i18n/locales/ko-KR.json +282 -0
  470. package/src/i18n/locales/pt-PT.json +282 -0
  471. package/src/i18n/locales/zh-CN.json +282 -0
  472. package/src/i18n/reputationCategoryLabels.ts +20 -0
  473. package/src/i18n/trustTierLabels.ts +18 -0
  474. package/src/index.ts +881 -0
  475. package/src/inference/OxyInferenceClient.ts +888 -0
  476. package/src/inference/__tests__/OxyInferenceClient.test.ts +834 -0
  477. package/src/logger/__tests__/logger.test.ts +207 -0
  478. package/src/logger/index.ts +217 -0
  479. package/src/mixins/OxyServices.accounts.ts +1330 -0
  480. package/src/mixins/OxyServices.analytics.ts +53 -0
  481. package/src/mixins/OxyServices.appData.ts +158 -0
  482. package/src/mixins/OxyServices.assets.ts +861 -0
  483. package/src/mixins/OxyServices.auth.ts +1858 -0
  484. package/src/mixins/OxyServices.chains.ts +134 -0
  485. package/src/mixins/OxyServices.civic.ts +945 -0
  486. package/src/mixins/OxyServices.connectedApps.ts +174 -0
  487. package/src/mixins/OxyServices.contacts.ts +73 -0
  488. package/src/mixins/OxyServices.deviceBoot.ts +210 -0
  489. package/src/mixins/OxyServices.deviceTransfer.ts +393 -0
  490. package/src/mixins/OxyServices.devices.ts +120 -0
  491. package/src/mixins/OxyServices.features.ts +454 -0
  492. package/src/mixins/OxyServices.followGraph.ts +293 -0
  493. package/src/mixins/OxyServices.identity.ts +705 -0
  494. package/src/mixins/OxyServices.identityBackup.ts +250 -0
  495. package/src/mixins/OxyServices.inference.ts +57 -0
  496. package/src/mixins/OxyServices.language.ts +144 -0
  497. package/src/mixins/OxyServices.links.ts +107 -0
  498. package/src/mixins/OxyServices.location.ts +46 -0
  499. package/src/mixins/OxyServices.nodes.ts +337 -0
  500. package/src/mixins/OxyServices.notifications.ts +142 -0
  501. package/src/mixins/OxyServices.payment.ts +163 -0
  502. package/src/mixins/OxyServices.privacy.ts +219 -0
  503. package/src/mixins/OxyServices.reputation.ts +436 -0
  504. package/src/mixins/OxyServices.security.ts +110 -0
  505. package/src/mixins/OxyServices.store.ts +585 -0
  506. package/src/mixins/OxyServices.topics.ts +169 -0
  507. package/src/mixins/OxyServices.user.ts +1137 -0
  508. package/src/mixins/OxyServices.utility.ts +1370 -0
  509. package/src/mixins/__tests__/OxyServices.civic.test.ts +1095 -0
  510. package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +124 -0
  511. package/src/mixins/__tests__/OxyServices.deviceTransfer.test.ts +271 -0
  512. package/src/mixins/__tests__/OxyServices.identity.test.ts +376 -0
  513. package/src/mixins/__tests__/OxyServices.links.test.ts +154 -0
  514. package/src/mixins/__tests__/OxyServices.nodes.test.ts +341 -0
  515. package/src/mixins/__tests__/OxyServices.rotateKey.test.ts +276 -0
  516. package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +192 -0
  517. package/src/mixins/__tests__/OxyServices.serviceAssetMetadataBySha.test.ts +135 -0
  518. package/src/mixins/__tests__/accounts.test.ts +727 -0
  519. package/src/mixins/__tests__/appData.test.ts +203 -0
  520. package/src/mixins/__tests__/assetCredentials.test.ts +47 -0
  521. package/src/mixins/__tests__/assetUpload.test.ts +191 -0
  522. package/src/mixins/__tests__/backgroundCredential.httpIntegration.test.ts +135 -0
  523. package/src/mixins/__tests__/chains.test.ts +113 -0
  524. package/src/mixins/__tests__/commonsSignIn.test.ts +1144 -0
  525. package/src/mixins/__tests__/connectedApps.test.ts +123 -0
  526. package/src/mixins/__tests__/deviceTokenMintPinned.test.ts +113 -0
  527. package/src/mixins/__tests__/discoveryErrorHandling.test.ts +266 -0
  528. package/src/mixins/__tests__/followCacheInvalidation.test.ts +173 -0
  529. package/src/mixins/__tests__/followGraph.test.ts +147 -0
  530. package/src/mixins/__tests__/followGraphPagination.test.ts +271 -0
  531. package/src/mixins/__tests__/getFileDownloadUrl.test.ts +346 -0
  532. package/src/mixins/__tests__/getFollowStatuses.test.ts +95 -0
  533. package/src/mixins/__tests__/getUsersByIds.test.ts +149 -0
  534. package/src/mixins/__tests__/identityBackup.test.ts +315 -0
  535. package/src/mixins/__tests__/identityWriteCacheInvalidation.test.ts +407 -0
  536. package/src/mixins/__tests__/inferenceFactory.test.ts +58 -0
  537. package/src/mixins/__tests__/onTokensChanged.test.ts +129 -0
  538. package/src/mixins/__tests__/preSessionSkipAuth.test.ts +284 -0
  539. package/src/mixins/__tests__/privacyCacheInvalidation.test.ts +155 -0
  540. package/src/mixins/__tests__/reputation.test.ts +522 -0
  541. package/src/mixins/__tests__/serviceAuth.test.ts +832 -0
  542. package/src/mixins/__tests__/serviceTokenJwks.test.ts +208 -0
  543. package/src/mixins/__tests__/store.test.ts +304 -0
  544. package/src/mixins/__tests__/topics.test.ts +156 -0
  545. package/src/mixins/__tests__/userReadCacheBypass.test.ts +129 -0
  546. package/src/mixins/__tests__/userTokenAuth.test.ts +746 -0
  547. package/src/mixins/__tests__/verifyChallenge.test.ts +130 -0
  548. package/src/mixins/__tests__/verifyServiceActingAs.test.ts +167 -0
  549. package/src/mixins/__tests__/webauthnAuth.test.ts +205 -0
  550. package/src/mixins/index.ts +201 -0
  551. package/src/mixins/mixinHelpers.ts +69 -0
  552. package/src/models/Topic.ts +46 -0
  553. package/src/models/interfaces.ts +786 -0
  554. package/src/models/session.ts +55 -0
  555. package/src/server/__tests__/auth.test.ts +78 -0
  556. package/src/server/__tests__/capabilityTicket.test.ts +111 -0
  557. package/src/server/__tests__/cors.socket.test.ts +225 -0
  558. package/src/server/__tests__/cors.test.ts +158 -0
  559. package/src/server/__tests__/rateLimit.test.ts +257 -0
  560. package/src/server/__tests__/safeFetch.test.ts +232 -0
  561. package/src/server/__tests__/securityHeaders.test.ts +348 -0
  562. package/src/server/__tests__/serviceTokenAttribution.test.ts +396 -0
  563. package/src/server/__tests__/userInvalidation.test.ts +203 -0
  564. package/src/server/__tests__/verifySecret.test.ts +40 -0
  565. package/src/server/auth.ts +271 -0
  566. package/src/server/capabilityTicket.ts +258 -0
  567. package/src/server/cors.ts +277 -0
  568. package/src/server/index.ts +136 -0
  569. package/src/server/rateLimit.ts +313 -0
  570. package/src/server/safeFetch.ts +586 -0
  571. package/src/server/securityHeaders.ts +467 -0
  572. package/src/server/userInvalidation.ts +199 -0
  573. package/src/server/verifySecret.ts +52 -0
  574. package/src/session/SessionClient.ts +1001 -0
  575. package/src/session/__tests__/SessionClient.additive.test.ts +153 -0
  576. package/src/session/__tests__/SessionClient.broadcastChannel.test.ts +114 -0
  577. package/src/session/__tests__/SessionClient.diagnostics.test.ts +91 -0
  578. package/src/session/__tests__/SessionClient.directory.test.ts +688 -0
  579. package/src/session/__tests__/SessionClient.httpIntegration.test.ts +65 -0
  580. package/src/session/__tests__/SessionClient.identityPin.test.ts +175 -0
  581. package/src/session/__tests__/SessionClient.rest.test.ts +119 -0
  582. package/src/session/__tests__/SessionClient.serverEvents.test.ts +72 -0
  583. package/src/session/__tests__/SessionClient.socket.test.ts +202 -0
  584. package/src/session/__tests__/SessionClient.socketFactory.test.ts +81 -0
  585. package/src/session/__tests__/SessionClient.state.test.ts +78 -0
  586. package/src/session/__tests__/SessionClient.switchTokenOrder.test.ts +170 -0
  587. package/src/session/__tests__/accountDelegationAccess.test.ts +104 -0
  588. package/src/session/__tests__/accountDialogController.test.ts +1969 -0
  589. package/src/session/__tests__/accountDialogShape.test.ts +118 -0
  590. package/src/session/__tests__/accountSwitchTargets.test.ts +137 -0
  591. package/src/session/__tests__/authStateStore.test.ts +451 -0
  592. package/src/session/__tests__/deviceDirectory.test.ts +422 -0
  593. package/src/session/__tests__/deviceSwitcherRows.test.ts +223 -0
  594. package/src/session/__tests__/identityPin.test.ts +131 -0
  595. package/src/session/__tests__/identitySession.test.ts +194 -0
  596. package/src/session/__tests__/projectSessionState.test.ts +105 -0
  597. package/src/session/__tests__/refresh.identityPin.test.ts +307 -0
  598. package/src/session/__tests__/refresh.test.ts +443 -0
  599. package/src/session/__tests__/sessionIntegration.test.ts +209 -0
  600. package/src/session/__tests__/sharedDeviceCredential.test.ts +300 -0
  601. package/src/session/accountDelegationAccess.ts +72 -0
  602. package/src/session/accountDialogController.ts +1494 -0
  603. package/src/session/accountSwitchTargets.ts +93 -0
  604. package/src/session/authStateStore.ts +502 -0
  605. package/src/session/createSessionClient.ts +39 -0
  606. package/src/session/deviceDirectory.ts +269 -0
  607. package/src/session/deviceSwitcherRows.ts +145 -0
  608. package/src/session/identityPin.ts +310 -0
  609. package/src/session/identitySession.ts +206 -0
  610. package/src/session/projectSessionState.ts +137 -0
  611. package/src/session/refresh.ts +517 -0
  612. package/src/session/sessionClientHost.ts +39 -0
  613. package/src/session/sharedDeviceCredential.ts +349 -0
  614. package/src/session/socketLoader.ts +30 -0
  615. package/src/shared/utils/colorUtils.ts +155 -0
  616. package/src/shared/utils/errorUtils.ts +181 -0
  617. package/src/shared/utils/index.ts +59 -0
  618. package/src/shared/utils/networkUtils.ts +248 -0
  619. package/src/shared/utils/themeUtils.ts +115 -0
  620. package/src/types/buffer.d.ts +97 -0
  621. package/src/types/expo-crypto.d.ts +16 -0
  622. package/src/types/expo-secure-store.d.ts +17 -0
  623. package/src/utils/__tests__/accountUtils.test.ts +151 -0
  624. package/src/utils/__tests__/activityId.test.ts +45 -0
  625. package/src/utils/__tests__/asyncUtils.test.ts +187 -0
  626. package/src/utils/__tests__/authWebUrl.test.ts +15 -0
  627. package/src/utils/__tests__/cacheKey.test.ts +0 -0
  628. package/src/utils/__tests__/coldBoot.test.ts +496 -0
  629. package/src/utils/__tests__/commonsApproval.test.ts +60 -0
  630. package/src/utils/__tests__/identityCacheSweep.test.ts +151 -0
  631. package/src/utils/__tests__/languageUtils.test.ts +219 -0
  632. package/src/utils/__tests__/oauthPkce.test.ts +272 -0
  633. package/src/utils/__tests__/officialOrigins.test.ts +31 -0
  634. package/src/utils/__tests__/oxyServiceEnvironment.test.ts +7 -0
  635. package/src/utils/__tests__/profileLinks.test.ts +180 -0
  636. package/src/utils/__tests__/redactUrl.test.ts +33 -0
  637. package/src/utils/__tests__/registrableApex.test.ts +62 -0
  638. package/src/utils/__tests__/textNormalization.test.ts +205 -0
  639. package/src/utils/__tests__/validationUtils.test.ts +703 -0
  640. package/src/utils/__tests__/webauthnOrigin.test.ts +83 -0
  641. package/src/utils/accountCacheSweep.ts +93 -0
  642. package/src/utils/accountUtils.ts +194 -0
  643. package/src/utils/activityId.ts +2 -0
  644. package/src/utils/apiUtils.ts +137 -0
  645. package/src/utils/asyncUtils.ts +280 -0
  646. package/src/utils/authHelpers.ts +148 -0
  647. package/src/utils/authWebUrl.ts +17 -0
  648. package/src/utils/avatarUtils.ts +39 -0
  649. package/src/utils/cache.ts +273 -0
  650. package/src/utils/cacheKey.ts +93 -0
  651. package/src/utils/coldBoot.ts +218 -0
  652. package/src/utils/commonsApproval.ts +39 -0
  653. package/src/utils/commonsDelivery.ts +132 -0
  654. package/src/utils/deviceManager.ts +203 -0
  655. package/src/utils/displayNamePolicyRanges.generated.ts +69 -0
  656. package/src/utils/edgeRegion.ts +2 -0
  657. package/src/utils/errorUtils.ts +352 -0
  658. package/src/utils/identityCacheSweep.ts +104 -0
  659. package/src/utils/languageUtils.ts +256 -0
  660. package/src/utils/oauthPkce.ts +392 -0
  661. package/src/utils/officialOrigins.ts +62 -0
  662. package/src/utils/oxyServiceEnvironment.ts +17 -0
  663. package/src/utils/platform.ts +139 -0
  664. package/src/utils/profileLinks.ts +91 -0
  665. package/src/utils/redactUrl.ts +28 -0
  666. package/src/utils/registrableApex.ts +41 -0
  667. package/src/utils/requestUtils.ts +237 -0
  668. package/src/utils/sessionUtils.ts +206 -0
  669. package/src/utils/textNormalization.ts +162 -0
  670. package/src/utils/userHandle.ts +49 -0
  671. package/src/utils/userIdentity.ts +60 -0
  672. package/src/utils/validationUtils.ts +390 -0
  673. package/src/utils/webauthnOrigin.ts +52 -0
@@ -0,0 +1,390 @@
1
+ import { establishIdentitySession, resolveIdentityPin } from './identitySession.js';
2
+ import { isNative } from '../utils/platform.js';
3
+ import { extractErrorStatus } from '../utils/errorUtils.js';
4
+ import { logger } from '../logger/index.js';
5
+ /**
6
+ * Lead time (ms) before access-token expiry at which the proactive scheduler
7
+ * re-mints. Mirrors `HttpService`'s per-request `TOKEN_REFRESH_LEAD_SECONDS`
8
+ * (60s) so the scheduled re-mint and the request-time preflight use the same
9
+ * window — the scheduler just fires it during idle/background.
10
+ */
11
+ export const TOKEN_REFRESH_LEAD_MS = 60000;
12
+ /**
13
+ * Max `setTimeout` delay (2^31 − 1 ms, ~24.8 days). A larger delay overflows
14
+ * the int32 timer field and fires IMMEDIATELY — with a long-TTL token that
15
+ * turns the reschedule-on-finish loop into a tight busy refresh. Clamp to it.
16
+ */
17
+ const MAX_TIMEOUT_DELAY_MS = 2147483647;
18
+ /**
19
+ * Floor (ms) on ANY scheduled delay. An already-expired / in-lead-window token
20
+ * computes a non-positive `exp − now − lead`; without this floor that becomes
21
+ * `setTimeout(…, 0)`, and a FAILING re-mint (offline / server error) would
22
+ * re-arm at 0 in the finally block → a tight 100%-CPU busy loop. The floor
23
+ * guarantees every re-arm yields the event loop.
24
+ */
25
+ const MIN_SCHEDULE_DELAY_MS = 1000;
26
+ /**
27
+ * Backoff schedule (ms) applied when a scheduled re-mint FAILS: first retry
28
+ * after {@link MIN_FAILURE_BACKOFF_MS}, doubling up to {@link MAX_FAILURE_BACKOFF_MS}.
29
+ * Reset to 0 on any success or token change. This is what converts the former
30
+ * zero-delay failure loop into a bounded, backing-off retry.
31
+ */
32
+ const MIN_FAILURE_BACKOFF_MS = 5000;
33
+ const MAX_FAILURE_BACKOFF_MS = 5 * 60000;
34
+ /**
35
+ * Arm 1 — the device-secret mint, run under the owning client's PROCESS-WIDE
36
+ * single-flight (`httpService.runSingleFlightDeviceSecretMint`).
37
+ *
38
+ * Concurrent lanes (cold boot, the proactive scheduler, a request-time preflight,
39
+ * a 401 retry, the socket token transport, or a tab-focus reconcile) must not
40
+ * each persist a different view of the mint response. Routing EVERY lane through
41
+ * this one single-flight makes concurrent callers await the SAME in-flight mint
42
+ * and all receive its result, so the durable store converges on one credential.
43
+ *
44
+ * On success it persists `nextDeviceSecret` (read-back-verified) BEFORE planting
45
+ * the access token; a failed durable persist yields `persist-failed` WITHOUT
46
+ * planting. This function performs NO store mutation on failure — the caller
47
+ * applies the drop/clear policy (which differs web vs native) from the returned
48
+ * status.
49
+ *
50
+ * `pin` makes the mint IDENTITY-BOUND: the request carries the pinned
51
+ * `accountId` (so the server mints that account's token without touching
52
+ * `activeAccountId`), and the persisted `sessionId`/`userId` are resolved from
53
+ * the PINNED account entry — never from `state.activeAccountId`, whose drift is
54
+ * exactly what the pin exists to stop.
55
+ */
56
+ export async function refreshDeviceSecretArm(deps) {
57
+ const { oxy, store } = deps;
58
+ const pin = deps.pin ?? null;
59
+ return oxy.httpService.runSingleFlightDeviceSecretMint(async () => {
60
+ const persisted = await store.load();
61
+ if (!persisted?.deviceId || !persisted?.deviceSecret) {
62
+ return { status: 'no-secret' };
63
+ }
64
+ let mint;
65
+ try {
66
+ // Unpinned callers pass NO third argument at all, so the account-mode call
67
+ // shape (and therefore the request body) is untouched by this feature.
68
+ mint = pin
69
+ ? await oxy.mintFromDeviceSecret(persisted.deviceId, persisted.deviceSecret, {
70
+ accountId: pin.accountId,
71
+ })
72
+ : await oxy.mintFromDeviceSecret(persisted.deviceId, persisted.deviceSecret);
73
+ }
74
+ catch (error) {
75
+ if (extractErrorStatus(error) === 401) {
76
+ // Structural read (not `instanceof Error`): the thrown value can be a
77
+ // plain ApiError-shaped object or come from another realm.
78
+ const message = error?.message;
79
+ const body = typeof message === 'string' ? message : '';
80
+ // ONLY the server's explicit `invalid_device_secret` proves the presented
81
+ // secret is bad and may clear the durable device credential. `no_active_session`
82
+ // is an authoritative signed-out. ANY OTHER 401 — a middleware/CSRF/proxy 401,
83
+ // an ALB/starting-instance 401, a CORS error page, etc., all common during a
84
+ // deploy/restart window — is NOT proof the secret diverged: treat it as
85
+ // transient and KEEP the credential so a later attempt self-heals. Wiping the
86
+ // credential on an ambiguous 401 is what logged users out on every deploy,
87
+ // ecosystem-wide.
88
+ if (body.includes('invalid_device_secret'))
89
+ return { status: 'invalid-secret' };
90
+ if (body.includes('no_active_session'))
91
+ return { status: 'no-session' };
92
+ // A pinned mint whose account left the device set. The secret is intact —
93
+ // never classify this as a bad secret, or the caller would drop a healthy
94
+ // credential over a stale identity binding.
95
+ if (body.includes('account_not_on_device'))
96
+ return { status: 'account-not-on-device' };
97
+ return { status: 'transient' };
98
+ }
99
+ return { status: 'transient' };
100
+ }
101
+ // The account this session is BOUND to: the pinned one when identity-bound
102
+ // (the server already minted for it), else the device's active account.
103
+ const boundAccountId = pin ? pin.accountId : mint.state.activeAccountId;
104
+ const bound = mint.state.accounts.find((a) => a.accountId === boundAccountId);
105
+ const next = {
106
+ ...persisted,
107
+ deviceId: mint.state.deviceId,
108
+ deviceSecret: mint.nextDeviceSecret,
109
+ accessToken: mint.accessToken,
110
+ expiresAt: mint.expiresAt,
111
+ ...(bound ? { sessionId: bound.sessionId, userId: bound.accountId } : {}),
112
+ };
113
+ // Persist nextDeviceSecret (read-back-verified) BEFORE planting the token.
114
+ // A failed durable persist must NOT plant.
115
+ const persistedOk = await store.save(next);
116
+ if (!persistedOk) {
117
+ return { status: 'persist-failed' };
118
+ }
119
+ oxy.setTokens(mint.accessToken);
120
+ return {
121
+ status: 'ok',
122
+ token: mint.accessToken,
123
+ sessionId: next.sessionId,
124
+ userId: next.userId,
125
+ state: mint.state,
126
+ };
127
+ });
128
+ }
129
+ /**
130
+ * Re-mint the persisted session and return the fresh access token, or `null`
131
+ * when no arm could produce one.
132
+ *
133
+ * Arm 1 (`POST /session/device/token`, via {@link refreshDeviceSecretArm}): mint
134
+ * from the persisted `deviceId` + `deviceSecret`. On a 401 the secret is diverged
135
+ * or the device has no live session: drop the secret so the mint lane stops (or
136
+ * clear the store on web, where there is no fallback), then fall to arm 2 on
137
+ * native. A transient error — or a durable-persist failure — leaves the store and
138
+ * returns `null` WITHOUT falling to shared-key (those are not bad-secret signals).
139
+ *
140
+ * Arm 2 (native shared-keychain): when the secret is absent or was just rejected,
141
+ * re-mint via `signInWithSharedIdentity` (which plants tokens). On success the
142
+ * recovered `{deviceId, deviceSecret, …}` is PERSISTED so the fast device-secret
143
+ * lane is repopulated (mirrors the cold boot's `shared-key-signin` step) — an
144
+ * in-session shared-key recovery must not leave the fast-lane credential empty.
145
+ *
146
+ * IDENTITY-BOUND clients (`deps.identity`) run a different arm 2: the
147
+ * shared-keychain lane is DISABLED (its cross-app slot may hold a different
148
+ * identity) and replaced by {@link establishIdentitySession}, which re-signs a
149
+ * challenge with the PRIMARY local key and rewrites the pin. Arm 1 is pinned.
150
+ */
151
+ export async function refreshPersistedSession(deps) {
152
+ const { oxy, store } = deps;
153
+ const identity = deps.identity ?? null;
154
+ // The shared keychain is never an identity-bound client's recovery path.
155
+ const allowSharedKeyFallback = identity ? false : (deps.allowSharedKeyFallback ?? isNative());
156
+ // Resolved per call: a re-established identity session can move the pin, and a
157
+ // replaced/removed local key clears it (in which case arm 1 must NOT mint —
158
+ // an unpinned mint would adopt whatever account the device switched to).
159
+ const pin = identity ? await resolveIdentityPin(identity) : null;
160
+ if (identity && !pin) {
161
+ return recoverIdentitySession(oxy, store, identity);
162
+ }
163
+ const arm1 = await refreshDeviceSecretArm({ oxy, store, pin });
164
+ switch (arm1.status) {
165
+ case 'ok':
166
+ return arm1.token;
167
+ case 'transient':
168
+ logger.debug('Persisted deviceSecret mint failed (transient) — keeping store', { component: 'refresh', method: 'refreshPersistedSession' });
169
+ return null;
170
+ case 'persist-failed':
171
+ // The server rotated the secret but it did not durably persist. Do NOT fall
172
+ // to shared-key and do NOT plant — a later attempt re-mints (the process
173
+ // mirror still holds the rotated secret the server accepts) and can persist
174
+ // once storage recovers. Never advertise a session on an unsaved secret.
175
+ logger.error('Device-secret mint rotated the secret but it could not be durably persisted — refusing to plant (a later attempt re-mints)', undefined, { component: 'refresh', method: 'refreshPersistedSession' });
176
+ return null;
177
+ case 'invalid-secret':
178
+ case 'no-session': {
179
+ // 401: secret diverged or no live session. When a key-based arm 2 can still
180
+ // recover (native shared key, or an identity-bound client's own primary
181
+ // key) drop ONLY the secret and keep the deviceId; otherwise (web) the
182
+ // session is over — clear the store.
183
+ const persisted = await store.load();
184
+ if (allowSharedKeyFallback || identity) {
185
+ if (persisted) {
186
+ await store.save({ ...persisted, deviceSecret: undefined });
187
+ }
188
+ }
189
+ else {
190
+ await store.clear();
191
+ }
192
+ break;
193
+ }
194
+ case 'account-not-on-device':
195
+ // The pinned account left this device's session set. The secret is healthy —
196
+ // leave the store untouched and let the identity arm re-establish.
197
+ logger.debug('Pinned device-secret mint rejected: the pinned account is no longer on this device — re-establishing from the identity key', { component: 'refresh', method: 'refreshPersistedSession' });
198
+ break;
199
+ case 'no-secret':
200
+ break;
201
+ }
202
+ if (identity) {
203
+ return recoverIdentitySession(oxy, store, identity);
204
+ }
205
+ if (allowSharedKeyFallback) {
206
+ try {
207
+ const session = await oxy.signInWithSharedIdentity();
208
+ if (session?.accessToken) {
209
+ // Repopulate the fast device-secret lane from the shared-key re-mint.
210
+ if (session.deviceId && session.deviceSecret) {
211
+ await store.save({
212
+ sessionId: session.sessionId,
213
+ userId: session.user.id,
214
+ deviceId: session.deviceId,
215
+ deviceSecret: session.deviceSecret,
216
+ accessToken: session.accessToken,
217
+ expiresAt: session.expiresAt,
218
+ });
219
+ }
220
+ return session.accessToken;
221
+ }
222
+ }
223
+ catch (error) {
224
+ logger.debug('Shared-key re-mint fallback failed', { component: 'refresh', method: 'refreshPersistedSession' }, error);
225
+ }
226
+ }
227
+ return null;
228
+ }
229
+ /**
230
+ * Arm 2 for an IDENTITY-BOUND client: re-establish the session from the PRIMARY
231
+ * local key and rewrite the pin (`establishIdentitySession` plants the token and
232
+ * persists the device credential itself).
233
+ *
234
+ * Returns `null` — never throws — when there is no local identity, the verify
235
+ * yielded no token, or the exchange failed: the caller treats that as "could not
236
+ * refresh", exactly like the shared-key arm. A locked keychain therefore ends
237
+ * signed out rather than falling back to the device's active account.
238
+ */
239
+ async function recoverIdentitySession(oxy, store, binding) {
240
+ try {
241
+ const established = await establishIdentitySession({ oxy, store, binding });
242
+ return established?.session.accessToken ?? null;
243
+ }
244
+ catch (error) {
245
+ logger.debug('Identity-key re-sign-in failed', { component: 'refresh', method: 'recoverIdentitySession' }, error);
246
+ return null;
247
+ }
248
+ }
249
+ /**
250
+ * Build the reactive `AuthRefreshHandler` (arm 1 + arm 2). Install it via
251
+ * {@link installAuthRefreshHandler} or directly on
252
+ * `oxy.httpService.setAuthRefreshHandler`. `HttpService` owns single-flight
253
+ * dedup + cooldown, so the timer, the request-time preflight, and a 401 all
254
+ * collapse to one network attempt.
255
+ */
256
+ export function createAuthRefreshHandler(deps) {
257
+ return async (_reason) => {
258
+ return refreshPersistedSession(deps);
259
+ };
260
+ }
261
+ /**
262
+ * Install the unified refresh handler on the owner client's `HttpService`.
263
+ * Returns a disposer that removes it.
264
+ */
265
+ export function installAuthRefreshHandler(deps) {
266
+ deps.oxy.httpService.setAuthRefreshHandler(createAuthRefreshHandler(deps));
267
+ return () => {
268
+ deps.oxy.httpService.setAuthRefreshHandler(null);
269
+ };
270
+ }
271
+ /**
272
+ * Start the proactive re-mint scheduler against `oxy`.
273
+ *
274
+ * Schedules a single timer to fire {@link TOKEN_REFRESH_LEAD_MS} before the
275
+ * current access token's `exp`, calling
276
+ * `oxy.httpService.refreshAccessToken('preflight')` (which runs the installed
277
+ * handler; deduped + cooldown-guarded). After every attempt it reschedules
278
+ * from the possibly-rotated token. It also reschedules whenever the token
279
+ * changes (a sign-out that clears the token cancels the timer) and, on web
280
+ * tab-focus, re-mints immediately if already inside the lead window (a
281
+ * long-hidden tab throttles timers, so the token can be expired on return).
282
+ *
283
+ * No-ops cleanly when there is no token or an opaque/no-`exp` token — the
284
+ * reactive 401 path stays the only re-mint trigger in that case. The timer is
285
+ * `.unref?.()`-ed so it never keeps a Node/Jest event loop alive.
286
+ */
287
+ export function startTokenRefreshScheduler(oxy) {
288
+ let disposed = false;
289
+ let timer = null;
290
+ // 0 = no active backoff; grows on consecutive failures, resets on success /
291
+ // token change. Keeps a failing re-mint from re-arming at zero delay.
292
+ let failureBackoffMs = 0;
293
+ const clearTimer = () => {
294
+ if (timer !== null) {
295
+ clearTimeout(timer);
296
+ timer = null;
297
+ }
298
+ };
299
+ /** Arm the timer for `delayMs`, flooring at {@link MIN_SCHEDULE_DELAY_MS} and capping at the int32 max. */
300
+ const armTimer = (delayMs) => {
301
+ clearTimer();
302
+ const clamped = Math.min(Math.max(delayMs, MIN_SCHEDULE_DELAY_MS), MAX_TIMEOUT_DELAY_MS);
303
+ timer = setTimeout(runRefresh, clamped);
304
+ // Never keep a Node/Jest event loop alive for a background re-mint timer.
305
+ timer.unref?.();
306
+ };
307
+ /** Schedule the next re-mint from the current token's expiry (the healthy path). */
308
+ const scheduleFromExpiry = () => {
309
+ clearTimer();
310
+ if (disposed || !oxy.getAccessToken()) {
311
+ return;
312
+ }
313
+ const expSeconds = oxy.getAccessTokenExpiry();
314
+ if (expSeconds === null) {
315
+ return;
316
+ }
317
+ armTimer(expSeconds * 1000 - Date.now() - TOKEN_REFRESH_LEAD_MS);
318
+ };
319
+ const runRefresh = () => {
320
+ // Clear any pending timer up front so an out-of-band trigger (focus) plus
321
+ // a fired timer can never double-run.
322
+ clearTimer();
323
+ void oxy.httpService.refreshAccessToken('preflight')
324
+ .then((token) => Boolean(token))
325
+ .catch(() => false)
326
+ .then((ok) => {
327
+ if (disposed) {
328
+ return;
329
+ }
330
+ if (ok) {
331
+ // Success — drop any backoff and re-arm from the rotated token's exp.
332
+ failureBackoffMs = 0;
333
+ scheduleFromExpiry();
334
+ return;
335
+ }
336
+ // Failure — back off (never re-arm at zero) and retry.
337
+ failureBackoffMs =
338
+ failureBackoffMs === 0
339
+ ? MIN_FAILURE_BACKOFF_MS
340
+ : Math.min(failureBackoffMs * 2, MAX_FAILURE_BACKOFF_MS);
341
+ armTimer(failureBackoffMs);
342
+ });
343
+ };
344
+ /** Public (re)schedule entry: a fresh token / focus signal — drop backoff and arm from expiry. */
345
+ const schedule = () => {
346
+ failureBackoffMs = 0;
347
+ scheduleFromExpiry();
348
+ };
349
+ const onFocus = () => {
350
+ if (disposed || !oxy.getAccessToken()) {
351
+ return;
352
+ }
353
+ const expSeconds = oxy.getAccessTokenExpiry();
354
+ if (expSeconds === null) {
355
+ return;
356
+ }
357
+ const remainingMs = expSeconds * 1000 - Date.now();
358
+ if (remainingMs <= TOKEN_REFRESH_LEAD_MS) {
359
+ runRefresh();
360
+ }
361
+ else {
362
+ schedule();
363
+ }
364
+ };
365
+ const unsubscribeTokens = oxy.onTokensChanged(() => {
366
+ if (!disposed) {
367
+ schedule();
368
+ }
369
+ });
370
+ let removeFocusListener = null;
371
+ if (typeof document !== 'undefined') {
372
+ const handler = () => {
373
+ if (document.visibilityState === 'visible') {
374
+ onFocus();
375
+ }
376
+ };
377
+ document.addEventListener('visibilitychange', handler);
378
+ removeFocusListener = () => document.removeEventListener('visibilitychange', handler);
379
+ }
380
+ schedule();
381
+ return {
382
+ dispose() {
383
+ disposed = true;
384
+ clearTimer();
385
+ unsubscribeTokens();
386
+ removeFocusListener?.();
387
+ removeFocusListener = null;
388
+ },
389
+ };
390
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Thin `SessionClientHost` adapter over an `OxyServices` instance.
3
+ *
4
+ * `SessionClient` is host-agnostic: it only needs a REST + token surface.
5
+ * `OxyServices` already exposes all of that except `getCurrentAccountId`,
6
+ * which has no direct equivalent — the adapter holds a mutable ref set by
7
+ * the caller (`OxyContext` in `@oxy.so/services`) via `setCurrentAccountId`.
8
+ *
9
+ * Shared here (rather than duplicated per consumer) because it is entirely
10
+ * platform-agnostic: every method it calls exists identically on
11
+ * `OxyServices` regardless of host (web, Expo/RN, Node).
12
+ */
13
+ export function createSessionClientHost(oxyServices) {
14
+ let currentAccountId = null;
15
+ let deviceCredential = null;
16
+ return {
17
+ makeRequest: (method, url, data, options) => oxyServices.makeRequest(method, url, data, options),
18
+ getBaseURL: () => oxyServices.getBaseURL(),
19
+ getAccessToken: () => oxyServices.getAccessToken(),
20
+ getDeviceCredential: () => deviceCredential,
21
+ onTokensChanged: (listener) => oxyServices.onTokensChanged(listener),
22
+ setTokens: (accessToken) => oxyServices.setTokens(accessToken),
23
+ getCurrentAccountId: () => currentAccountId,
24
+ setCurrentAccountId: (id) => {
25
+ currentAccountId = id;
26
+ },
27
+ setDeviceCredential: (credential) => {
28
+ deviceCredential = credential;
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,239 @@
1
+ /**
2
+ * The shared native DeviceSession credential — one device, one session, many apps.
3
+ *
4
+ * ## What this is, and what it deliberately is NOT
5
+ *
6
+ * Two different secrets can make a native Oxy app boot signed in, and conflating
7
+ * them is the bug this module exists to end:
8
+ *
9
+ * Commons private identity key → identity and signed approval ONLY. It is
10
+ * self-custody, irreplaceable, and must never
11
+ * become the general app session transport.
12
+ * Shared DeviceSession credential → THIS module. An ordinary `deviceId` +
13
+ * `deviceSecret` pair that restores ordinary
14
+ * official apps, follows the device's active
15
+ * context, and is individually rotatable and
16
+ * revocable server-side.
17
+ *
18
+ * An ordinary app needs the second one. Handing it the first — which is what the
19
+ * `shared-key-signin` lane does today — gives every sibling app the ability to
20
+ * sign as the user's cryptographic identity to obtain something as mundane as a
21
+ * session. That lane stays as a recovery/compatibility path; this one supersedes
22
+ * it for the ordinary case.
23
+ *
24
+ * ## Why sharing one credential is safe against the server
25
+ *
26
+ * `POST /session/device/token` does NOT rotate: the response echoes the presented
27
+ * secret back as `nextDeviceSecret` precisely so several first-party apps sharing
28
+ * one `DeviceSession` can refresh concurrently without invalidating one another.
29
+ * So N apps holding one credential is a supported server state, not a race — and
30
+ * because they then share ONE `DeviceSession`, they automatically share its
31
+ * `activeContextId` and its token-free `session_state` broadcasts.
32
+ *
33
+ * ## The one safety rule everything here is built around
34
+ *
35
+ * A read that FAILED and a slot that is EMPTY must never be the same value. The
36
+ * empty answer authorises writes (seed the slot); the failed answer must
37
+ * authorise nothing. {@link SharedDeviceCredentialRead} keeps them apart at the
38
+ * type level, and every decision below fails closed on anything that is not a
39
+ * positive `absent`/`present`.
40
+ *
41
+ * Platform-agnostic: the actual keychain / keystore access is injected as a
42
+ * {@link SharedDeviceCredentialStore} by `@oxy.so/services`. ESM-safe, no
43
+ * `require()`, no react/react-native/expo imports.
44
+ */
45
+ import { logger } from '../logger/index.js';
46
+ /** The usable `{deviceId, deviceSecret}` pair in a persisted state, or null. */
47
+ export function readLocalDeviceCredential(state) {
48
+ if (!state?.deviceId || !state.deviceSecret) {
49
+ return null;
50
+ }
51
+ return { deviceId: state.deviceId, deviceSecret: state.deviceSecret };
52
+ }
53
+ /**
54
+ * Narrow an UNTRUSTED bridge payload into a {@link SharedDeviceCredentialRead}.
55
+ *
56
+ * Anything unrecognised resolves to `unavailable`, never `absent`. A native
57
+ * module returning a shape this build does not understand (an older app in the
58
+ * signing group, a partially-applied upgrade) means we do not KNOW whether the
59
+ * device has a shared session — and "do not know" must never authorise a write
60
+ * that would overwrite one.
61
+ */
62
+ export function normalizeSharedDeviceSessionRead(raw) {
63
+ if (!raw || typeof raw !== 'object') {
64
+ return { state: 'unavailable', cause: new Error('shared device session bridge returned a non-object') };
65
+ }
66
+ const payload = raw;
67
+ if (payload.status === 'absent') {
68
+ return { state: 'absent' };
69
+ }
70
+ if (payload.status === 'present') {
71
+ const deviceId = payload.deviceId;
72
+ const deviceSecret = payload.deviceSecret;
73
+ if (typeof deviceId === 'string' &&
74
+ deviceId.length > 0 &&
75
+ typeof deviceSecret === 'string' &&
76
+ deviceSecret.length > 0) {
77
+ return { state: 'present', credential: { deviceId, deviceSecret } };
78
+ }
79
+ // A `present` verdict whose payload is incomplete is a broken slot, not an
80
+ // empty one. Reporting `absent` here would let the next successful sign-in
81
+ // overwrite whatever is really in there.
82
+ return {
83
+ state: 'unavailable',
84
+ cause: new Error('shared device session bridge reported `present` with an incomplete credential'),
85
+ };
86
+ }
87
+ if (payload.status === 'unavailable') {
88
+ const reason = typeof payload.reason === 'string' ? payload.reason : 'unknown';
89
+ return { state: 'unavailable', cause: new Error(`shared device session slot unavailable: ${reason}`) };
90
+ }
91
+ return {
92
+ state: 'unavailable',
93
+ cause: new Error(`shared device session bridge returned an unrecognised status: ${String(payload.status)}`),
94
+ };
95
+ }
96
+ /**
97
+ * Should this app adopt the shared credential? Pure.
98
+ *
99
+ * Adoption happens in exactly ONE case: the shared slot holds a credential and
100
+ * this app has none of its own. That single rule delivers the product
101
+ * requirement — a newly installed official app joins the device's existing
102
+ * session without another QR — while making the two failure modes that matter
103
+ * unreachable:
104
+ *
105
+ * - An app that is already signed in is NEVER moved onto another credential, so
106
+ * "no user is signed out merely because one app updates first" holds by
107
+ * construction, in both upgrade directions.
108
+ * - A failed read never looks like an empty slot, so a locked keychain resolves
109
+ * to "keep what I have" rather than "this is a fresh device".
110
+ *
111
+ * The cost is stated plainly: two apps that each already own a DIFFERENT device
112
+ * session stay on their own until one of them loses its credential. Converging
113
+ * them would mean signing one of them out or a server-side device merge, and
114
+ * neither is something a boot path may do silently.
115
+ */
116
+ export function decideSharedDeviceJoin(local, shared) {
117
+ if (readLocalDeviceCredential(local) !== null) {
118
+ return { action: 'skip', reason: 'local-credential-present' };
119
+ }
120
+ switch (shared.state) {
121
+ case 'present':
122
+ return { action: 'adopt', credential: shared.credential };
123
+ case 'absent':
124
+ return { action: 'skip', reason: 'shared-empty' };
125
+ case 'unavailable':
126
+ return { action: 'skip', reason: 'shared-unreadable' };
127
+ case 'unsupported':
128
+ return { action: 'skip', reason: 'shared-unsupported' };
129
+ }
130
+ }
131
+ /**
132
+ * Should this app write the credential it just proved into the shared slot? Pure.
133
+ *
134
+ * `proven` means the server accepted it moments ago — a successful sign-in or a
135
+ * successful mint. Only a proven credential is ever published, so the slot can
136
+ * never be seeded with something no app could use.
137
+ *
138
+ * A slot already held by a DIFFERENT `deviceId` is left alone. Overwriting it
139
+ * would silently migrate every other app on this device onto our session at their
140
+ * next cold boot — a real, user-visible change of who they are signed in as, and
141
+ * not something a background persist may decide.
142
+ */
143
+ export function decideSharedDevicePublish(proven, shared) {
144
+ switch (shared.state) {
145
+ case 'unsupported':
146
+ return { action: 'skip', reason: 'shared-unsupported' };
147
+ case 'unavailable':
148
+ return { action: 'skip', reason: 'shared-unreadable' };
149
+ case 'absent':
150
+ return { action: 'publish' };
151
+ case 'present': {
152
+ if (shared.credential.deviceId !== proven.deviceId) {
153
+ return { action: 'skip', reason: 'owned-by-another-device' };
154
+ }
155
+ if (shared.credential.deviceSecret === proven.deviceSecret) {
156
+ return { action: 'skip', reason: 'already-current' };
157
+ }
158
+ // Same device, newer secret. Sign-in rotates the secret (the mint does
159
+ // not), so the just-proven one is the credential a fresh install should
160
+ // join with.
161
+ return { action: 'publish' };
162
+ }
163
+ }
164
+ }
165
+ /**
166
+ * Read the shared slot, apply {@link decideSharedDevicePublish}, and write when
167
+ * it says so. Best-effort by contract: the caller's own durable credential is
168
+ * already persisted, so a failure here only means a future install will have to
169
+ * sign in interactively.
170
+ */
171
+ export async function publishProvenDeviceCredential(deps) {
172
+ const read = await deps.shared.read();
173
+ const decision = decideSharedDevicePublish(deps.credential, read);
174
+ if (decision.action === 'skip') {
175
+ if (decision.reason === 'shared-unreadable') {
176
+ logger.debug('shared device credential slot unreadable — not publishing (an unreadable slot is never an empty one)', { component: 'sharedDeviceCredential', method: 'publishProvenDeviceCredential' });
177
+ }
178
+ return { status: 'skipped', reason: decision.reason };
179
+ }
180
+ const published = await deps.shared.publish(deps.credential);
181
+ return published ? { status: 'published' } : { status: 'publish-failed' };
182
+ }
183
+ /**
184
+ * Wrap a platform {@link AuthStateStore} so that every durable credential it
185
+ * persists is ALSO mirrored into the shared slot.
186
+ *
187
+ * Writes mirror automatically; reads do NOT adopt. That split is deliberate:
188
+ *
189
+ * - Mirroring on write is the right place because `save()` is where a proven
190
+ * credential lands, on every lane there is — interactive sign-in, the cold
191
+ * boot mint, the refresh scheduler, the 401 re-mint, shared-key recovery. One
192
+ * seam, no lane left out, and no new call site to forget.
193
+ * - Adopting on read would hide a change of WHO THIS APP IS SIGNED IN AS inside
194
+ * a storage primitive, and would run on every `load()`. Adoption is an
195
+ * explicit, once-per-boot cold-boot step instead (`shared-device-adopt`).
196
+ *
197
+ * `clear()` deliberately does NOT clear the shared slot. This app signing out is
198
+ * not authority over the device-wide join point: other apps may still be signed
199
+ * in on that same credential, and once the server session is really gone the
200
+ * credential mints `no_active_session` for everyone anyway.
201
+ */
202
+ export function createSharedMirroringAuthStateStore(deps) {
203
+ const { local, shared } = deps;
204
+ // Process-local memo of the credential we last observed the shared slot to
205
+ // hold. Purely an optimization to keep the refresh scheduler from re-reading
206
+ // the keychain every mint; a slot wiped out from under us is re-seeded on the
207
+ // next launch rather than mid-process.
208
+ let mirrored = null;
209
+ return {
210
+ load: () => local.load(),
211
+ clear: () => local.clear(),
212
+ save: async (state) => {
213
+ // The durable local write is the contract this store owes its caller —
214
+ // run it first and report ITS result, unchanged. The mirror is additive.
215
+ const durablePersisted = await local.save(state);
216
+ const credential = readLocalDeviceCredential(state);
217
+ if (!credential) {
218
+ return durablePersisted;
219
+ }
220
+ if (mirrored !== null &&
221
+ mirrored.deviceId === credential.deviceId &&
222
+ mirrored.deviceSecret === credential.deviceSecret) {
223
+ return durablePersisted;
224
+ }
225
+ try {
226
+ const outcome = await publishProvenDeviceCredential({ shared, credential });
227
+ if (outcome.status === 'published' || (outcome.status === 'skipped' && outcome.reason === 'already-current')) {
228
+ mirrored = credential;
229
+ }
230
+ }
231
+ catch (error) {
232
+ // A store implementation is contractually non-throwing, but a mirror
233
+ // failure must never take down the durable write that already succeeded.
234
+ logger.debug('mirroring the device credential into the shared slot threw — the local credential is unaffected', { component: 'sharedDeviceCredential', method: 'save' }, error);
235
+ }
236
+ return durablePersisted;
237
+ },
238
+ };
239
+ }
@@ -0,0 +1,19 @@
1
+ import { logger } from '../logger/index.js';
2
+ let cachedFactory = null;
3
+ let loadAttempted = false;
4
+ export async function getSocketIO() {
5
+ if (cachedFactory)
6
+ return cachedFactory;
7
+ if (loadAttempted)
8
+ return null;
9
+ loadAttempted = true;
10
+ try {
11
+ const mod = (await import('socket.io-client'));
12
+ cachedFactory = mod.io ?? mod.default ?? null;
13
+ return cachedFactory;
14
+ }
15
+ catch (error) {
16
+ logger.warn('[SessionClient] socket.io-client import failed; realtime session sync disabled', { component: 'SessionClient' }, error);
17
+ return null;
18
+ }
19
+ }