@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,256 @@
1
+ /**
2
+ * Locale utilities for OxyServices.
3
+ *
4
+ * The Oxy platform models account languages as full BCP-47 locales
5
+ * (`language-REGION`, e.g. `es-ES`, `es-MX`, `pt-BR`). Region is significant:
6
+ * "Spanish (Spain)" is a different locale than "Spanish (Mexico)". A user's
7
+ * account locales live on `User.languages` as an ordered list with the PRIMARY
8
+ * (UI) locale first — there is no singular `language` field.
9
+ *
10
+ * This module is the single source of truth for the supported-locale catalog
11
+ * and every locale operation the SDK exposes: parsing base subtags, canonical
12
+ * normalization, validation, metadata lookup, and resolving a user's locales.
13
+ * It is pure and side-effect free.
14
+ */
15
+
16
+ import type { User } from '../models/interfaces';
17
+
18
+ /**
19
+ * A supported BCP-47 locale in the Oxy catalog.
20
+ */
21
+ export interface SupportedLanguage {
22
+ /** Canonical BCP-47 locale tag, `language-REGION` (e.g. `'es-ES'`). */
23
+ code: string;
24
+ /** ISO 639-1 base language subtag, lowercased (e.g. `'es'`). */
25
+ language: string;
26
+ /** ISO 3166-1 alpha-2 region subtag, uppercased (e.g. `'ES'`). */
27
+ region: string;
28
+ /** English display name, `'Language (Region)'` (e.g. `'Spanish (Spain)'`). */
29
+ name: string;
30
+ /** Endonym — the name as written in the locale itself (e.g. `'Español (España)'`). */
31
+ nativeName: string;
32
+ /** `true` when the locale is written right-to-left. Omitted for LTR locales. */
33
+ rtl?: boolean;
34
+ }
35
+
36
+ /**
37
+ * The supported-locale catalog. Ordered by prominence for display; the order
38
+ * is not otherwise significant. Every entry is a full `language-REGION` tag,
39
+ * with the base subtag lowercased and the region uppercased.
40
+ */
41
+ export const SUPPORTED_LANGUAGES: readonly SupportedLanguage[] = [
42
+ // English
43
+ { code: 'en-US', language: 'en', region: 'US', name: 'English (United States)', nativeName: 'English (United States)' },
44
+ { code: 'en-GB', language: 'en', region: 'GB', name: 'English (United Kingdom)', nativeName: 'English (United Kingdom)' },
45
+ { code: 'en-AU', language: 'en', region: 'AU', name: 'English (Australia)', nativeName: 'English (Australia)' },
46
+ { code: 'en-CA', language: 'en', region: 'CA', name: 'English (Canada)', nativeName: 'English (Canada)' },
47
+ { code: 'en-IN', language: 'en', region: 'IN', name: 'English (India)', nativeName: 'English (India)' },
48
+
49
+ // Spanish
50
+ { code: 'es-ES', language: 'es', region: 'ES', name: 'Spanish (Spain)', nativeName: 'Español (España)' },
51
+ { code: 'es-MX', language: 'es', region: 'MX', name: 'Spanish (Mexico)', nativeName: 'Español (México)' },
52
+ { code: 'es-US', language: 'es', region: 'US', name: 'Spanish (United States)', nativeName: 'Español (Estados Unidos)' },
53
+ { code: 'es-AR', language: 'es', region: 'AR', name: 'Spanish (Argentina)', nativeName: 'Español (Argentina)' },
54
+ { code: 'es-CO', language: 'es', region: 'CO', name: 'Spanish (Colombia)', nativeName: 'Español (Colombia)' },
55
+
56
+ // Portuguese
57
+ { code: 'pt-BR', language: 'pt', region: 'BR', name: 'Portuguese (Brazil)', nativeName: 'Português (Brasil)' },
58
+ { code: 'pt-PT', language: 'pt', region: 'PT', name: 'Portuguese (Portugal)', nativeName: 'Português (Portugal)' },
59
+
60
+ // French
61
+ { code: 'fr-FR', language: 'fr', region: 'FR', name: 'French (France)', nativeName: 'Français (France)' },
62
+ { code: 'fr-CA', language: 'fr', region: 'CA', name: 'French (Canada)', nativeName: 'Français (Canada)' },
63
+
64
+ // German
65
+ { code: 'de-DE', language: 'de', region: 'DE', name: 'German (Germany)', nativeName: 'Deutsch (Deutschland)' },
66
+ { code: 'de-AT', language: 'de', region: 'AT', name: 'German (Austria)', nativeName: 'Deutsch (Österreich)' },
67
+ { code: 'de-CH', language: 'de', region: 'CH', name: 'German (Switzerland)', nativeName: 'Deutsch (Schweiz)' },
68
+
69
+ // Italian
70
+ { code: 'it-IT', language: 'it', region: 'IT', name: 'Italian (Italy)', nativeName: 'Italiano (Italia)' },
71
+
72
+ // Dutch
73
+ { code: 'nl-NL', language: 'nl', region: 'NL', name: 'Dutch (Netherlands)', nativeName: 'Nederlands (Nederland)' },
74
+ { code: 'nl-BE', language: 'nl', region: 'BE', name: 'Dutch (Belgium)', nativeName: 'Nederlands (België)' },
75
+
76
+ // Nordic
77
+ { code: 'sv-SE', language: 'sv', region: 'SE', name: 'Swedish (Sweden)', nativeName: 'Svenska (Sverige)' },
78
+ { code: 'nb-NO', language: 'nb', region: 'NO', name: 'Norwegian Bokmål (Norway)', nativeName: 'Norsk bokmål (Norge)' },
79
+ { code: 'da-DK', language: 'da', region: 'DK', name: 'Danish (Denmark)', nativeName: 'Dansk (Danmark)' },
80
+ { code: 'fi-FI', language: 'fi', region: 'FI', name: 'Finnish (Finland)', nativeName: 'Suomi (Suomi)' },
81
+
82
+ // Central & Eastern Europe
83
+ { code: 'pl-PL', language: 'pl', region: 'PL', name: 'Polish (Poland)', nativeName: 'Polski (Polska)' },
84
+ { code: 'cs-CZ', language: 'cs', region: 'CZ', name: 'Czech (Czechia)', nativeName: 'Čeština (Česko)' },
85
+ { code: 'sk-SK', language: 'sk', region: 'SK', name: 'Slovak (Slovakia)', nativeName: 'Slovenčina (Slovensko)' },
86
+ { code: 'hu-HU', language: 'hu', region: 'HU', name: 'Hungarian (Hungary)', nativeName: 'Magyar (Magyarország)' },
87
+ { code: 'ro-RO', language: 'ro', region: 'RO', name: 'Romanian (Romania)', nativeName: 'Română (România)' },
88
+ { code: 'bg-BG', language: 'bg', region: 'BG', name: 'Bulgarian (Bulgaria)', nativeName: 'Български (България)' },
89
+ { code: 'hr-HR', language: 'hr', region: 'HR', name: 'Croatian (Croatia)', nativeName: 'Hrvatski (Hrvatska)' },
90
+ { code: 'sr-RS', language: 'sr', region: 'RS', name: 'Serbian (Serbia)', nativeName: 'Српски (Србија)' },
91
+ { code: 'sl-SI', language: 'sl', region: 'SI', name: 'Slovenian (Slovenia)', nativeName: 'Slovenščina (Slovenija)' },
92
+ { code: 'lt-LT', language: 'lt', region: 'LT', name: 'Lithuanian (Lithuania)', nativeName: 'Lietuvių (Lietuva)' },
93
+ { code: 'lv-LV', language: 'lv', region: 'LV', name: 'Latvian (Latvia)', nativeName: 'Latviešu (Latvija)' },
94
+ { code: 'et-EE', language: 'et', region: 'EE', name: 'Estonian (Estonia)', nativeName: 'Eesti (Eesti)' },
95
+ { code: 'uk-UA', language: 'uk', region: 'UA', name: 'Ukrainian (Ukraine)', nativeName: 'Українська (Україна)' },
96
+ { code: 'ru-RU', language: 'ru', region: 'RU', name: 'Russian (Russia)', nativeName: 'Русский (Россия)' },
97
+ { code: 'el-GR', language: 'el', region: 'GR', name: 'Greek (Greece)', nativeName: 'Ελληνικά (Ελλάδα)' },
98
+
99
+ // Turkish
100
+ { code: 'tr-TR', language: 'tr', region: 'TR', name: 'Turkish (Türkiye)', nativeName: 'Türkçe (Türkiye)' },
101
+
102
+ // Catalan
103
+ { code: 'ca-ES', language: 'ca', region: 'ES', name: 'Catalan (Spain)', nativeName: 'Català (Espanya)' },
104
+
105
+ // East, South & Southeast Asia
106
+ { code: 'ja-JP', language: 'ja', region: 'JP', name: 'Japanese (Japan)', nativeName: '日本語 (日本)' },
107
+ { code: 'ko-KR', language: 'ko', region: 'KR', name: 'Korean (South Korea)', nativeName: '한국어 (대한민국)' },
108
+ { code: 'zh-CN', language: 'zh', region: 'CN', name: 'Chinese (Simplified, China)', nativeName: '中文 (中国)' },
109
+ { code: 'zh-TW', language: 'zh', region: 'TW', name: 'Chinese (Traditional, Taiwan)', nativeName: '中文 (台灣)' },
110
+ { code: 'zh-HK', language: 'zh', region: 'HK', name: 'Chinese (Traditional, Hong Kong)', nativeName: '中文 (香港)' },
111
+ { code: 'hi-IN', language: 'hi', region: 'IN', name: 'Hindi (India)', nativeName: 'हिन्दी (भारत)' },
112
+ { code: 'bn-BD', language: 'bn', region: 'BD', name: 'Bengali (Bangladesh)', nativeName: 'বাংলা (বাংলাদেশ)' },
113
+ { code: 'id-ID', language: 'id', region: 'ID', name: 'Indonesian (Indonesia)', nativeName: 'Bahasa Indonesia (Indonesia)' },
114
+ { code: 'ms-MY', language: 'ms', region: 'MY', name: 'Malay (Malaysia)', nativeName: 'Bahasa Melayu (Malaysia)' },
115
+ { code: 'th-TH', language: 'th', region: 'TH', name: 'Thai (Thailand)', nativeName: 'ไทย (ประเทศไทย)' },
116
+ { code: 'vi-VN', language: 'vi', region: 'VN', name: 'Vietnamese (Vietnam)', nativeName: 'Tiếng Việt (Việt Nam)' },
117
+
118
+ // Right-to-left
119
+ { code: 'ar-SA', language: 'ar', region: 'SA', name: 'Arabic (Saudi Arabia)', nativeName: 'العربية (السعودية)', rtl: true },
120
+ { code: 'ar-EG', language: 'ar', region: 'EG', name: 'Arabic (Egypt)', nativeName: 'العربية (مصر)', rtl: true },
121
+ { code: 'he-IL', language: 'he', region: 'IL', name: 'Hebrew (Israel)', nativeName: 'עברית (ישראל)', rtl: true },
122
+ { code: 'fa-IR', language: 'fa', region: 'IR', name: 'Persian (Iran)', nativeName: 'فارسی (ایران)', rtl: true },
123
+ { code: 'ur-PK', language: 'ur', region: 'PK', name: 'Urdu (Pakistan)', nativeName: 'اردو (پاکستان)', rtl: true },
124
+ ];
125
+
126
+ /** Canonical fallback locale used when a display value cannot be resolved. */
127
+ export const FALLBACK_LOCALE = 'en-US';
128
+
129
+ /** O(1) lookup of a catalog entry by its canonical code. */
130
+ const LOCALE_BY_CODE: ReadonlyMap<string, SupportedLanguage> = new Map(
131
+ SUPPORTED_LANGUAGES.map((entry) => [entry.code, entry]),
132
+ );
133
+
134
+ /**
135
+ * Extract the base language subtag from a locale, lowercased.
136
+ *
137
+ * Tolerant of bare base subtags and of extra subtags (script/variant):
138
+ * `'es-ES'` → `'es'`, `'es'` → `'es'`, `'zh-Hant-TW'` → `'zh'`. Returns an
139
+ * empty string for empty input.
140
+ */
141
+ export function getBaseLanguage(locale: string): string {
142
+ return locale.trim().toLowerCase().split('-')[0] ?? '';
143
+ }
144
+
145
+ /**
146
+ * Normalize a locale string to its canonical `language-REGION` form when it is
147
+ * a supported locale, otherwise `undefined`.
148
+ *
149
+ * Canonicalization lowercases the base subtag and uppercases the region
150
+ * subtag (`'es-es'` → `'es-ES'`, `'EN-us'` → `'en-US'`) and validates the
151
+ * result against {@link SUPPORTED_LANGUAGES}. A bare base subtag (`'es'`) has
152
+ * no region and is therefore not a locale — it returns `undefined`.
153
+ */
154
+ export function normalizeLocale(input: string): string | undefined {
155
+ const trimmed = input.trim();
156
+ if (!trimmed) return undefined;
157
+
158
+ const parts = trimmed.split('-');
159
+ if (parts.length < 2) return undefined;
160
+
161
+ const base = parts[0]?.toLowerCase();
162
+ const region = parts[parts.length - 1]?.toUpperCase();
163
+ if (!base || !region) return undefined;
164
+
165
+ const canonical = `${base}-${region}`;
166
+ return LOCALE_BY_CODE.has(canonical) ? canonical : undefined;
167
+ }
168
+
169
+ /**
170
+ * Whether `input` resolves to a supported BCP-47 locale.
171
+ */
172
+ export function isSupportedLocale(input: string): boolean {
173
+ return normalizeLocale(input) !== undefined;
174
+ }
175
+
176
+ /**
177
+ * Resolve catalog metadata for a locale.
178
+ *
179
+ * @param code - A locale tag (any case; e.g. `'es-ES'`, `'es-es'`).
180
+ * @returns The {@link SupportedLanguage} entry, or `null` when the tag is empty
181
+ * or not a supported locale.
182
+ */
183
+ export function getLanguageMetadata(code: string | null | undefined): SupportedLanguage | null {
184
+ if (!code) return null;
185
+ const canonical = normalizeLocale(code);
186
+ if (!canonical) return null;
187
+ return LOCALE_BY_CODE.get(canonical) ?? null;
188
+ }
189
+
190
+ /**
191
+ * English display name for a locale (e.g. `'Spanish (Spain)'`).
192
+ * Falls back to the input tag, then {@link FALLBACK_LOCALE}.
193
+ */
194
+ export function getLanguageName(code: string | null | undefined): string {
195
+ return getLanguageMetadata(code)?.name || code || FALLBACK_LOCALE;
196
+ }
197
+
198
+ /**
199
+ * Native display name (endonym) for a locale (e.g. `'Español (España)'`).
200
+ * Falls back to the input tag, then {@link FALLBACK_LOCALE}.
201
+ */
202
+ export function getNativeLanguageName(code: string | null | undefined): string {
203
+ return getLanguageMetadata(code)?.nativeName || code || FALLBACK_LOCALE;
204
+ }
205
+
206
+ /**
207
+ * Base language subtags whose scripts are written right-to-left. Used to drive
208
+ * `I18nManager.forceRTL(...)` on React Native and `<html dir="rtl">` on web.
209
+ * Includes Arabic (`ar`), Hebrew (`he` / legacy `iw`), Persian (`fa`), and
210
+ * Urdu (`ur`).
211
+ */
212
+ const RTL_LANGUAGE_BASES: ReadonlySet<string> = new Set(['ar', 'he', 'iw', 'fa', 'ur']);
213
+
214
+ /**
215
+ * Whether a locale (or bare base subtag) is written right-to-left. Unknown
216
+ * tags are treated as left-to-right.
217
+ */
218
+ export function isRTLLocale(locale?: string | null): boolean {
219
+ if (!locale) return false;
220
+ return RTL_LANGUAGE_BASES.has(getBaseLanguage(locale));
221
+ }
222
+
223
+ /**
224
+ * Resolve the ordered list of account locales for a user, primary first.
225
+ *
226
+ * Reads `user.languages` (the only source — there is no singular `language`
227
+ * field), then normalizes each entry to its canonical `language-REGION` form,
228
+ * drops non-string and unsupported entries, and de-duplicates while preserving
229
+ * first-seen order. Pure and side-effect free — never throws on bad input.
230
+ */
231
+ export function getUserLanguages(user: Pick<User, 'languages'> | null | undefined): string[] {
232
+ const source = user?.languages;
233
+ if (!Array.isArray(source)) return [];
234
+
235
+ const result: string[] = [];
236
+ const seen = new Set<string>();
237
+
238
+ for (const entry of source) {
239
+ if (typeof entry !== 'string') continue;
240
+ const canonical = normalizeLocale(entry);
241
+ if (!canonical || seen.has(canonical)) continue;
242
+ seen.add(canonical);
243
+ result.push(canonical);
244
+ }
245
+
246
+ return result;
247
+ }
248
+
249
+ /**
250
+ * Resolve the single PRIMARY locale for a user (the first entry from
251
+ * {@link getUserLanguages}), or `undefined` when the user has no supported
252
+ * locale.
253
+ */
254
+ export function getPrimaryLanguage(user: Pick<User, 'languages'> | null | undefined): string | undefined {
255
+ return getUserLanguages(user)[0];
256
+ }
@@ -0,0 +1,392 @@
1
+ /**
2
+ * OAuth 2.0 Authorization Code + PKCE helpers for "Sign in with Oxy" third-party
3
+ * sign-in.
4
+ *
5
+ * Third-party Relying Parties (SPAs, static sites, and native apps that are NOT
6
+ * Oxy first-party) authenticate through the standard OAuth flow against
7
+ * `auth.oxy.so/authorize` — never FedCM, SSO bounces, or Oxy session cookies.
8
+ * Public clients (no secret) prove possession of the authorization code with
9
+ * PKCE (RFC 7636, S256): the RP generates a random `code_verifier`, sends its
10
+ * `code_challenge = BASE64URL(SHA-256(code_verifier))` on the authorize
11
+ * redirect, and later replays the raw verifier on the token exchange.
12
+ *
13
+ * All cross-platform crypto (random bytes, SHA-256) is delegated to the shared
14
+ * `@oxy.so/protocol` platform loaders — the exact primitives the rest of core's
15
+ * crypto already uses — so these helpers run identically on web, Node, and
16
+ * React Native. No `require()`, so the ESM build stays bundler-clean.
17
+ */
18
+
19
+ import { isNodeJS, isReactNative, loadExpoCrypto, loadNodeCrypto, sha256 } from '@oxy.so/protocol';
20
+ import { logger } from '../logger';
21
+
22
+ /** The central Oxy IdP authorization endpoint used by default. */
23
+ export const OXY_AUTHORIZE_URL = 'https://auth.oxy.so/authorize';
24
+
25
+ /** Default OAuth scope requested for a "Sign in with Oxy" third-party flow. */
26
+ export const DEFAULT_OAUTH_SCOPE = 'openid profile';
27
+
28
+ /**
29
+ * Number of random bytes behind a PKCE `code_verifier`. 64 bytes → 86 base64url
30
+ * characters, comfortably inside RFC 7636 §4.1's required 43–128 range.
31
+ */
32
+ const PKCE_VERIFIER_BYTES = 64;
33
+
34
+ /** Number of random bytes behind an OAuth `state` (CSRF) token. */
35
+ const OAUTH_STATE_BYTES = 32;
36
+
37
+ /** RFC 4648 §5 base64url alphabet (URL- and filename-safe, no padding). */
38
+ const BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
39
+
40
+ /** A generated PKCE verifier/challenge pair. */
41
+ export interface PkcePair {
42
+ /** The high-entropy secret replayed on the token exchange (kept client-side). */
43
+ codeVerifier: string;
44
+ /** `BASE64URL(SHA-256(codeVerifier))` — sent on the authorize redirect. */
45
+ codeChallenge: string;
46
+ /** The PKCE transformation method. Always `S256`. */
47
+ method: 'S256';
48
+ }
49
+
50
+ /** Parameters for {@link buildOAuthAuthorizeUrl}. */
51
+ export interface BuildOAuthAuthorizeUrlParams {
52
+ /** Authorize endpoint; defaults to {@link OXY_AUTHORIZE_URL}. */
53
+ authorizeBaseUrl?: string;
54
+ /** The registered `ApplicationCredential` public key (`oxy_dk_…`). */
55
+ clientId: string;
56
+ /** Exact registered redirect URI to return the authorization code to. */
57
+ redirectUri: string;
58
+ /** Requested scope; defaults to {@link DEFAULT_OAUTH_SCOPE}. */
59
+ scope?: string;
60
+ /** Opaque CSRF token from {@link generateOAuthState}. */
61
+ state: string;
62
+ /** The PKCE `codeChallenge` from {@link generatePkcePair}. */
63
+ codeChallenge: string;
64
+ /**
65
+ * OAuth `prompt` parameter — `login` forces a fresh authentication even when
66
+ * the IdP already has a session, `consent` forces the consent screen even for
67
+ * an already-granted scope. Both are ordinary OAuth/OIDC and are here for
68
+ * third-party relying parties building their own authorize link.
69
+ *
70
+ * `'none'` is deliberately NOT in this union. It is the silent-SSO value, and
71
+ * it was the only value this SDK ever sent — from the cold-boot cross-origin
72
+ * restore deleted in #691 phase 7b. Accepting it again would hand consumers a
73
+ * one-line rebuild of the automatic, gesture-less full-page bounce to the IdP
74
+ * that the popup transport exists to eliminate.
75
+ */
76
+ prompt?: 'login' | 'consent';
77
+ /**
78
+ * How the IdP should deliver the authorization response. Omitted (the
79
+ * default) means the ordinary top-level redirect back to `redirectUri`.
80
+ *
81
+ * `web_message` asks the IdP to `postMessage` the result to its opener
82
+ * instead, so a popup sign-in never navigates the relying party's tab. It is
83
+ * a REQUEST, not a guarantee: an IdP with no opener still redirects, which is
84
+ * why the popup transport must handle both outcomes. Only the authorization
85
+ * code, the `state`, and a typed OAuth error ever cross that channel — never
86
+ * a token, device secret, or the PKCE verifier, which the opener keeps.
87
+ */
88
+ responseMode?: 'web_message';
89
+ }
90
+
91
+ /**
92
+ * Cryptographically-secure random bytes, cross-platform.
93
+ *
94
+ * Mirrors the platform gating the rest of core's crypto already uses:
95
+ * `expo-crypto` on React Native, Node's built-in `crypto` on the server, and
96
+ * the Web Crypto API in the browser (also the fallback if Node's `crypto`
97
+ * fails to load in an unusual bundled-Node environment).
98
+ */
99
+ async function getSecureRandomBytes(byteLength: number): Promise<Uint8Array> {
100
+ if (isReactNative()) {
101
+ const crypto = await loadExpoCrypto();
102
+ return Uint8Array.from(await crypto.getRandomBytesAsync(byteLength));
103
+ }
104
+
105
+ if (isNodeJS()) {
106
+ try {
107
+ const nodeCrypto = await loadNodeCrypto();
108
+ return Uint8Array.from(nodeCrypto.randomBytes(byteLength));
109
+ } catch (error) {
110
+ logger.warn(
111
+ '[oxy.oauth] Node crypto unavailable for PKCE random bytes, falling back to Web Crypto',
112
+ { component: 'oauthPkce' },
113
+ error,
114
+ );
115
+ }
116
+ }
117
+
118
+ const bytes = new Uint8Array(byteLength);
119
+ globalThis.crypto.getRandomValues(bytes);
120
+ return bytes;
121
+ }
122
+
123
+ /** Encode raw bytes as unpadded base64url (RFC 4648 §5). */
124
+ function bytesToBase64Url(bytes: Uint8Array): string {
125
+ let output = '';
126
+ for (let i = 0; i < bytes.length; i += 3) {
127
+ const byte0 = bytes[i];
128
+ const hasByte1 = i + 1 < bytes.length;
129
+ const hasByte2 = i + 2 < bytes.length;
130
+ const byte1 = hasByte1 ? bytes[i + 1] : 0;
131
+ const byte2 = hasByte2 ? bytes[i + 2] : 0;
132
+
133
+ output += BASE64URL_ALPHABET[byte0 >> 2];
134
+ output += BASE64URL_ALPHABET[((byte0 & 0x03) << 4) | (byte1 >> 4)];
135
+ if (hasByte1) {
136
+ output += BASE64URL_ALPHABET[((byte1 & 0x0f) << 2) | (byte2 >> 6)];
137
+ }
138
+ if (hasByte2) {
139
+ output += BASE64URL_ALPHABET[byte2 & 0x3f];
140
+ }
141
+ }
142
+ return output;
143
+ }
144
+
145
+ /** Decode a lowercase-hex string into its raw bytes. */
146
+ function hexToBytes(hex: string): Uint8Array {
147
+ const bytes = new Uint8Array(hex.length / 2);
148
+ for (let i = 0; i < bytes.length; i += 1) {
149
+ bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
150
+ }
151
+ return bytes;
152
+ }
153
+
154
+ /**
155
+ * Compute the PKCE S256 `code_challenge` for a given verifier:
156
+ * `BASE64URL(SHA-256(ASCII(codeVerifier)))` (RFC 7636 §4.2). The verifier is
157
+ * base64url (ASCII), so its UTF-8 and ASCII byte encodings are identical.
158
+ *
159
+ * Reuses `@oxy.so/protocol`'s cross-platform {@link sha256} (which returns
160
+ * lowercase hex); the digest bytes are recovered and re-encoded as base64url.
161
+ */
162
+ export async function computeCodeChallenge(codeVerifier: string): Promise<string> {
163
+ const digestHex = await sha256(codeVerifier);
164
+ return bytesToBase64Url(hexToBytes(digestHex));
165
+ }
166
+
167
+ /**
168
+ * Generate a fresh PKCE verifier/challenge pair for an OAuth authorization-code
169
+ * flow. The verifier is 64 random bytes as base64url (86 chars, within RFC 7636
170
+ * §4.1's 43–128 range and drawn only from the unreserved set); the challenge is
171
+ * its S256 transform.
172
+ */
173
+ export async function generatePkcePair(): Promise<PkcePair> {
174
+ const codeVerifier = bytesToBase64Url(await getSecureRandomBytes(PKCE_VERIFIER_BYTES));
175
+ const codeChallenge = await computeCodeChallenge(codeVerifier);
176
+ return { codeVerifier, codeChallenge, method: 'S256' };
177
+ }
178
+
179
+ /**
180
+ * Generate an opaque, single-use OAuth `state` token (32 random bytes as
181
+ * base64url) for CSRF protection across the authorize redirect.
182
+ */
183
+ export async function generateOAuthState(): Promise<string> {
184
+ return bytesToBase64Url(await getSecureRandomBytes(OAUTH_STATE_BYTES));
185
+ }
186
+
187
+ /**
188
+ * Build the `auth.oxy.so/authorize` redirect URL for an OAuth authorization-code
189
+ * + PKCE (S256) flow. Built via the WHATWG `URL` API so a custom
190
+ * `authorizeBaseUrl` that already carries a query string keeps its existing
191
+ * params (the OAuth params are merged in, not clobbered by a naive `?` concat).
192
+ * All values are percent-encoded by `URL.searchParams`.
193
+ */
194
+ export function buildOAuthAuthorizeUrl(params: BuildOAuthAuthorizeUrlParams): string {
195
+ const {
196
+ authorizeBaseUrl = OXY_AUTHORIZE_URL,
197
+ clientId,
198
+ redirectUri,
199
+ scope = DEFAULT_OAUTH_SCOPE,
200
+ state,
201
+ codeChallenge,
202
+ } = params;
203
+
204
+ const url = new URL(authorizeBaseUrl);
205
+ url.searchParams.set('client_id', clientId);
206
+ url.searchParams.set('redirect_uri', redirectUri);
207
+ url.searchParams.set('response_type', 'code');
208
+ url.searchParams.set('state', state);
209
+ url.searchParams.set('scope', scope);
210
+ url.searchParams.set('code_challenge', codeChallenge);
211
+ url.searchParams.set('code_challenge_method', 'S256');
212
+ if (params.prompt) {
213
+ url.searchParams.set('prompt', params.prompt);
214
+ }
215
+ if (params.responseMode) {
216
+ url.searchParams.set('response_mode', params.responseMode);
217
+ }
218
+
219
+ return url.toString();
220
+ }
221
+
222
+ /** `sessionStorage` key for the OAuth CSRF `state` across an authorize redirect. */
223
+ export const OXY_OAUTH_STATE_STORAGE_KEY = 'oxy_oauth_state';
224
+
225
+ /** `sessionStorage` key for the PKCE `code_verifier` across an authorize redirect. */
226
+ export const OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = 'oxy_oauth_code_verifier';
227
+
228
+ /** `sessionStorage` key — the exact `redirect_uri` sent on the authorize request. */
229
+ export const OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = 'oxy.oauth_redirect_uri';
230
+
231
+ /**
232
+ * `sessionStorage` key for the in-app path to return to after an authorize
233
+ * round trip. See {@link persistOAuthReturnPath}.
234
+ */
235
+ export const OXY_OAUTH_RETURN_PATH_STORAGE_KEY = 'oxy.oauth_return_path';
236
+
237
+ /**
238
+ * Normalize a redirect URI to its origin. Official Oxy apps register apex
239
+ * origins (`https://inbox.oxy.so`) — never path-qualified URLs.
240
+ *
241
+ * This is why the path cannot ride along in `redirect_uri`, and why
242
+ * {@link persistOAuthReturnPath} exists: the authorize round trip always lands
243
+ * back on the bare origin, so the page the user was on has to be carried
244
+ * out-of-band and restored on return.
245
+ */
246
+ export function normalizeOAuthRedirectUri(input: string): string {
247
+ try {
248
+ return new URL(input).origin;
249
+ } catch {
250
+ return input;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Collapse `https://app.example/` → `https://app.example` for OAuth binding.
256
+ * Path-qualified redirect URIs are preserved — matches the API token exchange.
257
+ */
258
+ export function canonicalizeOAuthRedirectUri(redirectUri: string): string {
259
+ try {
260
+ const parsed = new URL(redirectUri);
261
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
262
+ return redirectUri;
263
+ }
264
+ if (parsed.pathname === '/' && !parsed.search && !parsed.hash) {
265
+ return parsed.origin;
266
+ }
267
+ return redirectUri;
268
+ } catch {
269
+ return redirectUri;
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Is this a safe same-origin path to restore into the address bar?
275
+ *
276
+ * Only site-relative paths are accepted. `//evil.com` and `/\evil.com` are
277
+ * rejected because browsers resolve both as protocol-relative *absolute* URLs —
278
+ * without this check, anything able to write one key of `sessionStorage` could
279
+ * turn the post-authorize restore into an open redirect.
280
+ */
281
+ function isSafeReturnPath(value: string): boolean {
282
+ if (!value.startsWith('/')) return false;
283
+ if (value.startsWith('//') || value.startsWith('/\\')) return false;
284
+ if (value.length > 2048) return false;
285
+ return true;
286
+ }
287
+
288
+ /**
289
+ * Remember the page the user was on before a full-page authorize redirect.
290
+ *
291
+ * Silent cross-origin restore navigates the whole tab to the IdP and back, and
292
+ * `redirect_uri` must be a registered apex origin, so the browser returns to
293
+ * `/` no matter which page the visit started on. Without this, every deep link
294
+ * — a shared URL, a search result, an ad landing page — silently became the
295
+ * home page for signed-out visitors on their first navigation in a tab.
296
+ *
297
+ * Stored next to the PKCE handshake it belongs to, and cleared by
298
+ * {@link clearOAuthHandshake} so a stale entry can never be applied to an
299
+ * unrelated later navigation.
300
+ */
301
+ export function persistOAuthReturnPath(path: string): void {
302
+ const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
303
+ if (!store || !isSafeReturnPath(path)) return;
304
+ try {
305
+ store.setItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY, path);
306
+ } catch (error) {
307
+ // Non-fatal: the round trip still completes, it just lands on the origin.
308
+ logger.warn(
309
+ 'Could not persist OAuth return path to sessionStorage',
310
+ { component: 'oauthPkce' },
311
+ error,
312
+ );
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Read and remove the persisted return path, or `null` when there is none or
318
+ * it failed validation.
319
+ *
320
+ * Single-use by design: it is consumed on the first return so a leftover value
321
+ * cannot redirect a later navigation in the same tab.
322
+ */
323
+ export function consumeOAuthReturnPath(): string | null {
324
+ const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
325
+ if (!store) return null;
326
+ try {
327
+ const value = store.getItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
328
+ store.removeItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
329
+ if (!value || !isSafeReturnPath(value)) return null;
330
+ return value;
331
+ } catch {
332
+ return null;
333
+ }
334
+ }
335
+
336
+ /** Persist the OAuth handshake for a full-page redirect return (web only). */
337
+ export function persistOAuthHandshake(
338
+ state: string,
339
+ codeVerifier: string,
340
+ redirectUri?: string,
341
+ ): boolean {
342
+ const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
343
+ try {
344
+ if (!store) throw new Error('sessionStorage is unavailable');
345
+ store.setItem(OXY_OAUTH_STATE_STORAGE_KEY, state);
346
+ store.setItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, codeVerifier);
347
+ if (redirectUri) {
348
+ store.setItem(
349
+ OXY_OAUTH_REDIRECT_URI_STORAGE_KEY,
350
+ canonicalizeOAuthRedirectUri(redirectUri),
351
+ );
352
+ }
353
+ return true;
354
+ } catch (error) {
355
+ logger.warn(
356
+ 'Could not persist OAuth handshake to sessionStorage',
357
+ { component: 'oauthPkce' },
358
+ error,
359
+ );
360
+ return false;
361
+ }
362
+ }
363
+
364
+ /** Read the persisted OAuth handshake, or `null` when absent. */
365
+ export function readOAuthHandshake(): {
366
+ state: string;
367
+ codeVerifier: string;
368
+ redirectUri?: string;
369
+ } | null {
370
+ const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
371
+ if (!store) return null;
372
+ const state = store.getItem(OXY_OAUTH_STATE_STORAGE_KEY);
373
+ const codeVerifier = store.getItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
374
+ if (!state || !codeVerifier) return null;
375
+ const redirectUri = store.getItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY) ?? undefined;
376
+ return redirectUri ? { state, codeVerifier, redirectUri } : { state, codeVerifier };
377
+ }
378
+
379
+ /** Drop persisted OAuth handshake keys after a successful or aborted return. */
380
+ export function clearOAuthHandshake(): void {
381
+ const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
382
+ try {
383
+ store?.removeItem(OXY_OAUTH_STATE_STORAGE_KEY);
384
+ store?.removeItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
385
+ store?.removeItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY);
386
+ // The return path belongs to this handshake. Leaving it behind would let a
387
+ // later, unrelated navigation in the same tab be redirected by it.
388
+ store?.removeItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
389
+ } catch {
390
+ // Best-effort cleanup only.
391
+ }
392
+ }