@oxy.so/core 4.5.0-oxy-one.0 → 4.5.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 (285) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/HttpService.js +63 -19
  3. package/dist/cjs/api/accounts.js +10 -3
  4. package/dist/cjs/api/agency.js +3 -1
  5. package/dist/cjs/api/apps.js +15 -4
  6. package/dist/cjs/api/assets.js +49 -12
  7. package/dist/cjs/api/auth.js +37 -11
  8. package/dist/cjs/api/billing.js +24 -7
  9. package/dist/cjs/api/civic.js +14 -4
  10. package/dist/cjs/api/follows.js +9 -2
  11. package/dist/cjs/api/identity.js +31 -9
  12. package/dist/cjs/api/nodes.js +3 -1
  13. package/dist/cjs/api/privacy.js +8 -2
  14. package/dist/cjs/api/session.js +15 -4
  15. package/dist/cjs/api/store.js +15 -5
  16. package/dist/cjs/api/topics.js +10 -3
  17. package/dist/cjs/api/users.js +16 -4
  18. package/dist/cjs/boot/sessionColdBoot.js +40 -13
  19. package/dist/cjs/constants/version.js +6 -6
  20. package/dist/cjs/crypto/identityMarker.js +3 -1
  21. package/dist/cjs/crypto/identityProof.js +6 -1
  22. package/dist/cjs/crypto/keyManager.js +87 -32
  23. package/dist/cjs/crypto/recoveryPhrase.js +2 -2
  24. package/dist/cjs/crypto/signatureService.js +3 -4
  25. package/dist/cjs/i18n/index.js +16 -3
  26. package/dist/cjs/i18n/locales/ar-SA.js +366 -366
  27. package/dist/cjs/i18n/locales/ca-ES.js +366 -366
  28. package/dist/cjs/i18n/locales/de-DE.js +366 -366
  29. package/dist/cjs/i18n/locales/en-US.js +1994 -1994
  30. package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
  31. package/dist/cjs/i18n/locales/fr-FR.js +366 -366
  32. package/dist/cjs/i18n/locales/it-IT.js +366 -366
  33. package/dist/cjs/i18n/locales/ja-JP.js +366 -366
  34. package/dist/cjs/i18n/locales/ko-KR.js +366 -366
  35. package/dist/cjs/i18n/locales/pt-PT.js +366 -366
  36. package/dist/cjs/i18n/locales/zh-CN.js +366 -366
  37. package/dist/cjs/inference/OxyInferenceClient.js +12 -18
  38. package/dist/cjs/models/interfaces.js +11 -11
  39. package/dist/cjs/server/OxyServer.js +86 -28
  40. package/dist/cjs/server/agentAccount.js +6 -2
  41. package/dist/cjs/server/aliaMachineCredential.js +39 -14
  42. package/dist/cjs/server/auth.js +5 -4
  43. package/dist/cjs/server/capabilityTicket.js +5 -5
  44. package/dist/cjs/server/index.js +2 -1
  45. package/dist/cjs/server/liveCapabilityTicket.js +4 -1
  46. package/dist/cjs/server/middleware.js +61 -42
  47. package/dist/cjs/server/namespaces.js +28 -7
  48. package/dist/cjs/server/requesterAssertion.js +36 -30
  49. package/dist/cjs/server/serviceTokenLifetime.js +7 -4
  50. package/dist/cjs/server/traffic.js +53 -14
  51. package/dist/cjs/server/trafficNodeHttp.js +33 -8
  52. package/dist/cjs/server/workloadIdentity.client.js +4 -0
  53. package/dist/cjs/server/workloadIdentity.js +28 -1
  54. package/dist/cjs/session/SessionClient.js +54 -17
  55. package/dist/cjs/session/accountDelegationAccess.js +4 -6
  56. package/dist/cjs/session/accountDialogController.js +1 -1
  57. package/dist/cjs/session/authStateStore.js +18 -9
  58. package/dist/cjs/session/identityPin.js +6 -2
  59. package/dist/cjs/session/refresh.js +81 -23
  60. package/dist/cjs/session/sharedDeviceCredential.js +28 -15
  61. package/dist/cjs/shared/utils/errorUtils.js +1 -1
  62. package/dist/cjs/shared/utils/networkUtils.js +1 -1
  63. package/dist/cjs/shared/utils/themeUtils.js +1 -1
  64. package/dist/cjs/utils/accountCacheSweep.js +3 -1
  65. package/dist/cjs/utils/accountUtils.js +1 -3
  66. package/dist/cjs/utils/asyncUtils.js +1 -1
  67. package/dist/cjs/utils/authHelpers.js +2 -1
  68. package/dist/cjs/utils/avatarUtils.js +4 -2
  69. package/dist/cjs/utils/cache.js +1 -1
  70. package/dist/cjs/utils/cacheKey.js +3 -1
  71. package/dist/cjs/utils/deviceManager.js +4 -4
  72. package/dist/cjs/utils/errorUtils.js +6 -4
  73. package/dist/cjs/utils/languageUtils.js +404 -57
  74. package/dist/cjs/utils/profileLinks.js +1 -3
  75. package/dist/cjs/utils/requestUtils.js +7 -1
  76. package/dist/cjs/utils/userIdentity.js +1 -1
  77. package/dist/cjs/utils/validationUtils.js +5 -6
  78. package/dist/esm/HttpService.js +64 -20
  79. package/dist/esm/api/accounts.js +11 -4
  80. package/dist/esm/api/agency.js +3 -1
  81. package/dist/esm/api/apps.js +15 -4
  82. package/dist/esm/api/assets.js +49 -12
  83. package/dist/esm/api/auth.js +37 -11
  84. package/dist/esm/api/billing.js +25 -8
  85. package/dist/esm/api/civic.js +14 -4
  86. package/dist/esm/api/follows.js +9 -2
  87. package/dist/esm/api/identity.js +31 -9
  88. package/dist/esm/api/nodes.js +3 -1
  89. package/dist/esm/api/privacy.js +8 -2
  90. package/dist/esm/api/session.js +15 -4
  91. package/dist/esm/api/store.js +15 -5
  92. package/dist/esm/api/topics.js +10 -3
  93. package/dist/esm/api/users.js +16 -4
  94. package/dist/esm/boot/sessionColdBoot.js +41 -14
  95. package/dist/esm/constants/version.js +6 -6
  96. package/dist/esm/crypto/identityMarker.js +3 -1
  97. package/dist/esm/crypto/identityProof.js +6 -1
  98. package/dist/esm/crypto/internal.js +1 -1
  99. package/dist/esm/crypto/keyManager.js +87 -32
  100. package/dist/esm/crypto/recoveryPhrase.js +2 -2
  101. package/dist/esm/crypto/signatureService.js +3 -4
  102. package/dist/esm/i18n/index.js +16 -3
  103. package/dist/esm/i18n/locales/ar-SA.js +366 -366
  104. package/dist/esm/i18n/locales/ca-ES.js +366 -366
  105. package/dist/esm/i18n/locales/de-DE.js +366 -366
  106. package/dist/esm/i18n/locales/en-US.js +1994 -1994
  107. package/dist/esm/i18n/locales/es-ES.js +1994 -1994
  108. package/dist/esm/i18n/locales/fr-FR.js +366 -366
  109. package/dist/esm/i18n/locales/it-IT.js +366 -366
  110. package/dist/esm/i18n/locales/ja-JP.js +366 -366
  111. package/dist/esm/i18n/locales/ko-KR.js +366 -366
  112. package/dist/esm/i18n/locales/pt-PT.js +366 -366
  113. package/dist/esm/i18n/locales/zh-CN.js +366 -366
  114. package/dist/esm/index.js +1 -1
  115. package/dist/esm/inference/OxyInferenceClient.js +13 -19
  116. package/dist/esm/models/interfaces.js +11 -11
  117. package/dist/esm/server/OxyServer.js +87 -29
  118. package/dist/esm/server/agentAccount.js +6 -2
  119. package/dist/esm/server/aliaMachineCredential.js +39 -14
  120. package/dist/esm/server/auth.js +6 -5
  121. package/dist/esm/server/capabilityTicket.js +6 -6
  122. package/dist/esm/server/index.js +5 -5
  123. package/dist/esm/server/liveCapabilityTicket.js +6 -3
  124. package/dist/esm/server/middleware.js +62 -43
  125. package/dist/esm/server/namespaces.js +31 -10
  126. package/dist/esm/server/requesterAssertion.js +36 -30
  127. package/dist/esm/server/serviceTokenLifetime.js +7 -4
  128. package/dist/esm/server/traffic.js +55 -16
  129. package/dist/esm/server/trafficNodeHttp.js +34 -9
  130. package/dist/esm/server/userInvalidation.js +1 -1
  131. package/dist/esm/server/workloadIdentity.client.js +3 -0
  132. package/dist/esm/server/workloadIdentity.js +27 -1
  133. package/dist/esm/session/SessionClient.js +54 -17
  134. package/dist/esm/session/accountDelegationAccess.js +5 -7
  135. package/dist/esm/session/accountDialogController.js +1 -1
  136. package/dist/esm/session/authStateStore.js +18 -9
  137. package/dist/esm/session/identityPin.js +6 -2
  138. package/dist/esm/session/refresh.js +82 -24
  139. package/dist/esm/session/sharedDeviceCredential.js +28 -15
  140. package/dist/esm/shared/utils/errorUtils.js +1 -1
  141. package/dist/esm/shared/utils/networkUtils.js +1 -1
  142. package/dist/esm/shared/utils/themeUtils.js +1 -1
  143. package/dist/esm/utils/accountCacheSweep.js +3 -1
  144. package/dist/esm/utils/accountUtils.js +1 -3
  145. package/dist/esm/utils/asyncUtils.js +1 -1
  146. package/dist/esm/utils/authHelpers.js +2 -1
  147. package/dist/esm/utils/avatarUtils.js +4 -2
  148. package/dist/esm/utils/cache.js +1 -1
  149. package/dist/esm/utils/cacheKey.js +3 -1
  150. package/dist/esm/utils/deviceManager.js +4 -4
  151. package/dist/esm/utils/errorUtils.js +6 -4
  152. package/dist/esm/utils/languageUtils.js +404 -57
  153. package/dist/esm/utils/profileLinks.js +1 -3
  154. package/dist/esm/utils/requestUtils.js +7 -1
  155. package/dist/esm/utils/userIdentity.js +1 -1
  156. package/dist/esm/utils/validationUtils.js +5 -6
  157. package/dist/types/HttpService.d.ts +1 -1
  158. package/dist/types/api/accounts.d.ts +1 -1
  159. package/dist/types/api/apps.d.ts +4 -4
  160. package/dist/types/api/assets.d.ts +2 -0
  161. package/dist/types/api/auth.d.ts +8 -3
  162. package/dist/types/crypto/internal.d.ts +1 -1
  163. package/dist/types/index.d.ts +4 -4
  164. package/dist/types/server/index.d.ts +6 -6
  165. package/dist/types/server/requesterAssertion.d.ts +2 -2
  166. package/dist/types/server/workloadIdentity.client.d.ts +5 -1
  167. package/dist/types/server/workloadIdentity.d.ts +26 -0
  168. package/dist/types/session/accountDelegationAccess.d.ts +1 -1
  169. package/dist/types/session/index.d.ts +1 -1
  170. package/dist/types/utils/userIdentity.d.ts +1 -1
  171. package/dist/types/utils/validationUtils.d.ts +4 -5
  172. package/package.json +2 -3
  173. package/src/HttpService.ts +183 -53
  174. package/src/OxyServices.errors.ts +7 -3
  175. package/src/OxyServices.ts +6 -2
  176. package/src/api/accounts.ts +76 -26
  177. package/src/api/agency.ts +24 -9
  178. package/src/api/appData.ts +12 -2
  179. package/src/api/apps.ts +81 -31
  180. package/src/api/assets.ts +122 -40
  181. package/src/api/auth.ts +154 -79
  182. package/src/api/billing.ts +112 -65
  183. package/src/api/civic.ts +61 -19
  184. package/src/api/contacts.ts +4 -1
  185. package/src/api/devices.ts +54 -21
  186. package/src/api/follows.ts +88 -31
  187. package/src/api/identity.ts +101 -33
  188. package/src/api/linkedAccounts.ts +26 -8
  189. package/src/api/nodes.ts +18 -6
  190. package/src/api/notifications.ts +26 -6
  191. package/src/api/privacy.ts +42 -8
  192. package/src/api/reputation.ts +18 -7
  193. package/src/api/session.ts +23 -5
  194. package/src/api/store.ts +81 -31
  195. package/src/api/topics.ts +18 -6
  196. package/src/api/users.ts +98 -35
  197. package/src/boot/sessionColdBoot.ts +60 -19
  198. package/src/civic/payloads.ts +1 -4
  199. package/src/client/context.ts +6 -1
  200. package/src/constants/version.ts +7 -6
  201. package/src/crypto/aead.ts +1 -5
  202. package/src/crypto/ecdh.ts +1 -4
  203. package/src/crypto/identityDerivations.ts +5 -1
  204. package/src/crypto/identityMarker.ts +3 -1
  205. package/src/crypto/identityProof.ts +6 -1
  206. package/src/crypto/index.ts +11 -11
  207. package/src/crypto/internal.ts +6 -1
  208. package/src/crypto/keyManager.ts +348 -98
  209. package/src/crypto/recoveryPhrase.ts +14 -9
  210. package/src/crypto/registrationPow.ts +1 -1
  211. package/src/crypto/signatureService.ts +14 -6
  212. package/src/i18n/accountCategoryLabels.ts +1 -4
  213. package/src/i18n/accountRoleLabels.ts +1 -4
  214. package/src/i18n/index.ts +25 -7
  215. package/src/i18n/locales/ar-SA.ts +375 -366
  216. package/src/i18n/locales/ca-ES.ts +382 -366
  217. package/src/i18n/locales/de-DE.ts +387 -366
  218. package/src/i18n/locales/en-US.ts +2034 -1994
  219. package/src/i18n/locales/es-ES.ts +2043 -1994
  220. package/src/i18n/locales/fr-FR.ts +387 -366
  221. package/src/i18n/locales/it-IT.ts +380 -366
  222. package/src/i18n/locales/ja-JP.ts +380 -366
  223. package/src/i18n/locales/ko-KR.ts +377 -366
  224. package/src/i18n/locales/pt-PT.ts +383 -366
  225. package/src/i18n/locales/zh-CN.ts +373 -366
  226. package/src/i18n/trustTierLabels.ts +1 -4
  227. package/src/index.ts +371 -349
  228. package/src/inference/OxyInferenceClient.ts +784 -780
  229. package/src/models/interfaces.ts +30 -27
  230. package/src/server/OxyServer.ts +227 -68
  231. package/src/server/agentAccount.ts +8 -2
  232. package/src/server/aliaMachineCredential.ts +57 -16
  233. package/src/server/auth.ts +9 -5
  234. package/src/server/capabilityTicket.ts +30 -21
  235. package/src/server/index.ts +29 -6
  236. package/src/server/liveCapabilityTicket.ts +31 -9
  237. package/src/server/middleware.ts +212 -121
  238. package/src/server/namespaces.ts +247 -94
  239. package/src/server/rateLimit.ts +2 -4
  240. package/src/server/requesterAssertion.ts +93 -54
  241. package/src/server/serviceTokenLifetime.ts +9 -4
  242. package/src/server/traffic.ts +117 -23
  243. package/src/server/trafficNodeHttp.ts +61 -14
  244. package/src/server/userInvalidation.ts +1 -4
  245. package/src/server/workloadIdentity.client.ts +12 -1
  246. package/src/server/workloadIdentity.ts +52 -5
  247. package/src/session/SessionClient.ts +170 -49
  248. package/src/session/accountDelegationAccess.ts +24 -31
  249. package/src/session/accountDialogController.ts +59 -14
  250. package/src/session/accountSwitchTargets.ts +10 -15
  251. package/src/session/authStateStore.ts +121 -81
  252. package/src/session/deviceDirectory.ts +1 -3
  253. package/src/session/identityPin.ts +10 -11
  254. package/src/session/identitySession.ts +3 -1
  255. package/src/session/index.ts +87 -82
  256. package/src/session/refresh.ts +138 -44
  257. package/src/session/sessionClientHost.ts +1 -3
  258. package/src/session/sharedDeviceCredential.ts +35 -14
  259. package/src/session/socketLoader.ts +9 -2
  260. package/src/shared/utils/errorUtils.ts +5 -2
  261. package/src/shared/utils/networkUtils.ts +5 -8
  262. package/src/shared/utils/themeUtils.ts +2 -6
  263. package/src/types/buffer.d.ts +66 -21
  264. package/src/types/expo-secure-store.d.ts +5 -1
  265. package/src/utils/accountCacheSweep.ts +4 -5
  266. package/src/utils/accountUtils.ts +107 -108
  267. package/src/utils/apiUtils.ts +3 -3
  268. package/src/utils/asyncUtils.ts +2 -2
  269. package/src/utils/authHelpers.ts +10 -5
  270. package/src/utils/avatarUtils.ts +11 -6
  271. package/src/utils/cache.ts +9 -10
  272. package/src/utils/cacheKey.ts +5 -1
  273. package/src/utils/coldBoot.ts +1 -3
  274. package/src/utils/commonsDelivery.ts +1 -4
  275. package/src/utils/deviceManager.ts +15 -14
  276. package/src/utils/errorUtils.ts +68 -86
  277. package/src/utils/languageUtils.ts +407 -58
  278. package/src/utils/oauthPkce.ts +1 -4
  279. package/src/utils/platform.ts +0 -1
  280. package/src/utils/profileLinks.ts +2 -5
  281. package/src/utils/requestUtils.ts +18 -14
  282. package/src/utils/sessionUtils.ts +34 -33
  283. package/src/utils/userHandle.ts +3 -1
  284. package/src/utils/userIdentity.ts +1 -1
  285. package/src/utils/validationUtils.ts +11 -9
