@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,1137 @@
1
+ /**
2
+ * User Management Methods Mixin
3
+ */
4
+ import type {
5
+ User,
6
+ Notification,
7
+ NotificationPreferences,
8
+ UserPreferences,
9
+ SearchProfilesResponse,
10
+ PaginationInfo,
11
+ PrivacySettings,
12
+ } from '../models/interfaces';
13
+ import type {
14
+ UserNameResponse,
15
+ UserProfileUpdate,
16
+ RecommendationRequest,
17
+ RecommendationItem,
18
+ ThemePreference,
19
+ } from '@oxy.so/contracts';
20
+ import { recommendationRequestSchema } from '@oxy.so/contracts';
21
+ import type { OxyServicesBase } from '../OxyServices.base';
22
+ import {
23
+ buildQueryParams,
24
+ buildPaginationParams,
25
+ type PaginationParams,
26
+ type FollowGraphParams,
27
+ } from '../utils/apiUtils';
28
+ import { KeyManager } from '../crypto/keyManager';
29
+ import { SignatureService } from '../crypto/signatureService';
30
+ import { normalizeUserIdentity, normalizeUserIdentityOrNull } from '../utils/userIdentity';
31
+ import { evictOxyIdentityCache } from '../utils/identityCacheSweep';
32
+ import { evictOxyAccountForestCache } from '../utils/accountCacheSweep';
33
+ import { logger } from '../logger';
34
+ import { extractErrorStatus } from '../utils/errorUtils';
35
+
36
+ /**
37
+ * Maximum number of ids sent per `POST /users/by-ids` request. Matches the
38
+ * server-side batch cap; larger inputs are split into multiple chunked calls.
39
+ */
40
+ const USERS_BY_IDS_CHUNK_SIZE = 100;
41
+
42
+ /**
43
+ * Maximum number of ids sent per `POST /users/follow-status/bulk` request.
44
+ * Matches the server-side `MAX_BULK_FOLLOW` cap; larger inputs are split into
45
+ * multiple chunked calls whose result maps are merged.
46
+ */
47
+ const FOLLOW_STATUS_CHUNK_SIZE = 200;
48
+
49
+ /**
50
+ * Response of the single follow/unfollow toggle route
51
+ * (`POST /users/:id/follow` and `DELETE /users/:id/follow`). The route reports a
52
+ * status message, which side of the toggle was applied, and the post-write
53
+ * follower/following counts for the affected users.
54
+ */
55
+ export interface FollowMutationResult {
56
+ /** Human-readable status message. */
57
+ message: string;
58
+ /** Which side of the toggle was applied, when reported by the route. */
59
+ action?: 'follow' | 'unfollow';
60
+ /** Post-write counts, when reported by the route. */
61
+ counts?: {
62
+ /** The target user's follower count after the write. */
63
+ followers: number;
64
+ /** The viewer's following count after the write. */
65
+ following: number;
66
+ };
67
+ }
68
+
69
+ /** Per-user outcome returned by `POST /users/follow/bulk`. */
70
+ export interface BulkFollowEntry {
71
+ /** The user ID that was processed. */
72
+ userId: string;
73
+ /** Whether the follow was applied (or already in place) without error. */
74
+ success: boolean;
75
+ /** Whether the caller was already following this user before the request. */
76
+ alreadyFollowing: boolean;
77
+ }
78
+
79
+ /** Response shape of `POST /users/follow/bulk`. */
80
+ export interface BulkFollowResult {
81
+ /** Per-user outcomes, in request order. */
82
+ results: BulkFollowEntry[];
83
+ /** Number of users newly followed by this request. */
84
+ followedCount: number;
85
+ }
86
+
87
+ /**
88
+ * The authenticated viewer's OWN social graph, ids-only — the response of
89
+ * `GET /users/me/graph`. Consolidates the accounts the viewer follows, the
90
+ * subset who follow back (mutuals), and the accounts the viewer has blocked
91
+ * into one payload so a consumer can fetch its whole viewer graph in a single
92
+ * round trip instead of three. Each list is server-bounded; bare ids only (no
93
+ * hydrated DTOs) because the consumer hydrates/ranks itself.
94
+ */
95
+ export interface ViewerGraph {
96
+ /** Accounts the viewer follows (most-recent first, bounded). */
97
+ followingIds: string[];
98
+ /** Accounts the viewer follows that ALSO follow the viewer back (bounded). */
99
+ mutualIds: string[];
100
+ /** Accounts the viewer has blocked (bounded). */
101
+ blockedIds: string[];
102
+ /** Accounts the viewer has restricted (bounded). */
103
+ restrictedIds: string[];
104
+ }
105
+
106
+ /** Per-user outcome returned by `POST /users/unfollow/bulk`. */
107
+ export interface BulkUnfollowEntry {
108
+ /** The user ID that was processed. */
109
+ userId: string;
110
+ /** Whether the unfollow was applied (or already absent) without error. */
111
+ success: boolean;
112
+ /** Whether the caller was following this user before the request. */
113
+ wasFollowing: boolean;
114
+ }
115
+
116
+ /** Response shape of `POST /users/unfollow/bulk`. */
117
+ export interface BulkUnfollowResult {
118
+ /** Per-user outcomes, in request order. */
119
+ results: BulkUnfollowEntry[];
120
+ /** Number of users newly unfollowed by this request. */
121
+ unfollowedCount: number;
122
+ }
123
+
124
+ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T) {
125
+ return class extends Base {
126
+ constructor(...args: any[]) {
127
+ super(...(args as [any]));
128
+ }
129
+
130
+ /**
131
+ * Service-token request, implemented by the auth mixin earlier in the
132
+ * composition pipeline (see `mixins/index.ts`). The user mixin is typed
133
+ * against `OxyServicesBase`, which does not carry the auth mixin's methods,
134
+ * so this `declare` surfaces the inherited runtime method to TypeScript
135
+ * without re-implementing it. Used by `getUsersByIds` to authenticate the
136
+ * server-to-server `/users/by-ids` bulk fetch with a bearer service token.
137
+ */
138
+ declare makeServiceRequest: <R = unknown>(
139
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
140
+ url: string,
141
+ data?: unknown,
142
+ userId?: string,
143
+ ) => Promise<R>;
144
+
145
+ /**
146
+ * Raw service credentials stored by `configureServiceAuth()` on the auth
147
+ * mixin (earlier in the pipeline). Surfaced here via `declare` — for the
148
+ * same typing reason as `makeServiceRequest` above — so `getUsersByIds` can
149
+ * detect whether this instance is service-configured (a backend) and pick
150
+ * the bearer-service path, or fall back to the user-session path (a browser/
151
+ * RN client). Both are `null` until `configureServiceAuth(apiKey, apiSecret)`
152
+ * is called.
153
+ */
154
+ declare _serviceApiKey: string | null;
155
+ declare _serviceApiSecret: string | null;
156
+
157
+ /**
158
+ * Get profile by username.
159
+ *
160
+ * @param username - The profile's username.
161
+ * @param options.cache - Defaults to `true` (5-minute TTL), matching prior
162
+ * behavior. Pass `{ cache: false }` to force a registry-fresh read: the
163
+ * request bypasses BOTH the cache lookup and the post-fetch cache write
164
+ * (see {@link HttpService.request}'s `cache` handling), so it neither
165
+ * serves nor overwrites any entry already cached for this key — a
166
+ * previously cached response (if one exists) is left in place until its
167
+ * own TTL expires or is explicitly invalidated elsewhere. Use this when a
168
+ * caller must observe a just-written change (e.g. a privacy/consent flag)
169
+ * that would otherwise be masked by the TTL window.
170
+ */
171
+ async getProfileByUsername(username: string, options?: { cache?: boolean }): Promise<User> {
172
+ try {
173
+ const user = await this.makeRequest<User>('GET', `/profiles/username/${encodeURIComponent(username)}`, undefined, {
174
+ cache: options?.cache ?? true,
175
+ cacheTTL: 5 * 60 * 1000, // 5 minutes cache for profiles
176
+ });
177
+ return normalizeUserIdentity(user);
178
+ } catch (error) {
179
+ throw this.handleError(error);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Lightweight username lookup for login flows.
185
+ * Returns minimal public info: exists, color, avatar, name.displayName.
186
+ * Faster than getProfileByUsername — no stats, no formatting.
187
+ */
188
+ async lookupUsername(username: string): Promise<{
189
+ exists: boolean;
190
+ username: string;
191
+ color: string | null;
192
+ avatar: string | null;
193
+ name: UserNameResponse;
194
+ }> {
195
+ return await this.makeRequest('GET', `/auth/lookup/${encodeURIComponent(username)}`, undefined, {
196
+ cache: true,
197
+ cacheTTL: 60 * 1000, // 1 minute cache
198
+ // Public login-flow lookup (pre-session) — skip the bearer preflight.
199
+ skipAuth: true,
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Search user profiles
205
+ */
206
+ async searchProfiles(query: string, pagination?: PaginationParams): Promise<SearchProfilesResponse> {
207
+ try {
208
+ const response = await this.makeRequest<SearchProfilesResponse>(
209
+ 'GET',
210
+ '/profiles/search',
211
+ buildQueryParams({ query, ...pagination }),
212
+ {
213
+ cache: true,
214
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
215
+ }
216
+ );
217
+
218
+ if (
219
+ typeof response !== 'object' ||
220
+ response === null ||
221
+ !Array.isArray(response.data)
222
+ ) {
223
+ throw new Error('Unexpected search response format');
224
+ }
225
+
226
+ const paginationInfo: PaginationInfo = response.pagination ?? {
227
+ total: response.data.length,
228
+ limit: pagination?.limit ?? response.data.length,
229
+ offset: pagination?.offset ?? 0,
230
+ hasMore: response.data.length === (pagination?.limit ?? response.data.length) &&
231
+ (pagination?.limit ?? response.data.length) > 0,
232
+ };
233
+
234
+ return {
235
+ data: response.data,
236
+ pagination: paginationInfo,
237
+ };
238
+ } catch (error) {
239
+ throw this.handleError(error);
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Resolve a fediverse handle to an Oxy user profile.
245
+ * Performs WebFinger discovery and returns the user, or null if not found.
246
+ * @param handle - Fediverse handle (e.g. "@user@mastodon.social" or "user@domain")
247
+ */
248
+ async resolveProfile(handle: string): Promise<User | null> {
249
+ try {
250
+ const result = await this.makeRequest<User | null>('GET', '/profiles/resolve', {
251
+ handle,
252
+ }, {
253
+ cache: true,
254
+ // 5 min — matches the sibling profile fetches (getUserById /
255
+ // getProfileByUsername). /profiles/resolve now carries the viewer-relative
256
+ // `relationship` (isFollowing / followsYou); `followsYou` is target→viewer,
257
+ // so the viewer can't self-refresh it via a follow action. The GET cache is
258
+ // identity-scoped (keyed by the caller's access-token identity tag → no
259
+ // cross-viewer poison), so a short TTL keeps "Follows you" reasonably fresh
260
+ // without a per-call bypass. The identity fields alone tolerate a longer
261
+ // window, but a 24h stale relationship is not acceptable.
262
+ cacheTTL: 5 * 60 * 1000,
263
+ });
264
+ return normalizeUserIdentityOrNull(result);
265
+ } catch (error: unknown) {
266
+ // Discovery is best-effort: an unresolvable handle is a normal "not
267
+ // found", not an exceptional condition, so the contract stays `null`.
268
+ // But a 404 (handle genuinely absent) must be distinguishable from a
269
+ // network/server failure (WebFinger upstream down, 5xx) for debugging —
270
+ // both used to be swallowed identically. Log at `debug` with context so
271
+ // the distinction is observable without turning expected misses into
272
+ // noise. Return contract is unchanged.
273
+ const status = extractErrorStatus(error);
274
+ const isNotFound = status === 404;
275
+ logger.debug(
276
+ isNotFound ? 'resolveProfile: handle not found' : 'resolveProfile: discovery failed',
277
+ {
278
+ method: 'resolveProfile',
279
+ handle,
280
+ status,
281
+ notFound: isNotFound,
282
+ error: error instanceof Error ? error.message : String(error),
283
+ },
284
+ );
285
+ return null;
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Resolve (find or create) a non-local user. All user creation for
291
+ * external accounts (federated, agent, automated) goes through this
292
+ * method — calling services never write user data directly.
293
+ */
294
+ async resolveExternalUser(data: {
295
+ type: 'federated' | 'agent' | 'automated';
296
+ username: string;
297
+ actorUri?: string;
298
+ domain?: string;
299
+ displayName?: string;
300
+ avatar?: string;
301
+ bio?: string;
302
+ ownerId?: string;
303
+ }): Promise<User> {
304
+ return normalizeUserIdentity(await this.makeRequest<User>('PUT', '/users/resolve', data));
305
+ }
306
+
307
+ /**
308
+ * Get profile recommendations.
309
+ *
310
+ * Public discovery read — works WITHOUT authentication. The SDK attaches the
311
+ * access token automatically when one is available (personalized via
312
+ * mutual-connection overlap), and falls back to popular public profiles when
313
+ * the caller is logged out. This deliberately does NOT use `withAuthRetry`,
314
+ * which would throw an authentication timeout for logged-out callers before
315
+ * the request is ever sent.
316
+ *
317
+ * Routing (two server endpoints, both validated against the same
318
+ * `@oxy.so/contracts` recommendation schemas so the wire shape cannot drift):
319
+ *
320
+ * - **GET `/profiles/recommendations`** (cached) is used for the simple,
321
+ * back-compatible case: no options at all, or only `excludeTypes` and/or
322
+ * `limit`. `excludeTypes` is sent as a comma-joined query param and
323
+ * `limit` as a numeric query param — byte-for-byte identical to the legacy
324
+ * behavior so every existing caller keeps the same request and cache key.
325
+ * - **POST `/profiles/recommendations`** is used whenever any of the scored
326
+ * (v2) fields — `boosts`, `excludeIds`, `signalWeights`, `clientId`, or
327
+ * `offset` — is present. The full options object is validated with
328
+ * `recommendationRequestSchema` and sent as the request body. The POST is
329
+ * cached by the HttpService keyed on the serialized body, so repeated
330
+ * identical scored requests are deduplicated/cached just like the GET path.
331
+ *
332
+ * @param options - {@link RecommendationRequest} from `@oxy.so/contracts`.
333
+ * Omitted entirely (or `{ excludeTypes }`) preserves the legacy GET path.
334
+ */
335
+ async getProfileRecommendations(
336
+ options?: RecommendationRequest,
337
+ ): Promise<RecommendationItem[]> {
338
+ // The scored (v2) POST path is selected when any field beyond the
339
+ // legacy GET-supported `excludeTypes`/`limit` pair is present.
340
+ const usesScoredPath = Boolean(
341
+ options &&
342
+ (options.clientId !== undefined ||
343
+ options.offset !== undefined ||
344
+ (options.excludeIds?.length ?? 0) > 0 ||
345
+ (options.boosts?.length ?? 0) > 0 ||
346
+ options.signalWeights !== undefined),
347
+ );
348
+
349
+ try {
350
+ if (usesScoredPath && options) {
351
+ // Validate the full request against the shared contract before
352
+ // sending. A malformed payload is surfaced to the caller rather than
353
+ // bounced by the server, and the parsed value strips unknown keys.
354
+ const body = recommendationRequestSchema.parse(options);
355
+ return await this.makeRequest<RecommendationItem[]>(
356
+ 'POST',
357
+ '/profiles/recommendations',
358
+ body,
359
+ // Cache keyed on the serialized body (see HttpService.generateCacheKey)
360
+ // so identical scored requests are served from cache, matching the
361
+ // GET path's caching semantics.
362
+ { cache: true },
363
+ );
364
+ }
365
+
366
+ const params: Record<string, string> = {};
367
+ if (options?.excludeTypes?.length) {
368
+ params.excludeTypes = options.excludeTypes.join(',');
369
+ }
370
+ if (options?.limit !== undefined) {
371
+ params.limit = String(options.limit);
372
+ }
373
+ return await this.makeRequest<RecommendationItem[]>(
374
+ 'GET',
375
+ '/profiles/recommendations',
376
+ Object.keys(params).length > 0 ? params : undefined,
377
+ { cache: true },
378
+ );
379
+ } catch (error: unknown) {
380
+ // Recommendations are a discovery read; failures are surfaced to the
381
+ // caller (contract unchanged: rethrow via `handleError`). Add debug
382
+ // observability first so a recurring upstream failure is diagnosable
383
+ // — distinguishing an auth/transport problem from a server 5xx.
384
+ logger.debug('getProfileRecommendations: discovery read failed', {
385
+ method: 'getProfileRecommendations',
386
+ path: usesScoredPath ? 'POST' : 'GET',
387
+ excludeTypes: options?.excludeTypes,
388
+ status: extractErrorStatus(error),
389
+ error: error instanceof Error ? error.message : String(error),
390
+ });
391
+ throw this.handleError(error);
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Get profiles similar to a given user, based on co-follower overlap.
397
+ */
398
+ async getSimilarProfiles(
399
+ userId: string,
400
+ limitOrParams?: number | { limit?: number; offset?: number },
401
+ ): Promise<User[]> {
402
+ const pagination =
403
+ typeof limitOrParams === 'number' ? { limit: limitOrParams } : limitOrParams ?? {};
404
+ const params = buildQueryParams(pagination);
405
+ const users = await this.makeRequest<User[]>('GET', `/profiles/${userId}/similar`, params, {
406
+ cache: true,
407
+ cacheTTL: 5 * 60 * 1000, // 5 min cache
408
+ });
409
+ return users.map((user) => normalizeUserIdentity(user));
410
+ }
411
+
412
+ /**
413
+ * Get user by ID.
414
+ *
415
+ * @param userId - The target user's id.
416
+ * @param options.cache - Defaults to `true` (5-minute TTL), matching prior
417
+ * behavior. Pass `{ cache: false }` to force a registry-fresh read: the
418
+ * request bypasses BOTH the cache lookup and the post-fetch cache write
419
+ * (see {@link HttpService.request}'s `cache` handling), so it neither
420
+ * serves nor overwrites any entry already cached for this key — a
421
+ * previously cached response (if one exists) is left in place until its
422
+ * own TTL expires or is explicitly invalidated elsewhere. Use this when a
423
+ * caller must observe a just-written change (e.g. a privacy/consent flag)
424
+ * that would otherwise be masked by the TTL window.
425
+ */
426
+ async getUserById(userId: string, options?: { cache?: boolean }): Promise<User> {
427
+ try {
428
+ const user = await this.makeRequest<User>('GET', `/users/${userId}`, undefined, {
429
+ cache: options?.cache ?? true,
430
+ cacheTTL: 5 * 60 * 1000, // 5 minutes cache
431
+ });
432
+ return normalizeUserIdentity(user);
433
+ } catch (error) {
434
+ throw this.handleError(error);
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Fetch many users by id in one round-trip per chunk.
440
+ *
441
+ * Built for feed/hydration call sites that would otherwise issue one
442
+ * `getUserById` request per unique author (the classic M+1). Ids are
443
+ * deduplicated and validated (empty/blank ids dropped) before being split
444
+ * into chunks of {@link USERS_BY_IDS_CHUNK_SIZE} and POSTed to
445
+ * `/users/by-ids` as `{ ids }`. The server returns the matched users as a
446
+ * flat `User[]` (order is not guaranteed and the caller is expected to map
447
+ * by `id`); each is run through `normalizeUserIdentity`, matching
448
+ * `getUserById`.
449
+ *
450
+ * **Dual-mode auth.** `/users/by-ids` is `optionalUserOrServiceAuth` on
451
+ * oxy-api: it accepts a service token, a user session, or an anonymous
452
+ * caller, and returns the SAME public `{ data: PublicUserProfile[] }`
453
+ * payload (canonical `name.displayName` + `_count`) in every case — no
454
+ * viewer-specific fields. This method picks the path automatically:
455
+ * - **Service-configured host (backend):** when `configureServiceAuth(apiKey,
456
+ * apiSecret)` has been called, the chunk is fetched via `makeServiceRequest`
457
+ * (attaches `Authorization: Bearer <serviceToken>`). This is the
458
+ * server-to-server feed/notification hydration path (e.g. Mention's
459
+ * `PostHydrationService`) and is unchanged.
460
+ * - **Plain client (browser / React Native with a user session):** when no
461
+ * service credentials are configured, the chunk is fetched via
462
+ * `makeRequest`, which attaches the configured user bearer. oxy-api's CSRF
463
+ * middleware skips bearer-authenticated writes, and `makeRequest` only
464
+ * fetches a CSRF token for cookie-only (no-bearer) state-changing requests,
465
+ * so the user-bearer POST is sent without CSRF and succeeds. Previously
466
+ * this method always used the service path, so every client-side caller
467
+ * silently received `[]` because `getServiceToken()` had no credentials.
468
+ *
469
+ * Both paths run results through `normalizeUserIdentity` and unwrap the
470
+ * API's `{ data }` envelope identically (`makeServiceRequest` is literally
471
+ * `makeRequest` plus a bearer service header).
472
+ *
473
+ * Resilience: chunks are independent. A failed chunk is logged and skipped
474
+ * — the method returns every user that resolved successfully rather than
475
+ * discarding the whole call on one chunk's failure. An empty/whitespace-only
476
+ * input resolves immediately with `[]` and performs no network call.
477
+ *
478
+ * Not cached at the SDK layer: the response is keyed on a multi-id POST body
479
+ * (low hit rate) and the backend maintains its own per-id Redis cache.
480
+ */
481
+ async getUsersByIds(ids: string[]): Promise<User[]> {
482
+ const uniqueIds = Array.from(
483
+ new Set(ids.filter((id): id is string => typeof id === 'string' && id.trim().length > 0)),
484
+ );
485
+ if (uniqueIds.length === 0) {
486
+ return [];
487
+ }
488
+
489
+ const chunks: string[][] = [];
490
+ for (let i = 0; i < uniqueIds.length; i += USERS_BY_IDS_CHUNK_SIZE) {
491
+ chunks.push(uniqueIds.slice(i, i + USERS_BY_IDS_CHUNK_SIZE));
492
+ }
493
+
494
+ // A backend that called configureServiceAuth() uses the bearer-service
495
+ // path; any other caller (browser / RN with a user session) uses the
496
+ // user-bearer path. See the method doc for why the user path is CSRF-safe.
497
+ const useServiceAuth = Boolean(this._serviceApiKey && this._serviceApiSecret);
498
+
499
+ // Run chunks concurrently; a single chunk failure must not sink the rest.
500
+ const settled = await Promise.all(
501
+ chunks.map(async (chunk): Promise<User[]> => {
502
+ try {
503
+ const users = useServiceAuth
504
+ ? await this.makeServiceRequest<User[]>('POST', '/users/by-ids', { ids: chunk })
505
+ : await this.makeRequest<User[]>('POST', '/users/by-ids', { ids: chunk }, { cache: false });
506
+ return Array.isArray(users) ? users.map((user) => normalizeUserIdentity(user)) : [];
507
+ } catch (error: unknown) {
508
+ logger.warn('getUsersByIds: chunk failed, continuing with remaining chunks', {
509
+ method: 'getUsersByIds',
510
+ mode: useServiceAuth ? 'service' : 'user',
511
+ chunkSize: chunk.length,
512
+ status: extractErrorStatus(error),
513
+ error: error instanceof Error ? error.message : String(error),
514
+ });
515
+ return [];
516
+ }
517
+ }),
518
+ );
519
+
520
+ return settled.flat();
521
+ }
522
+
523
+ /**
524
+ * Get current user
525
+ */
526
+ async getCurrentUser(): Promise<User> {
527
+ return this.withAuthRetry(async () => {
528
+ const user = await this.makeRequest<User>('GET', '/users/me', undefined, {
529
+ cache: true,
530
+ cacheTTL: 1 * 60 * 1000, // 1 minute cache for current user
531
+ });
532
+ return normalizeUserIdentity(user);
533
+ }, 'getCurrentUser');
534
+ }
535
+
536
+ /**
537
+ * Update user profile.
538
+ *
539
+ * Invalidates the SDK-side response cache for every endpoint that can
540
+ * return this user — the list is owned by {@link evictOxyIdentityCache}, so
541
+ * a new identity read is added in one place instead of to each writer
542
+ * separately (this method's own hand-written copy had already drifted from
543
+ * the server-side one, missing `GET /auth/lookup/*` and
544
+ * `GET /profiles/resolve`). The account forest (`GET /accounts` and the
545
+ * caller's own detail row) is swept too — a personal account IS this user,
546
+ * and `AccountNode.account` embeds the whole profile — from the list that
547
+ * {@link evictOxyAccountForestCache} owns, for the same reason: the accounts
548
+ * mixin writes those keys as well, and two hand-written copies drift.
549
+ *
550
+ * TanStack Query handles offline queuing automatically.
551
+ */
552
+ async updateProfile(updates: UserProfileUpdate): Promise<User> {
553
+ try {
554
+ const result = normalizeUserIdentity(
555
+ await this.makeRequest<User>('PUT', '/users/me', updates, { cache: false }),
556
+ );
557
+
558
+ evictOxyIdentityCache(this, result?.id);
559
+ evictOxyAccountForestCache(this, result?.id);
560
+
561
+ return result;
562
+ } catch (error) {
563
+ const errorMessage = error instanceof Error ? error.message : String(error);
564
+ const status = extractErrorStatus(error);
565
+
566
+ // Check if it's an authentication error (401)
567
+ const isAuthError = status === 401 ||
568
+ errorMessage.includes('Authentication required') ||
569
+ errorMessage.includes('Invalid or missing authorization header');
570
+
571
+ // If authentication error and we don't have a token, this might be an offline session
572
+ // The caller should handle syncing the session before retrying
573
+ if (isAuthError && !this.hasValidToken()) {
574
+ // Re-throw with a specific message so the caller knows to sync
575
+ throw new Error('AUTH_REQUIRED_OFFLINE_SESSION: Session needs to be synced to get a token');
576
+ }
577
+
578
+ throw this.handleError(error);
579
+ }
580
+ }
581
+
582
+ /**
583
+ * Get privacy settings for a user
584
+ * @param userId - The user ID (defaults to current user)
585
+ */
586
+ async getPrivacySettings(userId?: string): Promise<PrivacySettings> {
587
+ try {
588
+ const id = userId || (await this.getCurrentUser()).id;
589
+ return await this.makeRequest<PrivacySettings>('GET', `/privacy/${id}/privacy`, undefined, {
590
+ cache: true,
591
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
592
+ });
593
+ } catch (error) {
594
+ throw this.handleError(error);
595
+ }
596
+ }
597
+
598
+ /**
599
+ * Update privacy settings.
600
+ *
601
+ * Invalidates the cached `GET /privacy/<id>/privacy` response (the exact
602
+ * key `getPrivacySettings` reads, scoped to the same `id`) after the write.
603
+ * `getPrivacySettings` caches for ~2 minutes (identity-scoped); without
604
+ * busting that entry, a follow-up read within the TTL window returns the
605
+ * pre-update settings. `clearCacheEntry` deletes every identity-scoped
606
+ * variant of the key.
607
+ * @param settings - Partial privacy settings object
608
+ * @param userId - The user ID (defaults to current user)
609
+ */
610
+ async updatePrivacySettings(settings: Partial<PrivacySettings>, userId?: string): Promise<PrivacySettings> {
611
+ try {
612
+ const id = userId || (await this.getCurrentUser()).id;
613
+ const result = await this.makeRequest<PrivacySettings>('PATCH', `/privacy/${id}/privacy`, settings, {
614
+ cache: false,
615
+ });
616
+ // Privacy settings ride the user DTO, so every identity read goes stale
617
+ // too — same key list as any other profile write.
618
+ evictOxyIdentityCache(this, id);
619
+ this.clearCacheEntry(`GET:/privacy/${id}/privacy`);
620
+ return result;
621
+ } catch (error) {
622
+ throw this.handleError(error);
623
+ }
624
+ }
625
+
626
+ /**
627
+ * Update the authenticated user's notification preferences.
628
+ *
629
+ * Thin wrapper over `updateProfile` that constrains the patch to known
630
+ * notification channels — same persistence path, same cache invalidation,
631
+ * but type-safe at the call site.
632
+ */
633
+ async updateNotificationPreferences(
634
+ preferences: Partial<NotificationPreferences>
635
+ ): Promise<User> {
636
+ return this.updateProfile({ notificationPreferences: preferences });
637
+ }
638
+
639
+ /**
640
+ * Update the authenticated user's general preferences (language, theme,
641
+ * reduce-motion, timezone). Persisted on the User document via
642
+ * `PUT /users/me` — same cache-invalidation behaviour as `updateProfile`.
643
+ */
644
+ async updateUserPreferences(
645
+ preferences: Partial<UserPreferences>
646
+ ): Promise<User> {
647
+ return this.updateProfile({ userPreferences: preferences });
648
+ }
649
+
650
+ /**
651
+ * Update the authenticated user's portable theme preference (light/dark/
652
+ * system + Bloom color-preset key). Persisted on the User document via the
653
+ * SAME `PUT /users/me` settings path as the other preferences — same cache
654
+ * invalidation — so the next cold boot serves it on the self/session payload
655
+ * with no extra network call. The full object is written (both `mode` and
656
+ * `colorPreset` are required by the API).
657
+ */
658
+ async updateThemePreference(
659
+ themePreference: ThemePreference
660
+ ): Promise<User> {
661
+ return this.updateProfile({ themePreference });
662
+ }
663
+
664
+ /**
665
+ * Request account verification
666
+ */
667
+ async requestAccountVerification(reason: string, evidence?: string): Promise<{ message: string; requestId: string }> {
668
+ try {
669
+ return await this.makeRequest<{ message: string; requestId: string }>('POST', '/users/verify/request', {
670
+ reason,
671
+ evidence,
672
+ }, { cache: false });
673
+ } catch (error) {
674
+ throw this.handleError(error);
675
+ }
676
+ }
677
+
678
+ /**
679
+ * Download account data export
680
+ */
681
+ async downloadAccountData(format: 'json' | 'csv' = 'json'): Promise<Blob> {
682
+ try {
683
+ // Use httpService for blob responses (it handles blob responses automatically)
684
+ const result = await this.getClient().request<Blob>({
685
+ method: 'GET',
686
+ url: `/users/me/data`,
687
+ params: { format },
688
+ cache: false,
689
+ responseType: 'blob',
690
+ });
691
+
692
+ return result;
693
+ } catch (error) {
694
+ throw this.handleError(error);
695
+ }
696
+ }
697
+
698
+ /**
699
+ * Delete account permanently.
700
+ *
701
+ * Signs `delete:{publicKey}:{timestamp}` with the locally-stored identity
702
+ * private key and submits the signature alongside the confirmation text
703
+ * (must equal the user's username). The signature is the cryptographic
704
+ * proof of ownership — only the device holding the private key can issue
705
+ * a valid signature, so no password is required.
706
+ *
707
+ * @param confirmText - Must equal the user's username (verified server-side)
708
+ * @throws If no identity is stored on this device, or signing fails
709
+ */
710
+ async deleteAccount(confirmText: string): Promise<{ message: string }> {
711
+ try {
712
+ const publicKey = await KeyManager.getPublicKey();
713
+ if (!publicKey) {
714
+ throw new Error('No identity found on this device. Account deletion requires the device that holds your identity key.');
715
+ }
716
+
717
+ const timestamp = Date.now();
718
+ const message = `delete:${publicKey}:${timestamp}`;
719
+ const signature = await SignatureService.sign(message);
720
+
721
+ return await this.makeRequest<{ message: string }>('DELETE', '/users/me', {
722
+ signature,
723
+ timestamp,
724
+ confirmText,
725
+ }, { cache: false });
726
+ } catch (error) {
727
+ throw this.handleError(error);
728
+ }
729
+ }
730
+
731
+
732
+ /**
733
+ * Invalidate every cached read a follow/unfollow write invalidates.
734
+ *
735
+ * Shared by the four mutation entry points (`followUser`, `unfollowUser`,
736
+ * `followUsers`, `unfollowUsers`) so they can never drift on which caches a
737
+ * write busts.
738
+ *
739
+ * The follower/following/mutuals LISTS are cleared by PREFIX rather than by
740
+ * exact key. Those reads are paginated and ordered, so one logical list is
741
+ * spread across many content-addressed keys
742
+ * (`GET:/users/<id>/followers:{"limit":"20","offset":"40","sort":"oldest"}`);
743
+ * an exact-key clear would only bust whichever page/sort variant happened to
744
+ * be read last and would leave every other page stale. `clearCacheByPrefix`
745
+ * deletes all of them, and all identity-scoped variants of each.
746
+ */
747
+ invalidateFollowGraphCaches(targetUserIds: string[]): void {
748
+ for (const id of targetUserIds) {
749
+ this.clearCacheEntry(`GET:/users/${id}/follow-status`);
750
+ // Profile fetches embed viewer-relative `relationship` — bust so a
751
+ // remount doesn't serve a stale isFollowing for up to 5 minutes.
752
+ this.clearCacheEntry(`GET:/users/${id}`);
753
+ // The target gained/lost a follower, and the viewer's presence in the
754
+ // target's "followers you know" set changed with it.
755
+ this.clearCacheByPrefix(`GET:/users/${id}/followers`);
756
+ this.clearCacheByPrefix(`GET:/users/${id}/mutuals`);
757
+ }
758
+ this.clearCacheByPrefix('GET:/profiles/username/');
759
+ this.clearCacheByPrefix('GET:/profiles/resolve');
760
+ // The write changed the viewer's OWN following list and graph.
761
+ const viewerId = this.getCurrentUserId();
762
+ if (viewerId) {
763
+ this.clearCacheByPrefix(`GET:/users/${viewerId}/following`);
764
+ }
765
+ this.clearCacheEntry('GET:/users/me/graph');
766
+ }
767
+
768
+ /**
769
+ * Follow a user.
770
+ *
771
+ * Invalidates the cached `GET /users/<id>/follow-status` response after
772
+ * the write. `getFollowStatus` caches for ~1 minute (identity-scoped);
773
+ * without busting that entry, a `FollowButton` that remounts within the
774
+ * TTL window re-reads the STALE pre-write status and reverts the optimistic
775
+ * UI (the "follow resets after navigating away and back" bug).
776
+ * `clearCacheEntry` deletes every identity-scoped variant of the key.
777
+ */
778
+ async followUser(userId: string): Promise<FollowMutationResult> {
779
+ try {
780
+ const result = await this.makeRequest<FollowMutationResult>('POST', `/users/${userId}/follow`, undefined, { cache: false });
781
+ this.invalidateFollowGraphCaches([userId]);
782
+ return result;
783
+ } catch (error) {
784
+ throw this.handleError(error);
785
+ }
786
+ }
787
+
788
+ /**
789
+ * Follow multiple users in a single request.
790
+ *
791
+ * POSTs `/users/follow/bulk` with `{ userIds }` (server caps the batch at
792
+ * 200). Returns the per-user outcomes and the count of users newly
793
+ * followed. An empty `userIds` array resolves immediately with an empty
794
+ * result and performs no network call.
795
+ */
796
+ async followUsers(userIds: string[]): Promise<BulkFollowResult> {
797
+ if (userIds.length === 0) {
798
+ return { results: [], followedCount: 0 };
799
+ }
800
+ try {
801
+ const result = await this.makeRequest<BulkFollowResult>('POST', '/users/follow/bulk', { userIds }, { cache: false });
802
+ this.invalidateFollowGraphCaches(userIds);
803
+ return result;
804
+ } catch (error) {
805
+ throw this.handleError(error);
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Unfollow multiple users in a single request.
811
+ *
812
+ * POSTs `/users/unfollow/bulk` with `{ userIds }` (server caps the batch at
813
+ * 200). Returns the per-user outcomes and the count of users newly
814
+ * unfollowed. An empty `userIds` array resolves immediately with an empty
815
+ * result and performs no network call.
816
+ */
817
+ async unfollowUsers(userIds: string[]): Promise<BulkUnfollowResult> {
818
+ if (userIds.length === 0) {
819
+ return { results: [], unfollowedCount: 0 };
820
+ }
821
+ try {
822
+ const result = await this.makeRequest<BulkUnfollowResult>('POST', '/users/unfollow/bulk', { userIds }, { cache: false });
823
+ this.invalidateFollowGraphCaches(userIds);
824
+ return result;
825
+ } catch (error) {
826
+ throw this.handleError(error);
827
+ }
828
+ }
829
+
830
+ /**
831
+ * Unfollow a user
832
+ */
833
+ async unfollowUser(userId: string): Promise<FollowMutationResult> {
834
+ try {
835
+ const result = await this.makeRequest<FollowMutationResult>('DELETE', `/users/${userId}/follow`, undefined, { cache: false });
836
+ this.invalidateFollowGraphCaches([userId]);
837
+ return result;
838
+ } catch (error) {
839
+ throw this.handleError(error);
840
+ }
841
+ }
842
+
843
+ /**
844
+ * Get follow status
845
+ */
846
+ async getFollowStatus(userId: string): Promise<{ isFollowing: boolean }> {
847
+ try {
848
+ return await this.makeRequest('GET', `/users/${userId}/follow-status`, undefined, {
849
+ cache: true,
850
+ cacheTTL: 1 * 60 * 1000, // 1 minute cache
851
+ });
852
+ } catch (error) {
853
+ throw this.handleError(error);
854
+ }
855
+ }
856
+
857
+ /**
858
+ * Resolve the viewer's follow status for MANY users in one round-trip per
859
+ * chunk. Built for list UIs (a page of `FollowButton`s) that would otherwise
860
+ * fire one `getFollowStatus` per button (the classic N+1).
861
+ *
862
+ * Ids are deduplicated and validated (empty/blank ids dropped), split into
863
+ * chunks of {@link FOLLOW_STATUS_CHUNK_SIZE} (the server's bulk cap), and
864
+ * POSTed to `/users/follow-status/bulk` as `{ userIds }`. The per-chunk
865
+ * `{ statuses }` maps are merged into one `Record<string, boolean>` covering
866
+ * every requested id — ids the viewer does not follow come back `false`.
867
+ *
868
+ * Uncached (`{ cache: false }`): the UI store owns follow-status freshness
869
+ * and writes optimistically on every mutation, so an SDK cache here would
870
+ * serve a stale status right after a follow/unfollow. An empty/whitespace-
871
+ * only input resolves immediately with `{}` and performs no network call.
872
+ */
873
+ async getFollowStatuses(userIds: string[]): Promise<Record<string, boolean>> {
874
+ const uniqueIds = Array.from(
875
+ new Set(userIds.filter((id): id is string => typeof id === 'string' && id.trim().length > 0)),
876
+ );
877
+ if (uniqueIds.length === 0) {
878
+ return {};
879
+ }
880
+
881
+ const chunks: string[][] = [];
882
+ for (let i = 0; i < uniqueIds.length; i += FOLLOW_STATUS_CHUNK_SIZE) {
883
+ chunks.push(uniqueIds.slice(i, i + FOLLOW_STATUS_CHUNK_SIZE));
884
+ }
885
+
886
+ try {
887
+ const responses = await Promise.all(
888
+ chunks.map((chunk) =>
889
+ this.makeRequest<{ statuses: Record<string, boolean> }>(
890
+ 'POST',
891
+ '/users/follow-status/bulk',
892
+ { userIds: chunk },
893
+ { cache: false },
894
+ ),
895
+ ),
896
+ );
897
+
898
+ const merged: Record<string, boolean> = {};
899
+ for (const response of responses) {
900
+ Object.assign(merged, response?.statuses ?? {});
901
+ }
902
+ return merged;
903
+ } catch (error) {
904
+ throw this.handleError(error);
905
+ }
906
+ }
907
+
908
+ /**
909
+ * Get user followers.
910
+ *
911
+ * `sort` orders the underlying follow edges — `recent` (newest first, the
912
+ * server default) or `oldest`. Because the response is cached and the cache
913
+ * key is content-addressed on the query params, each `limit`/`offset`/`sort`
914
+ * combination is its own entry.
915
+ */
916
+ async getUserFollowers(
917
+ userId: string,
918
+ pagination?: FollowGraphParams
919
+ ): Promise<{ followers: User[]; total: number; hasMore: boolean }> {
920
+ try {
921
+ const params = buildQueryParams(pagination || {});
922
+ const response = await this.makeRequest<{ data: User[]; pagination: { total: number; hasMore: boolean } }>('GET', `/users/${userId}/followers`, params, {
923
+ cache: true,
924
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
925
+ });
926
+ return {
927
+ followers: response.data || [],
928
+ total: response.pagination.total,
929
+ hasMore: response.pagination.hasMore,
930
+ };
931
+ } catch (error) {
932
+ throw this.handleError(error);
933
+ }
934
+ }
935
+
936
+ /**
937
+ * Get user following. `sort` behaves as in {@link getUserFollowers}.
938
+ */
939
+ async getUserFollowing(
940
+ userId: string,
941
+ pagination?: FollowGraphParams
942
+ ): Promise<{ following: User[]; total: number; hasMore: boolean }> {
943
+ try {
944
+ const params = buildQueryParams(pagination || {});
945
+ const response = await this.makeRequest<{ data: User[]; pagination: { total: number; hasMore: boolean } }>('GET', `/users/${userId}/following`, params, {
946
+ cache: true,
947
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
948
+ });
949
+ return {
950
+ following: response.data || [],
951
+ total: response.pagination.total,
952
+ hasMore: response.pagination.hasMore,
953
+ };
954
+ } catch (error) {
955
+ throw this.handleError(error);
956
+ }
957
+ }
958
+
959
+ /**
960
+ * Get user mutuals ("followers you know" — users the authenticated viewer
961
+ * follows who also follow `userId`). The viewer is derived server-side from auth.
962
+ */
963
+ async getUserMutuals(
964
+ userId: string,
965
+ pagination?: FollowGraphParams
966
+ ): Promise<{ mutuals: User[]; total: number; hasMore: boolean }> {
967
+ try {
968
+ const params = buildQueryParams(pagination || {});
969
+ const response = await this.makeRequest<{ data: User[]; pagination: { total: number; hasMore: boolean } }>('GET', `/users/${userId}/mutuals`, params, {
970
+ cache: true,
971
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
972
+ });
973
+ return {
974
+ mutuals: response.data || [],
975
+ total: response.pagination.total,
976
+ hasMore: response.pagination.hasMore,
977
+ };
978
+ } catch (error) {
979
+ throw this.handleError(error);
980
+ }
981
+ }
982
+
983
+ /**
984
+ * Get the authenticated VIEWER's OWN mutual-follow user ids — the accounts the
985
+ * viewer follows that ALSO follow the viewer back (a bidirectional follow
986
+ * edge). The viewer is derived server-side from the SDK's auth token (never a
987
+ * param), so there is no target id to pass.
988
+ *
989
+ * Returns a bounded, lean list of ids meant to SEED a "Mutuals" feed (the
990
+ * consumer hydrates/ranks the posts itself) — distinct from
991
+ * {@link getUserMutuals}, which returns hydrated "followers you know" DTOs
992
+ * about ANOTHER profile. An anonymous caller resolves to an empty array.
993
+ */
994
+ async getMutualUserIds(
995
+ params?: { limit?: number }
996
+ ): Promise<string[]> {
997
+ try {
998
+ const query = buildPaginationParams(params || {});
999
+ const response = await this.makeRequest<{ data: string[] }>('GET', '/users/mutual-ids', query, {
1000
+ cache: true,
1001
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
1002
+ });
1003
+ return response.data || [];
1004
+ } catch (error) {
1005
+ throw this.handleError(error);
1006
+ }
1007
+ }
1008
+
1009
+ /**
1010
+ * Get the authenticated VIEWER's bounded "follows-of-follows" user ids — the
1011
+ * union of the accounts followed by the accounts the viewer follows (a
1012
+ * two-hop walk of the follow graph), MINUS the viewer's own follows and the
1013
+ * viewer themselves. The viewer is derived server-side from the SDK's auth
1014
+ * token (never a param), so there is no target id to pass.
1015
+ *
1016
+ * Returns a bounded, lean list of ids meant to SEED a friends-of-friends
1017
+ * feed (the consumer hydrates/ranks the posts itself), ordered by frequency
1018
+ * (accounts followed by more of the viewer's follows first), then recency.
1019
+ * An anonymous caller resolves to an empty array. Mirrors
1020
+ * {@link getMutualUserIds}'s caching posture.
1021
+ */
1022
+ async getFollowsOfFollowsIds(
1023
+ params?: { limit?: number }
1024
+ ): Promise<string[]> {
1025
+ try {
1026
+ const query = buildPaginationParams(params || {});
1027
+ const response = await this.makeRequest<{ data: string[] }>('GET', '/users/follows-of-follows-ids', query, {
1028
+ cache: true,
1029
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
1030
+ });
1031
+ return response.data || [];
1032
+ } catch (error) {
1033
+ throw this.handleError(error);
1034
+ }
1035
+ }
1036
+
1037
+ /**
1038
+ * Get the authenticated VIEWER's OWN social graph — the accounts they follow,
1039
+ * the subset who follow back (mutuals), and the accounts they have blocked —
1040
+ * as ONE ids-only payload. The viewer is derived server-side from the SDK's
1041
+ * auth token (never a param).
1042
+ *
1043
+ * Consolidates what were three separate round trips (`getUserFollowing` /
1044
+ * `getMutualUserIds` / `getBlockedUsers`) into a single request so a consumer
1045
+ * can prime its whole viewer graph at once. Mirrors {@link getMutualUserIds}'s
1046
+ * caching posture (2-minute identity-scoped cache); the follow/unfollow/block/
1047
+ * unblock write methods bust this entry so a local mutation is reflected
1048
+ * immediately. An anonymous caller resolves to empty lists.
1049
+ */
1050
+ async getViewerGraph(): Promise<ViewerGraph> {
1051
+ try {
1052
+ const response = await this.makeRequest<{ data: ViewerGraph }>('GET', '/users/me/graph', undefined, {
1053
+ cache: true,
1054
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
1055
+ });
1056
+ const graph = response.data;
1057
+ return {
1058
+ followingIds: graph?.followingIds || [],
1059
+ mutualIds: graph?.mutualIds || [],
1060
+ blockedIds: graph?.blockedIds || [],
1061
+ restrictedIds: graph?.restrictedIds || [],
1062
+ };
1063
+ } catch (error) {
1064
+ throw this.handleError(error);
1065
+ }
1066
+ }
1067
+
1068
+ /**
1069
+ * Get notifications
1070
+ */
1071
+ async getNotifications(): Promise<Notification[]> {
1072
+ return this.withAuthRetry(async () => {
1073
+ return await this.makeRequest<Notification[]>('GET', '/notifications', undefined, {
1074
+ cache: false, // Don't cache notifications - always get fresh data
1075
+ });
1076
+ }, 'getNotifications');
1077
+ }
1078
+
1079
+ /**
1080
+ * Get unread notification count
1081
+ */
1082
+ async getUnreadCount(): Promise<number> {
1083
+ try {
1084
+ const res = await this.makeRequest<{ count: number }>('GET', '/notifications/unread-count', undefined, {
1085
+ cache: false, // Don't cache unread count - always get fresh data
1086
+ });
1087
+ return res.count;
1088
+ } catch (error) {
1089
+ throw this.handleError(error);
1090
+ }
1091
+ }
1092
+
1093
+ /**
1094
+ * Create notification
1095
+ */
1096
+ async createNotification(data: Partial<Notification>): Promise<Notification> {
1097
+ try {
1098
+ return await this.makeRequest<Notification>('POST', '/notifications', data, { cache: false });
1099
+ } catch (error) {
1100
+ throw this.handleError(error);
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * Mark notification as read
1106
+ */
1107
+ async markNotificationAsRead(notificationId: string): Promise<void> {
1108
+ try {
1109
+ await this.makeRequest('PUT', `/notifications/${notificationId}/read`, undefined, { cache: false });
1110
+ } catch (error) {
1111
+ throw this.handleError(error);
1112
+ }
1113
+ }
1114
+
1115
+ /**
1116
+ * Mark all notifications as read
1117
+ */
1118
+ async markAllNotificationsAsRead(): Promise<void> {
1119
+ try {
1120
+ await this.makeRequest('PUT', '/notifications/read-all', undefined, { cache: false });
1121
+ } catch (error) {
1122
+ throw this.handleError(error);
1123
+ }
1124
+ }
1125
+
1126
+ /**
1127
+ * Delete notification
1128
+ */
1129
+ async deleteNotification(notificationId: string): Promise<void> {
1130
+ try {
1131
+ await this.makeRequest('DELETE', `/notifications/${notificationId}`, undefined, { cache: false });
1132
+ } catch (error) {
1133
+ throw this.handleError(error);
1134
+ }
1135
+ }
1136
+ };
1137
+ }