@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,203 @@
1
+ /**
2
+ * App-Data Mixin Tests
3
+ *
4
+ * Exercises the typed helpers around `/users/me/app-data/...`. We stub
5
+ * `makeRequest` so the tests run without a network or a database — what we
6
+ * care about here is request shape (method, URL, body), identifier
7
+ * validation, and response handling (`null` when missing, echo on write).
8
+ *
9
+ * The mixin sits behind `withAuthRetry`, which polls for a token before
10
+ * running the operation. We force a token in via `__resetTokensForTests`'
11
+ * companion path (set access token directly) so the auth wait short-circuits.
12
+ */
13
+
14
+ import { OxyServices } from '../../OxyServices';
15
+ import { OxyAppDataIdentifierError } from '../OxyServices.appData';
16
+
17
+ const setAccessTokenForTest = (oxy: OxyServices): void => {
18
+ // Tokens are managed by HttpService — `hasAccessToken()` is the gate the
19
+ // `withAuthRetry` loop polls. Reaching in via the public httpService and
20
+ // calling setTokens with a dummy avoids us needing to expose new test
21
+ // hooks just for this.
22
+ oxy.httpService.setTokens('test-token');
23
+ };
24
+
25
+ describe('OxyServices.appData', () => {
26
+ let oxy: OxyServices;
27
+ let makeRequestSpy: jest.SpyInstance;
28
+
29
+ beforeEach(() => {
30
+ oxy = new OxyServices({ baseURL: 'http://test.invalid' });
31
+ setAccessTokenForTest(oxy);
32
+ makeRequestSpy = jest.spyOn(oxy, 'makeRequest');
33
+ });
34
+
35
+ afterEach(() => {
36
+ makeRequestSpy.mockRestore();
37
+ });
38
+
39
+ describe('getAppData', () => {
40
+ it('returns the stored value when the API responds with one', async () => {
41
+ makeRequestSpy.mockResolvedValue({ value: { completed: ['intro'] } });
42
+
43
+ const result = await oxy.getAppData<{ completed: string[] }>(
44
+ 'academy',
45
+ 'getting-started',
46
+ );
47
+
48
+ expect(result).toEqual({ completed: ['intro'] });
49
+ expect(makeRequestSpy).toHaveBeenCalledWith(
50
+ 'GET',
51
+ '/users/me/app-data/academy/getting-started',
52
+ undefined,
53
+ expect.objectContaining({ cache: false }),
54
+ );
55
+ });
56
+
57
+ it('returns null when the API responds with `value: null`', async () => {
58
+ makeRequestSpy.mockResolvedValue({ value: null });
59
+ const result = await oxy.getAppData('academy', 'unknown');
60
+ expect(result).toBeNull();
61
+ });
62
+
63
+ it('returns null when the response object is missing `value`', async () => {
64
+ makeRequestSpy.mockResolvedValue({});
65
+ const result = await oxy.getAppData('academy', 'unknown');
66
+ expect(result).toBeNull();
67
+ });
68
+
69
+ it('URL-encodes namespace and key path segments', async () => {
70
+ makeRequestSpy.mockResolvedValue({ value: 'ok' });
71
+ await oxy.getAppData('a-b_c', 'd-e_f');
72
+ // URL-encoding is a no-op for our allowed character set, but we still
73
+ // run through encodeURIComponent — make sure that's wired so the call
74
+ // site doesn't accidentally bypass it later.
75
+ expect(makeRequestSpy.mock.calls[0][1]).toBe('/users/me/app-data/a-b_c/d-e_f');
76
+ });
77
+
78
+ it('throws OxyAppDataIdentifierError for invalid namespace', async () => {
79
+ await expect(oxy.getAppData('Bad Namespace', 'k')).rejects.toBeInstanceOf(
80
+ OxyAppDataIdentifierError,
81
+ );
82
+ expect(makeRequestSpy).not.toHaveBeenCalled();
83
+ });
84
+
85
+ it('throws OxyAppDataIdentifierError for invalid key', async () => {
86
+ await expect(oxy.getAppData('ns', 'Bad/Key')).rejects.toBeInstanceOf(
87
+ OxyAppDataIdentifierError,
88
+ );
89
+ expect(makeRequestSpy).not.toHaveBeenCalled();
90
+ });
91
+
92
+ it('rejects empty identifiers (regex requires at least one char)', async () => {
93
+ await expect(oxy.getAppData('', 'k')).rejects.toBeInstanceOf(
94
+ OxyAppDataIdentifierError,
95
+ );
96
+ await expect(oxy.getAppData('n', '')).rejects.toBeInstanceOf(
97
+ OxyAppDataIdentifierError,
98
+ );
99
+ });
100
+
101
+ it('rejects identifiers longer than 64 chars', async () => {
102
+ const tooLong = 'a'.repeat(65);
103
+ await expect(oxy.getAppData(tooLong, 'k')).rejects.toBeInstanceOf(
104
+ OxyAppDataIdentifierError,
105
+ );
106
+ });
107
+
108
+ it('surfaces API errors (e.g. 401) via withAuthRetry', async () => {
109
+ const err = Object.assign(new Error('Authentication required'), {
110
+ response: { status: 401 },
111
+ });
112
+ makeRequestSpy.mockRejectedValue(err);
113
+ await expect(oxy.getAppData('academy', 'getting-started')).rejects.toThrow();
114
+ });
115
+ });
116
+
117
+ describe('setAppData', () => {
118
+ it('writes the value and returns the server-echoed value', async () => {
119
+ makeRequestSpy.mockResolvedValue({ value: { completed: ['intro'] } });
120
+
121
+ const result = await oxy.setAppData('academy', 'getting-started', {
122
+ completed: ['intro'],
123
+ });
124
+
125
+ expect(result).toEqual({ completed: ['intro'] });
126
+ expect(makeRequestSpy).toHaveBeenCalledWith(
127
+ 'PUT',
128
+ '/users/me/app-data/academy/getting-started',
129
+ { value: { completed: ['intro'] } },
130
+ expect.objectContaining({ cache: false }),
131
+ );
132
+ });
133
+
134
+ it('falls back to the caller value when the server response omits it', async () => {
135
+ makeRequestSpy.mockResolvedValue({});
136
+ const result = await oxy.setAppData('academy', 'k', 'hello');
137
+ expect(result).toBe('hello');
138
+ });
139
+
140
+ it('throws OxyAppDataIdentifierError before issuing a request', async () => {
141
+ await expect(oxy.setAppData('UPPER', 'k', 1)).rejects.toBeInstanceOf(
142
+ OxyAppDataIdentifierError,
143
+ );
144
+ expect(makeRequestSpy).not.toHaveBeenCalled();
145
+ });
146
+ });
147
+
148
+ describe('deleteAppData', () => {
149
+ it('issues a DELETE and resolves', async () => {
150
+ makeRequestSpy.mockResolvedValue(undefined);
151
+
152
+ await expect(oxy.deleteAppData('academy', 'getting-started')).resolves.toBeUndefined();
153
+ expect(makeRequestSpy).toHaveBeenCalledWith(
154
+ 'DELETE',
155
+ '/users/me/app-data/academy/getting-started',
156
+ undefined,
157
+ expect.objectContaining({ cache: false }),
158
+ );
159
+ });
160
+
161
+ it('throws OxyAppDataIdentifierError on invalid identifiers', async () => {
162
+ await expect(oxy.deleteAppData('ns', 'BAD KEY')).rejects.toBeInstanceOf(
163
+ OxyAppDataIdentifierError,
164
+ );
165
+ });
166
+ });
167
+
168
+ describe('listAppData', () => {
169
+ it('returns the entries map from the API', async () => {
170
+ makeRequestSpy.mockResolvedValue({
171
+ entries: {
172
+ 'getting-started': { completed: ['intro'] },
173
+ 'using-oxy-id': { completed: [] },
174
+ },
175
+ });
176
+
177
+ const result = await oxy.listAppData<{ completed: string[] }>('academy');
178
+
179
+ expect(result).toEqual({
180
+ 'getting-started': { completed: ['intro'] },
181
+ 'using-oxy-id': { completed: [] },
182
+ });
183
+ expect(makeRequestSpy).toHaveBeenCalledWith(
184
+ 'GET',
185
+ '/users/me/app-data/academy',
186
+ undefined,
187
+ expect.objectContaining({ cache: false }),
188
+ );
189
+ });
190
+
191
+ it('returns an empty object when the API returns no entries', async () => {
192
+ makeRequestSpy.mockResolvedValue({});
193
+ const result = await oxy.listAppData('academy');
194
+ expect(result).toEqual({});
195
+ });
196
+
197
+ it('throws OxyAppDataIdentifierError on invalid namespace', async () => {
198
+ await expect(oxy.listAppData('Bad Namespace')).rejects.toBeInstanceOf(
199
+ OxyAppDataIdentifierError,
200
+ );
201
+ });
202
+ });
203
+ });
@@ -0,0 +1,47 @@
1
+ import { OxyServices } from '../../OxyServices';
2
+
3
+ interface FetchCall {
4
+ url: string;
5
+ init: RequestInit | undefined;
6
+ }
7
+
8
+ describe('asset fetch credentials', () => {
9
+ const originalFetch = globalThis.fetch;
10
+
11
+ afterEach(() => {
12
+ globalThis.fetch = originalFetch;
13
+ jest.restoreAllMocks();
14
+ });
15
+
16
+ it('omits credentials for arbitrary asset content URLs', async () => {
17
+ const calls: FetchCall[] = [];
18
+ globalThis.fetch = async (input, init) => {
19
+ calls.push({ url: String(input), init });
20
+ return new Response('asset-body', { status: 200 });
21
+ };
22
+
23
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
24
+
25
+ await oxy.fetchAssetContent('https://attacker.oxy.so/cdn/object.txt', 'text');
26
+
27
+ expect(calls).toHaveLength(1);
28
+ expect(calls[0].url).toBe('https://attacker.oxy.so/cdn/object.txt');
29
+ expect(calls[0].init?.credentials).toBe('omit');
30
+ });
31
+
32
+ it('includes credentials for asset content fetched from the configured API origin', async () => {
33
+ const calls: FetchCall[] = [];
34
+ globalThis.fetch = async (input, init) => {
35
+ calls.push({ url: String(input), init });
36
+ return new Response('asset-body', { status: 200 });
37
+ };
38
+
39
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
40
+
41
+ await oxy.fetchAssetContent('https://api.oxy.so/assets/private-file/stream', 'text');
42
+
43
+ expect(calls).toHaveLength(1);
44
+ expect(calls[0].url).toBe('https://api.oxy.so/assets/private-file/stream');
45
+ expect(calls[0].init?.credentials).toBe('include');
46
+ });
47
+ });
@@ -0,0 +1,191 @@
1
+ /**
2
+ * `OxyServices.assetUpload()` multipart-body tests.
3
+ *
4
+ * `assetUpload` accepts three input shapes: a web `File`, a web `Blob`, or a
5
+ * React Native `{ uri, type?, name?, size? }` descriptor. The descriptor path
6
+ * is platform-sensitive:
7
+ *
8
+ * - React Native — RN's FormData reads the file from disk via the uri during
9
+ * the multipart request, so the descriptor is appended as-is.
10
+ * - Web (browser/Node) — the browser's FormData CANNOT read bytes from a plain
11
+ * `{ uri }` object (it would serialize `[object Object]` → the server stores
12
+ * a 0-byte asset). The uri must be materialized into a real `Blob` via
13
+ * `fetch` before appending. An empty fetched blob must throw instead of
14
+ * silently uploading an empty asset.
15
+ *
16
+ * These tests assert exactly which value lands in the FormData `file` part for
17
+ * each platform, and that an empty web source is rejected.
18
+ */
19
+
20
+ import { OxyServices } from '../../OxyServices';
21
+
22
+ /**
23
+ * Captures every `FormData.append` call so a test can inspect the multipart body
24
+ * that `assetUpload` built without sending a real network request.
25
+ */
26
+ function captureUpload(oxy: OxyServices) {
27
+ const appended: Array<{ name: string; value: unknown; fileName?: string }> = [];
28
+ // Capture-only: do NOT delegate to the real (undici) FormData.append. Node's
29
+ // undici rejects a plain { uri } object as not-a-Blob, but real React Native
30
+ // FormData accepts it — the test asserts on captured args, not a built body.
31
+ const appendSpy = jest
32
+ .spyOn(FormData.prototype, 'append')
33
+ .mockImplementation(function (this: FormData, name: string, value: unknown, fileName?: string) {
34
+ appended.push({ name, value, fileName });
35
+ });
36
+
37
+ const requestSpy = jest
38
+ .spyOn(oxy.getClient(), 'request')
39
+ .mockResolvedValue({ file: { id: 'asset123' } } as never);
40
+
41
+ return {
42
+ appended,
43
+ requestSpy,
44
+ restore: () => {
45
+ appendSpy.mockRestore();
46
+ requestSpy.mockRestore();
47
+ },
48
+ };
49
+ }
50
+
51
+ describe('OxyServices.assetUpload — uri descriptor', () => {
52
+ const originalNavigator = (globalThis as { navigator?: unknown }).navigator;
53
+ const originalFetch = globalThis.fetch;
54
+
55
+ afterEach(() => {
56
+ jest.restoreAllMocks();
57
+ if (originalNavigator === undefined) {
58
+ delete (globalThis as { navigator?: unknown }).navigator;
59
+ } else {
60
+ (globalThis as { navigator?: unknown }).navigator = originalNavigator;
61
+ }
62
+ globalThis.fetch = originalFetch;
63
+ });
64
+
65
+ describe('web (NOT React Native)', () => {
66
+ beforeEach(() => {
67
+ // Node/jsdom-like: no React Native navigator → isReactNative() === false.
68
+ delete (globalThis as { navigator?: unknown }).navigator;
69
+ });
70
+
71
+ it('materializes a blob: uri into a real, non-empty Blob before appending', async () => {
72
+ const bytes = new Blob([new Uint8Array([1, 2, 3, 4, 5])], { type: 'image/png' });
73
+ const fetchMock = jest
74
+ .fn()
75
+ .mockResolvedValue({ ok: true, status: 200, blob: async () => bytes });
76
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
77
+
78
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
79
+ const capture = captureUpload(oxy);
80
+
81
+ try {
82
+ await oxy.assetUpload({ uri: 'blob:https://app.test/abc', type: 'image/png', name: 'avatar.png' });
83
+
84
+ expect(fetchMock).toHaveBeenCalledWith('blob:https://app.test/abc');
85
+
86
+ const filePart = capture.appended.find((p) => p.name === 'file');
87
+ expect(filePart).toBeDefined();
88
+ // The appended value is the fetched Blob with real bytes — NOT the { uri } object.
89
+ expect(filePart?.value).toBeInstanceOf(Blob);
90
+ expect((filePart?.value as Blob).size).toBe(5);
91
+ expect((filePart?.value as { uri?: string }).uri).toBeUndefined();
92
+ expect(filePart?.fileName).toBe('avatar.png');
93
+ } finally {
94
+ capture.restore();
95
+ }
96
+ });
97
+
98
+ it('wraps a typeless fetched blob with the descriptor MIME type', async () => {
99
+ const typeless = new Blob([new Uint8Array([9, 9, 9])]); // type === ''
100
+ const fetchMock = jest
101
+ .fn()
102
+ .mockResolvedValue({ ok: true, status: 200, blob: async () => typeless });
103
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
104
+
105
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
106
+ const capture = captureUpload(oxy);
107
+
108
+ try {
109
+ await oxy.assetUpload({ uri: 'data:application/octet-stream;base64,CQkJ', type: 'image/jpeg', name: 'x.jpg' });
110
+
111
+ const filePart = capture.appended.find((p) => p.name === 'file');
112
+ expect(filePart?.value).toBeInstanceOf(Blob);
113
+ expect((filePart?.value as Blob).size).toBe(3);
114
+ expect((filePart?.value as Blob).type).toBe('image/jpeg');
115
+ } finally {
116
+ capture.restore();
117
+ }
118
+ });
119
+
120
+ it('throws "Cannot upload an empty file" when the fetched blob is empty', async () => {
121
+ const empty = new Blob([], { type: 'image/png' });
122
+ const fetchMock = jest
123
+ .fn()
124
+ .mockResolvedValue({ ok: true, status: 200, blob: async () => empty });
125
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
126
+
127
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
128
+ const capture = captureUpload(oxy);
129
+
130
+ try {
131
+ await expect(
132
+ oxy.assetUpload({ uri: 'blob:https://app.test/empty', type: 'image/png', name: 'empty.png' }),
133
+ ).rejects.toThrow('Cannot upload an empty file');
134
+
135
+ // Nothing was sent — the empty source surfaces instead of creating a 0-byte asset.
136
+ expect(capture.requestSpy).not.toHaveBeenCalled();
137
+ } finally {
138
+ capture.restore();
139
+ }
140
+ });
141
+
142
+ it('throws when the uri cannot be fetched (non-ok response)', async () => {
143
+ const fetchMock = jest
144
+ .fn()
145
+ .mockResolvedValue({ ok: false, status: 404, blob: async () => new Blob([]) });
146
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
147
+
148
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
149
+ const capture = captureUpload(oxy);
150
+
151
+ try {
152
+ await expect(
153
+ oxy.assetUpload({ uri: 'https://cdn.test/missing.png', type: 'image/png', name: 'missing.png' }),
154
+ ).rejects.toThrow('Failed to read file from uri (status 404)');
155
+ expect(capture.requestSpy).not.toHaveBeenCalled();
156
+ } finally {
157
+ capture.restore();
158
+ }
159
+ });
160
+ });
161
+
162
+ describe('React Native', () => {
163
+ beforeEach(() => {
164
+ // Make isReactNative() === true: navigator.product === 'ReactNative'.
165
+ (globalThis as { navigator?: unknown }).navigator = { product: 'ReactNative' };
166
+ });
167
+
168
+ it('appends the descriptor as-is and never calls fetch', async () => {
169
+ const fetchMock = jest.fn();
170
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
171
+
172
+ const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
173
+ const capture = captureUpload(oxy);
174
+
175
+ const descriptor = { uri: 'file:///tmp/avatar.png', type: 'image/png', name: 'avatar.png', size: 1024 };
176
+
177
+ try {
178
+ await oxy.assetUpload(descriptor);
179
+
180
+ // RN path: the raw descriptor object lands in the multipart body unchanged.
181
+ const filePart = capture.appended.find((p) => p.name === 'file');
182
+ expect(filePart?.value).toBe(descriptor);
183
+ expect(filePart?.fileName).toBe('avatar.png');
184
+ // No in-JS materialization on RN — FormData reads the file from the uri.
185
+ expect(fetchMock).not.toHaveBeenCalled();
186
+ } finally {
187
+ capture.restore();
188
+ }
189
+ });
190
+ });
191
+ });
@@ -0,0 +1,135 @@
1
+ import type { DeviceBackgroundCredentialResponse } from '@oxy.so/contracts';
2
+ import { OxyServices } from '../../OxyServices';
3
+
4
+ /**
5
+ * Real-stack integration test for `provisionBackgroundCredential`: a genuine
6
+ * `HttpService` (via `OxyServices`) with `global.fetch` stubbed to return the
7
+ * EXACT wire bodies oxy-api sends, rather than a `makeRequest` spy.
8
+ *
9
+ * The unit suite (`OxyServices.deviceBoot.test.ts`) stubs `makeRequest`, so by
10
+ * construction it cannot see either of the two things this file pins:
11
+ *
12
+ * 1. **The `{ data }` envelope.** The route answers
13
+ * `{ data: { deviceId, secret, accountId, expiresAt } }`, and
14
+ * `HttpService.unwrapResponse` strips that outer envelope — so the mixin
15
+ * must validate the FLAT credential and must not read `.data` a second
16
+ * time. A spy returning the flat shape asserts that assumption instead of
17
+ * testing it; the same blind spot produced a P0 in `SessionClient` (see
18
+ * `SessionClient.httpIntegration.test.ts`).
19
+ * 2. **The 404 degrade against the REAL error object.** The `404 → null` path
20
+ * is what keeps a client on a newer SDK than the server from breaking, and
21
+ * it keys on the status surviving whatever `HttpService` throws. A
22
+ * hand-built `Object.assign(new Error(), { status: 404 })` proves only that
23
+ * the branch reads the shape the test itself invented.
24
+ *
25
+ * The stub is URL-aware because a state-changing request through the real stack
26
+ * fetches `GET /csrf-token` FIRST. A blanket stub answers that call too, which
27
+ * both shifts the request under test out of `calls[0]` and (on a non-200 stub)
28
+ * makes the CSRF fetch burn its own retries — so a naive call-count assertion
29
+ * measures CSRF attempts rather than the route.
30
+ */
31
+ const ROUTE = '/session/device/background-credential';
32
+
33
+ const CREDENTIAL: DeviceBackgroundCredentialResponse = {
34
+ deviceId: 'device-real',
35
+ secret: 'bg-secret-from-the-wire',
36
+ accountId: 'acct-1',
37
+ expiresAt: '2030-01-01T00:00:00.000Z',
38
+ };
39
+
40
+ /** The route's success body: the credential under this API's `data` envelope. */
41
+ const ROUTE_BODY = { data: CREDENTIAL };
42
+
43
+ /** oxy-api's 404 body for an unmatched path (server.ts's terminal handler). */
44
+ const NOT_FOUND_BODY = { error: 'NOT_FOUND', message: 'Resource not found' };
45
+
46
+ const jsonResponse = (body: unknown, status: number) =>
47
+ new Response(JSON.stringify(body), {
48
+ status,
49
+ headers: { 'content-type': 'application/json' },
50
+ });
51
+
52
+ /**
53
+ * A syntactically real, far-future access token. It must be a decodable JWT:
54
+ * `HttpService.getAuthHeader` runs `jwtDecode` and sends NO bearer at all when
55
+ * that throws, so an opaque placeholder would silently turn this into an
56
+ * anonymous request and make the bearer assertion below untestable.
57
+ */
58
+ const ACCESS_TOKEN = (() => {
59
+ const segment = (payload: object) => Buffer.from(JSON.stringify(payload)).toString('base64url');
60
+ return [
61
+ segment({ alg: 'none', typ: 'JWT' }),
62
+ segment({ sub: 'acct-1', exp: 4_102_444_800 }), // 2100-01-01
63
+ 'signature-not-verified-client-side',
64
+ ].join('.');
65
+ })();
66
+
67
+ describe('provisionBackgroundCredential over a real HttpService', () => {
68
+ const originalFetch = global.fetch;
69
+
70
+ /** Answers the CSRF preflight properly; answers the route under test with `body`/`status`. */
71
+ const stubFetch = (body: unknown, status: number) => {
72
+ const fetchMock = jest.fn(async (input: unknown) => {
73
+ if (String(input).includes('/csrf-token')) {
74
+ return jsonResponse({ csrfToken: 'csrf-test-token' }, 200);
75
+ }
76
+ return jsonResponse(body, status);
77
+ });
78
+ global.fetch = fetchMock as unknown as typeof fetch;
79
+ return fetchMock;
80
+ };
81
+
82
+ /** Every stubbed call whose URL is the route under test (i.e. not the CSRF preflight). */
83
+ const routeCalls = (fetchMock: jest.Mock) =>
84
+ fetchMock.mock.calls.filter(([input]) => String(input).includes(ROUTE));
85
+
86
+ const client = () => {
87
+ const oxy = new OxyServices({ baseURL: 'http://api.test.invalid' });
88
+ // Bearer required: the server derives both the deviceId and the account
89
+ // from it, and this call (unlike the device-secret mint) does not skipAuth.
90
+ oxy.setTokens(ACCESS_TOKEN);
91
+ return oxy;
92
+ };
93
+
94
+ afterEach(() => {
95
+ global.fetch = originalFetch;
96
+ });
97
+
98
+ it('unwraps the { data } envelope and returns the flat credential', async () => {
99
+ stubFetch(ROUTE_BODY, 200);
100
+
101
+ const result = await client().provisionBackgroundCredential();
102
+
103
+ // Would be `{ data: {...} }` if the envelope were not unwrapped, and would
104
+ // throw (contract validation failure) if `.data` were read twice.
105
+ expect(result).toEqual(CREDENTIAL);
106
+ expect(result?.secret).toBe('bg-secret-from-the-wire');
107
+ });
108
+
109
+ it('sends a POST to the route with NO body and a bearer', async () => {
110
+ const fetchMock = stubFetch(ROUTE_BODY, 200);
111
+
112
+ await client().provisionBackgroundCredential();
113
+
114
+ const calls = routeCalls(fetchMock);
115
+ expect(calls).toHaveLength(1);
116
+ const [, init] = calls[0] as unknown as [string, RequestInit];
117
+ expect(init.method).toBe('POST');
118
+ // No body at all — not `'undefined'`, not `'{}'`. The server derives the
119
+ // deviceId and the account from the bearer; anything sent here would be
120
+ // ignored at best and mass-assignment surface at worst.
121
+ expect(init.body ?? null).toBeNull();
122
+ expect(new Headers(init.headers).get('authorization')).toBe(`Bearer ${ACCESS_TOKEN}`);
123
+ });
124
+
125
+ it('returns null on the real 404 response, without retrying the route', async () => {
126
+ const fetchMock = stubFetch(NOT_FOUND_BODY, 404);
127
+
128
+ await expect(client().provisionBackgroundCredential()).resolves.toBeNull();
129
+
130
+ // 4xx is not retried (`retryAsync`'s default shouldRetry), so an absent
131
+ // endpoint costs exactly one request to the route — the degrade must not
132
+ // burn a backoff loop on every provision attempt.
133
+ expect(routeCalls(fetchMock)).toHaveLength(1);
134
+ });
135
+ });
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The chains client — what it puts on the wire.
3
+ *
4
+ * There is exactly one piece of real logic here and it is the read's query
5
+ * string: joining two lists, and including `since`/`limit` only when the caller
6
+ * gave them. Everything else is a pass-through, so these cases pin the part that
7
+ * can actually be wrong rather than restating the method bodies.
8
+ *
9
+ * `makeServiceRequest` is stubbed because it belongs to the auth mixin and has
10
+ * its own suites; what matters here is the method, path and payload it is handed.
11
+ */
12
+
13
+ import { OxyServicesChainsMixin } from '../OxyServices.chains';
14
+
15
+ type Call = { method: string; url: string; data?: unknown };
16
+
17
+ /** A minimal host carrying the mixin, with the service transport recorded. */
18
+ function client(): { calls: Call[]; api: any } {
19
+ const calls: Call[] = [];
20
+ class Base {
21
+ makeServiceRequest(method: string, url: string, data?: unknown) {
22
+ calls.push({ method, url, data });
23
+ return Promise.resolve({ records: [], nextCursor: null });
24
+ }
25
+ }
26
+ const Mixed = OxyServicesChainsMixin(Base as any);
27
+ return { calls, api: new (Mixed as any)() };
28
+ }
29
+
30
+ describe('appendChainRecord', () => {
31
+ it('POSTs the record to /chains/records untouched', async () => {
32
+ const { calls, api } = client();
33
+
34
+ await api.appendChainRecord({
35
+ oxyUserId: 'u1',
36
+ collection: 'app.mention.feed.post',
37
+ rkey: 'p1',
38
+ record: { text: 'hi' },
39
+ });
40
+
41
+ expect(calls).toEqual([
42
+ {
43
+ method: 'POST',
44
+ url: '/chains/records',
45
+ data: {
46
+ oxyUserId: 'u1',
47
+ collection: 'app.mention.feed.post',
48
+ rkey: 'p1',
49
+ record: { text: 'hi' },
50
+ },
51
+ },
52
+ ]);
53
+ });
54
+ });
55
+
56
+ describe('readChainRecords', () => {
57
+ it('joins authors and collections into one comma-separated query each', async () => {
58
+ const { calls, api } = client();
59
+
60
+ await api.readChainRecords({
61
+ oxyUserIds: ['u1', 'u2'],
62
+ collections: ['app.mention.feed.post', 'app.mention.feed.like'],
63
+ });
64
+
65
+ const url = new URL(`http://x${calls[0].url}`);
66
+ expect(calls[0].method).toBe('GET');
67
+ expect(url.pathname).toBe('/chains/records');
68
+ expect(url.searchParams.get('authors')).toBe('u1,u2');
69
+ expect(url.searchParams.get('collections')).toBe('app.mention.feed.post,app.mention.feed.like');
70
+ });
71
+
72
+ it('omits since and limit when the caller gave neither', async () => {
73
+ // A `since=` or `limit=` sent as an empty string is not the same request —
74
+ // the server validates both, so an always-present key would 400 a first page.
75
+ const { calls, api } = client();
76
+
77
+ await api.readChainRecords({ oxyUserIds: ['u1'], collections: ['app.mention.feed.post'] });
78
+
79
+ const url = new URL(`http://x${calls[0].url}`);
80
+ expect(url.searchParams.has('since')).toBe(false);
81
+ expect(url.searchParams.has('limit')).toBe(false);
82
+ });
83
+
84
+ it('sends since and limit when it did', async () => {
85
+ const { calls, api } = client();
86
+
87
+ await api.readChainRecords({
88
+ oxyUserIds: ['u1'],
89
+ collections: ['app.mention.feed.post'],
90
+ since: 'Y3Vyc29y',
91
+ limit: 25,
92
+ });
93
+
94
+ const url = new URL(`http://x${calls[0].url}`);
95
+ expect(url.searchParams.get('since')).toBe('Y3Vyc29y');
96
+ expect(url.searchParams.get('limit')).toBe('25');
97
+ });
98
+
99
+ it('escapes a cursor that is not URL-safe', async () => {
100
+ // Cursors are opaque to the caller, so this client must not assume the
101
+ // encoding the server happens to use today.
102
+ const { calls, api } = client();
103
+
104
+ await api.readChainRecords({
105
+ oxyUserIds: ['u1'],
106
+ collections: ['app.mention.feed.post'],
107
+ since: 'a+b/c=',
108
+ });
109
+
110
+ const url = new URL(`http://x${calls[0].url}`);
111
+ expect(url.searchParams.get('since')).toBe('a+b/c=');
112
+ });
113
+ });