@@ -15,7 +15,7 @@
15
15
  import { TTLCache, registerCacheForCleanup, unregisterCacheFromCleanup } from './utils/cache.js';
16
16
  import { RequestDeduplicator, RequestQueue, SimpleLogger } from './utils/requestUtils.js';
17
17
  import { retryAsync } from './utils/asyncUtils.js';
18
- import { createCancelledError, ErrorCodes, handleHttpError, isCancelledError, parseHttpErrorBody } from './utils/errorUtils.js';
18
+ import { createCancelledError, ErrorCodes, handleHttpError, isCancelledError, parseHttpErrorBody, } from './utils/errorUtils.js';
19
19
  import { decodeTokenClaims } from './utils/tokenClaims.js';
20
20
  import { isReactNative } from '@oxy.so/protocol/random';
21
21
  import { computeIdentityTag, fnv1a32 } from './utils/cacheKey.js';
@@ -36,14 +36,25 @@ function sameTokenSession(left, right) {
36
36
  return false;
37
37
  // A refresh may rotate exp/iat/jti while preserving its account and session.
38
38
  // Opaque/malformed context fields cannot establish that equivalence.
39
- return ['userId', 'id', 'sub', 'sessionId', 'operatorId', 'clientId', 'appId', 'authMethodId'].every((key) => (a[key] === undefined || typeof a[key] === 'string') && a[key] === b[key]);
39
+ return [
40
+ 'userId',
41
+ 'id',
42
+ 'sub',
43
+ 'sessionId',
44
+ 'operatorId',
45
+ 'clientId',
46
+ 'appId',
47
+ 'authMethodId',
48
+ ].every((key) => (a[key] === undefined || typeof a[key] === 'string') && a[key] === b[key]);
40
49
  }
41
50
  /** Cancel this wait only; the shared mint continues for other callers. */
42
51
  function awaitRequestSignal(pending, signal) {
43
52
  if (!signal)
44
53
  return pending;
45
54
  return new Promise((resolve, reject) => {
46
- const onAbort = () => { reject(createCancelledError()); };
55
+ const onAbort = () => {
56
+ reject(createCancelledError());
57
+ };
47
58
  if (signal.aborted)
48
59
  onAbort();
49
60
  else
@@ -202,7 +213,9 @@ function isAbortLike(error) {
202
213
  function linkAbort(parent, child) {
203
214
  if (parent.aborted) {
204
215
  child.abort(parent.reason);
205
- return () => { };
216
+ return () => {
217
+ /* nothing was ever attached */
218
+ };
206
219
  }
207
220
  const onAbort = () => {
208
221
  child.abort(parent.reason);
@@ -314,8 +327,7 @@ export class HttpService {
314
327
  // the cache for the global cleanup interval. Default (config unset) keeps
315
328
  // caching ON with the 5-minute TTL — unchanged for existing consumers.
316
329
  this.cacheDisabled =
317
- config.enableCache === false ||
318
- (typeof config.cacheTTL === 'number' && config.cacheTTL <= 0);
330
+ config.enableCache === false || (typeof config.cacheTTL === 'number' && config.cacheTTL <= 0);
319
331
  this.cache = new TTLCache(config.cacheTTL && config.cacheTTL > 0 ? config.cacheTTL : 5 * 60 * 1000);
320
332
  if (!this.cacheDisabled) {
321
333
  registerCacheForCleanup(this.cache);
@@ -491,7 +503,7 @@ export class HttpService {
491
503
  const isUrlEncoded = this.isUrlSearchParams(data);
492
504
  // Build headers - start with defaults
493
505
  const headers = {
494
- 'Accept': 'application/json',
506
+ Accept: 'application/json',
495
507
  };
496
508
  // Only set Content-Type for non-FormData requests (FormData sets it automatically with boundary)
497
509
  if (isUrlEncoded) {
@@ -525,7 +537,11 @@ export class HttpService {
525
537
  // `fetch` as-is: RN's fetch does not consistently encode it, and doing
526
538
  // it here keeps the body identical across every platform.
527
539
  const bodyValue = method !== 'GET' && data
528
- ? (isFormData ? data : isUrlEncoded ? data.toString() : JSON.stringify(data))
540
+ ? isFormData
541
+ ? data
542
+ : isUrlEncoded
543
+ ? data.toString()
544
+ : JSON.stringify(data)
529
545
  : undefined;
530
546
  // React Native FormData workaround:
531
547
  // Expo SDK 56's "winter fetch" rejects RN file descriptors `{uri, type, name}`
@@ -567,7 +583,13 @@ export class HttpService {
567
583
  // queue slot, so a queued retry waits for a slot while holding one.
568
584
  // With every slot held by a 401 (a revoked session under load) no
569
585
  // retry could ever start and the whole client stalled.
570
- return this.request({ ...config, _isAuthRetry: true, retry: false, deduplicate: false, bypassQueue: true });
586
+ return this.request({
587
+ ...config,
588
+ _isAuthRetry: true,
589
+ retry: false,
590
+ deduplicate: false,
591
+ bypassQueue: true,
592
+ });
571
593
  }
572
594
  // Refresh failed or no token — clear tokens
573
595
  this.tokenStore.clearTokens();
@@ -594,7 +616,11 @@ export class HttpService {
594
616
  const parsed = parseHttpErrorBody(errorBody);
595
617
  const error = new Error(parsed.message ?? `HTTP ${response.status}: ${response.statusText}`);
596
618
  error.status = response.status;
597
- error.response = { status: response.status, statusText: response.statusText, data: errorBody };
619
+ error.response = {
620
+ status: response.status,
621
+ statusText: response.statusText,
622
+ data: errorBody,
623
+ };
598
624
  // Only set `code`/`details` when the server actually sent them.
599
625
  // Assigning `undefined` would still create the property, which changes
600
626
  // how `handleHttpError` classifies the error downstream.
@@ -641,7 +667,11 @@ export class HttpService {
641
667
  }
642
668
  }
643
669
  }
644
- else if (contentType && (contentType.includes('application/octet-stream') || contentType.includes('image/') || contentType.includes('video/') || contentType.includes('audio/'))) {
670
+ else if (contentType &&
671
+ (contentType.includes('application/octet-stream') ||
672
+ contentType.includes('image/') ||
673
+ contentType.includes('video/') ||
674
+ contentType.includes('audio/'))) {
645
675
  // For binary responses (blobs), return the blob directly without unwrapping
646
676
  responseData = await response.blob();
647
677
  }
@@ -707,7 +737,9 @@ export class HttpService {
707
737
  // Control-plane calls the auth lane depends on (`bypassQueue`, e.g. the
708
738
  // device-secret mint) run DIRECTLY — a queued mint could never acquire a
709
739
  // slot when every slot is parked awaiting it.
710
- config.bypassQueue ? attempt(domain) : this.requestQueue.enqueue(() => attempt(domain), domain);
740
+ config.bypassQueue
741
+ ? attempt(domain)
742
+ : this.requestQueue.enqueue(() => attempt(domain), domain);
711
743
  const execute = (domain) => retry
712
744
  ? retryAsync(() => runAttempt(domain), {
713
745
  maxRetries,
@@ -813,7 +845,9 @@ export class HttpService {
813
845
  try {
814
846
  xhr.abort();
815
847
  }
816
- catch { /* xhr already finished */ }
848
+ catch {
849
+ /* xhr already finished */
850
+ }
817
851
  };
818
852
  if (abortSignal.aborted) {
819
853
  reject(new DOMException('The user aborted a request.', 'AbortError'));
@@ -1226,8 +1260,10 @@ export class HttpService {
1226
1260
  // an auth mint, even when the trusted transport accepts absolute URLs.
1227
1261
  const target = new URL(this.buildURL(config.url));
1228
1262
  const base = new URL(this.baseURL);
1229
- if (target.origin !== base.origin || !['http:', 'https:'].includes(target.protocol)
1230
- || target.username || target.password) {
1263
+ if (target.origin !== base.origin ||
1264
+ !['http:', 'https:'].includes(target.protocol) ||
1265
+ target.username ||
1266
+ target.password) {
1231
1267
  throw new Error('Raw response requests must target the configured API origin');
1232
1268
  }
1233
1269
  if (config.signal?.aborted)
@@ -1237,7 +1273,8 @@ export class HttpService {
1237
1273
  const contextToken = this.syncAccessTokenFromProvider();
1238
1274
  const beforeAuthEpoch = this.sessionEpoch;
1239
1275
  const authHeader = await awaitRequestSignal(this.getAuthHeader(), config.signal);
1240
- if (beforeAuthEpoch !== this.sessionEpoch || !sameTokenSession(contextToken, this.tokenStore.getAccessToken())) {
1276
+ if (beforeAuthEpoch !== this.sessionEpoch ||
1277
+ !sameTokenSession(contextToken, this.tokenStore.getAccessToken())) {
1241
1278
  throw new OxyAuthenticationError('The request session changed', 'AUTH_SESSION_CHANGED');
1242
1279
  }
1243
1280
  if (config.signal?.aborted)
@@ -1250,7 +1287,8 @@ export class HttpService {
1250
1287
  async requestResponseAttempt(config, fullUrl, authHeader, isAuthRetry, epoch, contextToken) {
1251
1288
  const assertCurrent = () => {
1252
1289
  this.syncAccessTokenFromProvider();
1253
- if (epoch !== this.sessionEpoch || !sameTokenSession(contextToken, this.tokenStore.getAccessToken())) {
1290
+ if (epoch !== this.sessionEpoch ||
1291
+ !sameTokenSession(contextToken, this.tokenStore.getAccessToken())) {
1254
1292
  throw new OxyAuthenticationError('The request session changed', 'AUTH_SESSION_CHANGED');
1255
1293
  }
1256
1294
  };
@@ -1295,7 +1333,11 @@ export class HttpService {
1295
1333
  throw error;
1296
1334
  }
1297
1335
  const replayable = body === undefined || typeof body === 'string';
1298
- if (response.status === 401 && !isAuthRetry && authHeader && replayable && !config.signal?.aborted) {
1336
+ if (response.status === 401 &&
1337
+ !isAuthRetry &&
1338
+ authHeader &&
1339
+ replayable &&
1340
+ !config.signal?.aborted) {
1299
1341
  let refreshed;
1300
1342
  try {
1301
1343
  refreshed = await awaitRequestSignal(this.refreshAccessToken('response-401'), config.signal);
@@ -1330,7 +1372,9 @@ export class HttpService {
1330
1372
  this.config.onRequestEnd?.(config.url, config.method, duration, false);
1331
1373
  const normalizedError = config.signal?.aborted || isCancelledError(error)
1332
1374
  ? createCancelledError()
1333
- : error instanceof Error ? error : new Error(String(error));
1375
+ : error instanceof Error
1376
+ ? error
1377
+ : new Error(String(error));
1334
1378
  this.config.onRequestError?.(config.url, config.method, normalizedError);
1335
1379
  if (isCancelledError(normalizedError) || normalizedError instanceof OxyAuthenticationError)
1336
1380
  throw normalizedError;
@@ -1502,7 +1546,7 @@ export class HttpService {
1502
1546
  * per resource.
1503
1547
  * @returns Number of entries deleted
1504
1548
  */
1505
- invalidateCache({ keys = [], prefixes = [] }) {
1549
+ invalidateCache({ keys = [], prefixes = [], }) {
1506
1550
  if (keys.length === 0 && prefixes.length === 0)
1507
1551
  return 0;
1508
1552
  const exact = new Set(keys);
@@ -17,7 +17,7 @@
17
17
  * no per-request "acting-as" header. Identity is carried by the session/token,
18
18
  * so a switch propagates through reload and cross-domain exactly like a login.
19
19
  */
20
- import { agentKeyOperationSchema, executeAgentKeyOperationSchema, agentKeyOperationChallengeSchema, agentKeyOperationResultSchema, agentKeyListResponseSchema } from '@oxy.so/contracts';
20
+ import { agentKeyOperationSchema, executeAgentKeyOperationSchema, agentKeyOperationChallengeSchema, agentKeyOperationResultSchema, agentKeyListResponseSchema, } from '@oxy.so/contracts';
21
21
  import { normalizeUserIdentity } from '../utils/userIdentity.js';
22
22
  import { evictOxyIdentityCache } from '../utils/identityCacheSweep.js';
23
23
  import { evictOxyAccountForestCache, oxyAccountDetailCacheKey, OXY_ACCOUNT_PER_ACCOUNT_CACHE_PREFIX, } from '../utils/accountCacheSweep.js';
@@ -93,7 +93,9 @@ export class AccountAgentKeysApi {
93
93
  this.ctx = ctx;
94
94
  }
95
95
  async list(accountId) {
96
- return agentKeyListResponseSchema.parse(await this.ctx.request('GET', `/accounts/${enc(accountId)}/agent-keys`, undefined, { cache: false }));
96
+ return agentKeyListResponseSchema.parse(await this.ctx.request('GET', `/accounts/${enc(accountId)}/agent-keys`, undefined, {
97
+ cache: false,
98
+ }));
97
99
  }
98
100
  async requestChallenge(accountId, operation) {
99
101
  const response = await this.ctx.request('POST', `/accounts/${enc(accountId)}/agent-keys/challenge`, agentKeyOperationSchema.parse(operation), { cache: false });
@@ -182,7 +184,9 @@ export class AccountsApi {
182
184
  * @param data - Account configuration: kind, optional parent, and profile.
183
185
  */
184
186
  async create(data) {
185
- const res = await this.ctx.request('POST', '/accounts', data, { cache: false });
187
+ const res = await this.ctx.request('POST', '/accounts', data, {
188
+ cache: false,
189
+ });
186
190
  // A new account changes the accessible forest — bust every cached list.
187
191
  evictOxyAccountForestCache(this.ctx.http);
188
192
  return res.account;
@@ -222,7 +226,10 @@ export class AccountsApi {
222
226
  cache: false,
223
227
  });
224
228
  this.ctx.http.invalidateCache({
225
- keys: [`GET:/accounts/${enc(accountId)}/members`, `GET:/accounts/${enc(accountId)}/credentials`],
229
+ keys: [
230
+ `GET:/accounts/${enc(accountId)}/members`,
231
+ `GET:/accounts/${enc(accountId)}/credentials`,
232
+ ],
226
233
  });
227
234
  evictOxyAccountForestCache(this.ctx.http, accountId);
228
235
  return result;
@@ -33,7 +33,9 @@ export class DelegationGrantsApi {
33
33
  }
34
34
  /** Revoke a grant. */
35
35
  async revoke(grantId, ownerAccountId) {
36
- await this.ctx.request('DELETE', `/capabilities/grants/${enc(grantId)}`, undefined, { cache: false });
36
+ await this.ctx.request('DELETE', `/capabilities/grants/${enc(grantId)}`, undefined, {
37
+ cache: false,
38
+ });
37
39
  this.ctx.oxy.cache.delete(grantsKey(ownerAccountId));
38
40
  }
39
41
  }
@@ -68,7 +68,10 @@ export class ConnectedAppsApi {
68
68
  * `GET /auth/grants`, briefly cached; `revoke` busts it.
69
69
  */
70
70
  async list() {
71
- return this.ctx.request('GET', '/auth/grants', undefined, { cache: true, cacheTTL: SHORT_TTL });
71
+ return this.ctx.request('GET', '/auth/grants', undefined, {
72
+ cache: true,
73
+ cacheTTL: SHORT_TTL,
74
+ });
72
75
  }
73
76
  /**
74
77
  * Revoke this user's grant for a connected application. After this the
@@ -90,7 +93,9 @@ export class ConnectedAppsApi {
90
93
  }
91
94
  /** Revoke one external MCP connection and every token in its family. */
92
95
  async revokeMcpClient(grantId) {
93
- await this.ctx.request('DELETE', `/auth/mcp/oauth/grants/${enc(grantId)}`, undefined, { cache: false });
96
+ await this.ctx.request('DELETE', `/auth/mcp/oauth/grants/${enc(grantId)}`, undefined, {
97
+ cache: false,
98
+ });
94
99
  this.ctx.oxy.cache.delete('GET:/auth/mcp/oauth/grants');
95
100
  }
96
101
  }
@@ -149,7 +154,10 @@ export class AppsApi {
149
154
  const result = await this.ctx.request('DELETE', appPath(applicationId), undefined, {
150
155
  cache: false,
151
156
  });
152
- this.invalidateLists([`GET:${appPath(applicationId)}`, `GET:${credentialsPath(applicationId)}`]);
157
+ this.invalidateLists([
158
+ `GET:${appPath(applicationId)}`,
159
+ `GET:${credentialsPath(applicationId)}`,
160
+ ]);
153
161
  return result;
154
162
  }
155
163
  /**
@@ -180,7 +188,10 @@ export class AppsApi {
180
188
  * `GET:/applications/<id>…` detail keys.
181
189
  */
182
190
  invalidateLists(extraKeys = []) {
183
- this.ctx.http.invalidateCache({ keys: ['GET:/applications', ...extraKeys], prefixes: ['GET:/applications?'] });
191
+ this.ctx.http.invalidateCache({
192
+ keys: ['GET:/applications', ...extraKeys],
193
+ prefixes: ['GET:/applications?'],
194
+ });
184
195
  }
185
196
  }
186
197
  function appPath(applicationId) {
@@ -34,6 +34,12 @@ const ASSET_URL_CACHE_LIFETIME_FRACTION = 0.5;
34
34
  /** Assumed URL lifetime (seconds) when no `expiresIn` is asked for; the API default. */
35
35
  const DEFAULT_ASSET_URL_EXPIRES_IN_SECONDS = 3600;
36
36
  const ASSET_TTL = 5 * 60 * 1000;
37
+ /**
38
+ * Default bound on one `POST /assets/upload`, in ms. The client's per-request
39
+ * default (5s) is sized for JSON calls; a video sent over a phone connection
40
+ * needs minutes, and timing it out at 5s failed every upload past a few MB.
41
+ */
42
+ const ASSET_UPLOAD_TIMEOUT_MS = 10 * 60 * 1000;
37
43
  /**
38
44
  * How long a resolved asset URL may stay in the SDK's GET cache, in ms: never
39
45
  * longer than the media token's lifetime, and discounted below it.
@@ -61,7 +67,13 @@ export class AssetsApi {
61
67
  });
62
68
  if (body.type && !headers.has('Content-Type'))
63
69
  headers.set('Content-Type', body.type);
64
- const result = await fetch(target.toString(), { method: 'PUT', body, headers, credentials: 'omit', redirect: 'error' });
70
+ const result = await fetch(target.toString(), {
71
+ method: 'PUT',
72
+ body,
73
+ headers,
74
+ credentials: 'omit',
75
+ redirect: 'error',
76
+ });
65
77
  if (!result.ok)
66
78
  throw new Error(`Presigned upload failed (${result.status})`);
67
79
  }
@@ -79,7 +91,8 @@ export class AssetsApi {
79
91
  const fileName = 'name' in file && file.name ? file.name : 'unknown';
80
92
  const fileSize = 'size' in file && file.size ? file.size : 0;
81
93
  const formData = new FormData();
82
- if ((typeof File !== 'undefined' && file instanceof File) || (typeof Blob !== 'undefined' && file instanceof Blob)) {
94
+ if ((typeof File !== 'undefined' && file instanceof File) ||
95
+ (typeof Blob !== 'undefined' && file instanceof Blob)) {
83
96
  formData.append('file', file, fileName);
84
97
  }
85
98
  else if ('uri' in file && typeof file.uri === 'string') {
@@ -95,7 +108,9 @@ export class AssetsApi {
95
108
  throw new Error(`Failed to read file from uri (status ${res.status})`);
96
109
  }
97
110
  const fetched = await res.blob();
98
- const blob = fetched.type === '' && descriptor.type ? new Blob([fetched], { type: descriptor.type }) : fetched;
111
+ const blob = fetched.type === '' && descriptor.type
112
+ ? new Blob([fetched], { type: descriptor.type })
113
+ : fetched;
99
114
  if (blob.size === 0) {
100
115
  throw new Error('Cannot upload an empty file');
101
116
  }
@@ -110,13 +125,19 @@ export class AssetsApi {
110
125
  if (options.metadata)
111
126
  formData.append('metadata', JSON.stringify(options.metadata));
112
127
  try {
113
- const response = await this.ctx.request('POST', '/assets/upload', formData, { cache: false });
128
+ const response = await this.ctx.request('POST', '/assets/upload', formData, {
129
+ cache: false,
130
+ timeout: options.timeout ?? ASSET_UPLOAD_TIMEOUT_MS,
131
+ });
114
132
  options.onProgress?.(100);
115
133
  return response;
116
134
  }
117
135
  catch (error) {
118
136
  logger.error('File upload error', error, { component: 'oxy.assets' });
119
- error.fileContext = { fileName, fileSize };
137
+ error.fileContext = {
138
+ fileName,
139
+ fileSize,
140
+ };
120
141
  throw error;
121
142
  }
122
143
  }
@@ -132,7 +153,9 @@ export class AssetsApi {
132
153
  // ── Links ────────────────────────────────────────────────────────────────
133
154
  /** Link an asset to an entity (what keeps it alive). */
134
155
  async link(fileId, target, options = {}) {
135
- const body = { ...target };
156
+ const body = {
157
+ ...target,
158
+ };
136
159
  if (options.visibility)
137
160
  body.visibility = options.visibility;
138
161
  if (options.webhookUrl)
@@ -152,7 +175,10 @@ export class AssetsApi {
152
175
  // ── Reads ────────────────────────────────────────────────────────────────
153
176
  /** An asset's record: size, mime, visibility, links, variants. */
154
177
  async get(fileId) {
155
- return this.ctx.request('GET', `/assets/${enc(fileId)}`, undefined, { cache: true, cacheTTL: ASSET_TTL });
178
+ return this.ctx.request('GET', `/assets/${enc(fileId)}`, undefined, {
179
+ cache: true,
180
+ cacheTTL: ASSET_TTL,
181
+ });
156
182
  }
157
183
  /** The signed-in user's files, newest first. */
158
184
  async list(params = {}) {
@@ -165,7 +191,9 @@ export class AssetsApi {
165
191
  }
166
192
  /** The account's storage usage, aggregated from its assets. */
167
193
  async usage() {
168
- return this.ctx.request('GET', '/storage/usage', undefined, { cache: false });
194
+ return this.ctx.request('GET', '/storage/usage', undefined, {
195
+ cache: false,
196
+ });
169
197
  }
170
198
  // ── URLs ─────────────────────────────────────────────────────────────────
171
199
  /**
@@ -247,7 +275,9 @@ export class AssetsApi {
247
275
  body.expiresIn = options.expiresIn;
248
276
  if (typeof options?.context === 'string')
249
277
  body.context = options.context;
250
- return this.ctx.request('POST', '/assets/batch-access', body, { cache: false });
278
+ return this.ctx.request('POST', '/assets/batch-access', body, {
279
+ cache: false,
280
+ });
251
281
  }
252
282
  // ── Content ──────────────────────────────────────────────────────────────
253
283
  /** An asset's content as text. */
@@ -267,7 +297,9 @@ export class AssetsApi {
267
297
  */
268
298
  async delete(fileId, options = {}) {
269
299
  const url = `/assets/${enc(fileId)}${options.force ? '?force=true' : ''}`;
270
- const res = await this.ctx.request('DELETE', url, undefined, { cache: false });
300
+ const res = await this.ctx.request('DELETE', url, undefined, {
301
+ cache: false,
302
+ });
271
303
  this.evict(fileId);
272
304
  return res;
273
305
  }
@@ -309,7 +341,10 @@ export class AssetsApi {
309
341
  }
310
342
  /** Drop the cached record and every cached URL variant of an asset, in one pass. */
311
343
  evict(fileId) {
312
- this.ctx.http.invalidateCache({ keys: [`GET:/assets/${enc(fileId)}`], prefixes: [`GET:/assets/${enc(fileId)}/url`] });
344
+ this.ctx.http.invalidateCache({
345
+ keys: [`GET:/assets/${enc(fileId)}`],
346
+ prefixes: [`GET:/assets/${enc(fileId)}/url`],
347
+ });
313
348
  }
314
349
  }
315
350
  const enc = encodeURIComponent;
@@ -328,7 +363,9 @@ function dedupeFileAccessRequests(requests) {
328
363
  if (seen.has(key))
329
364
  continue;
330
365
  seen.add(key);
331
- out.push(req.variant === undefined ? { fileId: req.fileId } : { fileId: req.fileId, variant: req.variant });
366
+ out.push(req.variant === undefined
367
+ ? { fileId: req.fileId }
368
+ : { fileId: req.fileId, variant: req.variant });
332
369
  }
333
370
  return out;
334
371
  }
@@ -196,7 +196,9 @@ export class AuthPasswordApi {
196
196
  return signInStep(this.ctx, res, 'auth/signin/password');
197
197
  }
198
198
  async set(request) {
199
- return this.ctx.request('PUT', '/users/me/password', request, { cache: false });
199
+ return this.ctx.request('PUT', '/users/me/password', request, {
200
+ cache: false,
201
+ });
200
202
  }
201
203
  }
202
204
  /** `oxy.auth.totp` — the account’s authenticator app. */
@@ -205,7 +207,9 @@ export class AuthTotpApi {
205
207
  this.ctx = ctx;
206
208
  }
207
209
  async enroll() {
208
- const res = await this.ctx.request('POST', '/users/me/totp/enroll', undefined, { cache: false });
210
+ const res = await this.ctx.request('POST', '/users/me/totp/enroll', undefined, {
211
+ cache: false,
212
+ });
209
213
  const parsed = safeParseContract(totpEnrollResponseSchema, res);
210
214
  if (!parsed)
211
215
  throw new Error('users/me/totp/enroll returned an unexpected response shape');
@@ -273,7 +277,10 @@ export class AuthOAuthApi {
273
277
  client_id: params.clientId,
274
278
  code_verifier: params.codeVerifier,
275
279
  });
276
- const res = await this.ctx.request('POST', '/auth/oauth/token', form, { cache: false, skipAuth: true });
280
+ const res = await this.ctx.request('POST', '/auth/oauth/token', form, {
281
+ cache: false,
282
+ skipAuth: true,
283
+ });
277
284
  if (!res || typeof res !== 'object') {
278
285
  throw new Error('auth/oauth/token returned an unexpected response shape');
279
286
  }
@@ -324,7 +331,9 @@ export class AuthOAuthApi {
324
331
  * The response is a flat JSON document — no `{ data }` wrapper.
325
332
  */
326
333
  async userInfo() {
327
- const res = await this.ctx.request('GET', '/auth/oauth/userinfo', undefined, { cache: false });
334
+ const res = await this.ctx.request('GET', '/auth/oauth/userinfo', undefined, {
335
+ cache: false,
336
+ });
328
337
  if (!res || typeof res !== 'object') {
329
338
  throw new Error('auth/oauth/userinfo returned an unexpected response shape');
330
339
  }
@@ -652,12 +661,18 @@ export class AuthAgentApi {
652
661
  }
653
662
  async requestChallenge(publicKey) {
654
663
  const body = agentChallengeRequestSchema.parse({ publicKey });
655
- const response = await this.ctx.request('POST', '/auth/agent/challenge', body, { cache: false, skipAuth: true });
664
+ const response = await this.ctx.request('POST', '/auth/agent/challenge', body, {
665
+ cache: false,
666
+ skipAuth: true,
667
+ });
656
668
  return agentProofClaimsSchema.parse(response);
657
669
  }
658
670
  async verify(publicKey, proof) {
659
671
  const body = agentVerifyRequestSchema.parse({ publicKey, ...proof });
660
- const response = await this.ctx.request('POST', '/auth/agent/verify', body, { cache: false, skipAuth: true });
672
+ const response = await this.ctx.request('POST', '/auth/agent/verify', body, {
673
+ cache: false,
674
+ skipAuth: true,
675
+ });
661
676
  return plantSession(this.ctx, response, 'auth/agent/verify');
662
677
  }
663
678
  }
@@ -672,14 +687,19 @@ export class AuthApi {
672
687
  this.agent = new AuthAgentApi(ctx);
673
688
  }
674
689
  /**
675
- * Register a new identity with public key authentication
676
- * Identity is purely cryptographic - username and profile data are optional
690
+ * Register a new account for a public key, together with its username.
691
+ *
692
+ * The username is required: the server writes it in the same insert as the
693
+ * key, so an account never exists without one. A taken username is a 409
694
+ * (`Username already taken`), as is a key that is already registered
695
+ * (`Identity already registered`).
677
696
  *
678
697
  * @param publicKey - The user's ECDSA public key (hex)
679
698
  * @param signature - Signature of the registration request
680
699
  * @param timestamp - Timestamp when the signature was created
700
+ * @param username - The account's username
681
701
  */
682
- async registerKey(publicKey, signature, timestamp) {
702
+ async registerKey(publicKey, signature, timestamp, username) {
683
703
  // Advisory for now (server soft-enforces — see
684
704
  // `SessionController.register`), but solved unconditionally so every
685
705
  // client is already sending it once the server starts requiring it.
@@ -689,6 +709,7 @@ export class AuthApi {
689
709
  publicKey,
690
710
  signature,
691
711
  timestamp,
712
+ username,
692
713
  powNonce,
693
714
  }, { cache: false, skipAuth: true });
694
715
  if (!res || (typeof res === 'object' && Object.keys(res).length === 0)) {
@@ -853,7 +874,10 @@ export class AuthApi {
853
874
  */
854
875
  async checkUsername(username) {
855
876
  // Public availability lookup (pre-session) — skip the bearer preflight.
856
- return this.ctx.request('GET', `/auth/check-username/${username}`, undefined, { cache: false, skipAuth: true });
877
+ return this.ctx.request('GET', `/auth/check-username/${username}`, undefined, {
878
+ cache: false,
879
+ skipAuth: true,
880
+ });
857
881
  }
858
882
  /**
859
883
  * The authenticator's code (or a backup code) for a first factor's
@@ -871,7 +895,9 @@ export class AuthApi {
871
895
  return plantSession(this.ctx, res, 'auth/signup');
872
896
  }
873
897
  async methods() {
874
- const res = await this.ctx.request('GET', '/users/me/sign-in-methods', undefined, { cache: false });
898
+ const res = await this.ctx.request('GET', '/users/me/sign-in-methods', undefined, {
899
+ cache: false,
900
+ });
875
901
  const parsed = safeParseContract(signInMethodsSchema, res);
876
902
  if (!parsed)
877
903
  throw new Error('users/me/sign-in-methods returned an unexpected response shape');
@@ -1,4 +1,4 @@
1
- import { personalPlanCheckoutRequestSchema, personalPlanCheckoutResultSchema } from '@oxy.so/contracts';
1
+ import { personalPlanCheckoutRequestSchema, personalPlanCheckoutResultSchema, } from '@oxy.so/contracts';
2
2
  /**
3
3
  * `oxy.billing` — the signed-in user's subscription, wallet and payment history.
4
4
  *
@@ -24,7 +24,9 @@ export class BillingApi {
24
24
  }
25
25
  /** Public versioned personal plans. Discovery never proves customer access. */
26
26
  async personalPlans() {
27
- return personalPlanCatalogueSchema.parse(await this.ctx.request('GET', '/billing/personal-plans', undefined, { cache: false }));
27
+ return personalPlanCatalogueSchema.parse(await this.ctx.request('GET', '/billing/personal-plans', undefined, {
28
+ cache: false,
29
+ }));
28
30
  }
29
31
  /** Product rights only; requires a production application-bound user session. Never cached. */
30
32
  async productAccess(query) {
@@ -34,19 +36,29 @@ export class BillingApi {
34
36
  }
35
37
  /** Named commercial sources for the current subject; no inferred plan collapse. */
36
38
  async productSubscriptions(expectedSubjectAccountId) {
37
- return productSubscriptionsResponseSchema.parse(await this.ctx.request("GET", expectedSubjectAccountId ? `/billing/product-subscriptions?expectedSubjectAccountId=${encodeURIComponent(expectedSubjectAccountId)}` : "/billing/product-subscriptions", undefined, { cache: false })).subscriptions;
39
+ return productSubscriptionsResponseSchema.parse(await this.ctx.request('GET', expectedSubjectAccountId
40
+ ? `/billing/product-subscriptions?expectedSubjectAccountId=${encodeURIComponent(expectedSubjectAccountId)}`
41
+ : '/billing/product-subscriptions', undefined, { cache: false })).subscriptions;
38
42
  }
39
43
  /** Per-grant credit provenance; no interpretation of the historical mixed balance. */
40
44
  async creditGrants() {
41
- return subscriptionCreditGrantsResponseSchema.parse(await this.ctx.request("GET", "/billing/credit-grants", undefined, { cache: false })).grants;
45
+ return subscriptionCreditGrantsResponseSchema.parse(await this.ctx.request('GET', '/billing/credit-grants', undefined, { cache: false })).grants;
42
46
  }
43
47
  /** Payer-only named-source cancellation. Resolves on provider acceptance, including pending local reconciliation. Use cancelProductSubscriptionWithStatus for detailed completion state. */
44
48
  async cancelProductSubscription(sourceId, expectedSubjectAccountId, actionId) {
45
- await this.ctx.request("POST", "/billing/product-subscriptions/cancel", cancelProductSubscriptionSchema.parse({ sourceId, ...(actionId ? { actionId } : {}), ...(expectedSubjectAccountId ? { expectedSubjectAccountId } : {}) }), { cache: false });
49
+ await this.ctx.request('POST', '/billing/product-subscriptions/cancel', cancelProductSubscriptionSchema.parse({
50
+ sourceId,
51
+ ...(actionId ? { actionId } : {}),
52
+ ...(expectedSubjectAccountId ? { expectedSubjectAccountId } : {}),
53
+ }), { cache: false });
46
54
  }
47
55
  /** Provider acceptance versus completed local cancellation, for user-facing flows. */
48
56
  async cancelProductSubscriptionWithStatus(sourceId, expectedSubjectAccountId, actionId) {
49
- return productSubscriptionCancellationResultSchema.parse(await this.ctx.request('POST', '/billing/product-subscriptions/cancel', cancelProductSubscriptionSchema.parse({ sourceId, ...(actionId ? { actionId } : {}), ...(expectedSubjectAccountId ? { expectedSubjectAccountId } : {}) }), { cache: false }));
57
+ return productSubscriptionCancellationResultSchema.parse(await this.ctx.request('POST', '/billing/product-subscriptions/cancel', cancelProductSubscriptionSchema.parse({
58
+ sourceId,
59
+ ...(actionId ? { actionId } : {}),
60
+ ...(expectedSubjectAccountId ? { expectedSubjectAccountId } : {}),
61
+ }), { cache: false }));
50
62
  }
51
63
  /** The signed-in user's payment history. Never cached. */
52
64
  async payments() {
@@ -63,7 +75,10 @@ export class BillingApi {
63
75
  /** A user's wallet (default: the signed-in user's). Cached briefly: the balance moves. */
64
76
  async wallet(userId) {
65
77
  const id = this.resolveUserId(userId);
66
- return this.ctx.request('GET', `/wallet/${id}`, undefined, { cache: true, cacheTTL: WALLET_TTL });
78
+ return this.ctx.request('GET', `/wallet/${id}`, undefined, {
79
+ cache: true,
80
+ cacheTTL: WALLET_TTL,
81
+ });
67
82
  }
68
83
  /** A page of a user's wallet transactions (default: the signed-in user's). Never cached. */
69
84
  async walletTransactions(options = {}) {
@@ -73,7 +88,9 @@ export class BillingApi {
73
88
  params.limit = options.limit;
74
89
  if (options.offset)
75
90
  params.offset = options.offset;
76
- return this.ctx.request('GET', `/wallet/transactions/${id}`, params, { cache: false });
91
+ return this.ctx.request('GET', `/wallet/transactions/${id}`, params, {
92
+ cache: false,
93
+ });
77
94
  }
78
95
  resolveUserId(userId) {
79
96
  const id = userId || this.ctx.oxy.session.userId;