@oxyhq/core 1.5.0 → 1.6.1

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 (92) hide show
  1. package/dist/cjs/AuthManager.js +14 -1
  2. package/dist/cjs/HttpService.js +87 -69
  3. package/dist/cjs/OxyServices.base.js +5 -4
  4. package/dist/cjs/crypto/keyManager.js +1 -13
  5. package/dist/cjs/crypto/signatureService.js +7 -20
  6. package/dist/cjs/index.js +9 -1
  7. package/dist/cjs/mixins/OxyServices.analytics.js +2 -2
  8. package/dist/cjs/mixins/OxyServices.assets.js +14 -14
  9. package/dist/cjs/mixins/OxyServices.auth.js +19 -19
  10. package/dist/cjs/mixins/OxyServices.developer.js +6 -6
  11. package/dist/cjs/mixins/OxyServices.devices.js +7 -7
  12. package/dist/cjs/mixins/OxyServices.features.js +23 -23
  13. package/dist/cjs/mixins/OxyServices.fedcm.js +1 -1
  14. package/dist/cjs/mixins/OxyServices.karma.js +6 -6
  15. package/dist/cjs/mixins/OxyServices.location.js +2 -2
  16. package/dist/cjs/mixins/OxyServices.payment.js +6 -6
  17. package/dist/cjs/mixins/OxyServices.popup.js +1 -1
  18. package/dist/cjs/mixins/OxyServices.privacy.js +6 -6
  19. package/dist/cjs/mixins/OxyServices.security.js +3 -3
  20. package/dist/cjs/mixins/OxyServices.user.js +22 -22
  21. package/dist/cjs/mixins/OxyServices.utility.js +39 -10
  22. package/dist/cjs/utils/authHelpers.js +114 -0
  23. package/dist/cjs/utils/platform.js +14 -0
  24. package/dist/esm/AuthManager.js +14 -1
  25. package/dist/esm/HttpService.js +87 -69
  26. package/dist/esm/OxyServices.base.js +5 -4
  27. package/dist/esm/crypto/keyManager.js +1 -13
  28. package/dist/esm/crypto/signatureService.js +2 -15
  29. package/dist/esm/index.js +2 -0
  30. package/dist/esm/mixins/OxyServices.analytics.js +2 -2
  31. package/dist/esm/mixins/OxyServices.assets.js +14 -14
  32. package/dist/esm/mixins/OxyServices.auth.js +19 -19
  33. package/dist/esm/mixins/OxyServices.developer.js +6 -6
  34. package/dist/esm/mixins/OxyServices.devices.js +7 -7
  35. package/dist/esm/mixins/OxyServices.features.js +23 -23
  36. package/dist/esm/mixins/OxyServices.fedcm.js +1 -1
  37. package/dist/esm/mixins/OxyServices.karma.js +6 -6
  38. package/dist/esm/mixins/OxyServices.location.js +2 -2
  39. package/dist/esm/mixins/OxyServices.payment.js +6 -6
  40. package/dist/esm/mixins/OxyServices.popup.js +1 -1
  41. package/dist/esm/mixins/OxyServices.privacy.js +6 -6
  42. package/dist/esm/mixins/OxyServices.security.js +3 -3
  43. package/dist/esm/mixins/OxyServices.user.js +22 -22
  44. package/dist/esm/mixins/OxyServices.utility.js +39 -10
  45. package/dist/esm/utils/authHelpers.js +105 -0
  46. package/dist/esm/utils/platform.js +12 -0
  47. package/dist/types/HttpService.d.ts +4 -1
  48. package/dist/types/OxyServices.base.d.ts +1 -1
  49. package/dist/types/index.d.ts +2 -0
  50. package/dist/types/mixins/OxyServices.analytics.d.ts +1 -1
  51. package/dist/types/mixins/OxyServices.assets.d.ts +1 -1
  52. package/dist/types/mixins/OxyServices.auth.d.ts +1 -1
  53. package/dist/types/mixins/OxyServices.developer.d.ts +1 -1
  54. package/dist/types/mixins/OxyServices.devices.d.ts +1 -1
  55. package/dist/types/mixins/OxyServices.features.d.ts +1 -1
  56. package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -1
  57. package/dist/types/mixins/OxyServices.karma.d.ts +1 -1
  58. package/dist/types/mixins/OxyServices.language.d.ts +1 -1
  59. package/dist/types/mixins/OxyServices.location.d.ts +1 -1
  60. package/dist/types/mixins/OxyServices.payment.d.ts +1 -1
  61. package/dist/types/mixins/OxyServices.popup.d.ts +1 -1
  62. package/dist/types/mixins/OxyServices.privacy.d.ts +1 -1
  63. package/dist/types/mixins/OxyServices.redirect.d.ts +1 -1
  64. package/dist/types/mixins/OxyServices.security.d.ts +1 -1
  65. package/dist/types/mixins/OxyServices.user.d.ts +1 -1
  66. package/dist/types/mixins/OxyServices.utility.d.ts +20 -6
  67. package/dist/types/utils/authHelpers.d.ts +57 -0
  68. package/dist/types/utils/platform.d.ts +8 -0
  69. package/package.json +1 -1
  70. package/src/AuthManager.ts +14 -1
  71. package/src/HttpService.ts +85 -67
  72. package/src/OxyServices.base.ts +5 -4
  73. package/src/crypto/keyManager.ts +1 -15
  74. package/src/crypto/signatureService.ts +2 -17
  75. package/src/index.ts +11 -0
  76. package/src/mixins/OxyServices.analytics.ts +2 -2
  77. package/src/mixins/OxyServices.assets.ts +14 -14
  78. package/src/mixins/OxyServices.auth.ts +19 -19
  79. package/src/mixins/OxyServices.developer.ts +6 -6
  80. package/src/mixins/OxyServices.devices.ts +7 -7
  81. package/src/mixins/OxyServices.features.ts +23 -23
  82. package/src/mixins/OxyServices.fedcm.ts +1 -1
  83. package/src/mixins/OxyServices.karma.ts +6 -6
  84. package/src/mixins/OxyServices.location.ts +2 -2
  85. package/src/mixins/OxyServices.payment.ts +6 -6
  86. package/src/mixins/OxyServices.popup.ts +1 -1
  87. package/src/mixins/OxyServices.privacy.ts +6 -6
  88. package/src/mixins/OxyServices.security.ts +3 -3
  89. package/src/mixins/OxyServices.user.ts +22 -22
  90. package/src/mixins/OxyServices.utility.ts +41 -11
  91. package/src/utils/authHelpers.ts +140 -0
  92. package/src/utils/platform.ts +14 -0
@@ -25,7 +25,9 @@ import { isNative, getPlatformOS } from './utils/platform';
25
25
  */
26
26
  const isNativeApp = isNative();
27
27
  /**
28
- * Token store for authentication (singleton)
28
+ * Token store for authentication (instance-based)
29
+ * Each HttpService gets its own TokenStore to prevent conflicts
30
+ * when multiple OxyServices instances coexist server-side.
29
31
  */
30
32
  class TokenStore {
31
33
  constructor() {
@@ -34,12 +36,6 @@ class TokenStore {
34
36
  this.csrfToken = null;
35
37
  this.csrfTokenFetchPromise = null;
36
38
  }
37
- static getInstance() {
38
- if (!TokenStore.instance) {
39
- TokenStore.instance = new TokenStore();
40
- }
41
- return TokenStore.instance;
42
- }
43
39
  setTokens(accessToken, refreshToken = '') {
44
40
  this.accessToken = accessToken;
45
41
  this.refreshToken = refreshToken;
@@ -83,6 +79,7 @@ class TokenStore {
83
79
  export class HttpService {
84
80
  constructor(config) {
85
81
  this.tokenRefreshPromise = null;
82
+ this.tokenRefreshCooldownUntil = 0;
86
83
  this._onTokenRefreshed = null;
87
84
  // Performance monitoring
88
85
  this.requestMetrics = {
@@ -95,7 +92,7 @@ export class HttpService {
95
92
  };
96
93
  this.config = config;
97
94
  this.baseURL = config.baseURL;
98
- this.tokenStore = TokenStore.getInstance();
95
+ this.tokenStore = new TokenStore();
99
96
  this.logger = new SimpleLogger(config.enableLogging || false, config.logLevel || 'error', 'HttpService');
100
97
  // Initialize performance infrastructure
101
98
  this.cache = new TTLCache(config.cacheTTL || 5 * 60 * 1000);
@@ -247,6 +244,20 @@ export class HttpService {
247
244
  // Refresh failed or no token — clear tokens
248
245
  this.tokenStore.clearTokens();
249
246
  }
247
+ // On 403 with CSRF error, clear cached token and retry once
248
+ if (response.status === 403 && !config._isCsrfRetry) {
249
+ try {
250
+ const clonedResponse = response.clone();
251
+ const errBody = await clonedResponse.json();
252
+ if (errBody?.code === 'CSRF_TOKEN_INVALID' || errBody?.code === 'CSRF_TOKEN_MISSING') {
253
+ this.tokenStore.clearCsrfToken();
254
+ return this.request({ ...config, _isCsrfRetry: true, retry: false });
255
+ }
256
+ }
257
+ catch {
258
+ // Failed to parse error body — not a CSRF error
259
+ }
260
+ }
250
261
  // Try to parse error response (handle empty/malformed JSON)
251
262
  let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
252
263
  const contentType = response.headers.get('content-type');
@@ -403,52 +414,57 @@ export class HttpService {
403
414
  return existingPromise;
404
415
  }
405
416
  const fetchPromise = (async () => {
406
- try {
407
- if (isDev())
408
- console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/api/csrf-token`);
409
- // Use AbortController for timeout (more compatible than AbortSignal.timeout)
410
- const controller = new AbortController();
411
- const timeoutId = setTimeout(() => controller.abort(), 5000);
412
- const response = await fetch(`${this.baseURL}/api/csrf-token`, {
413
- method: 'GET',
414
- headers: { 'Accept': 'application/json' },
415
- credentials: 'include', // Required to receive and send cookies
416
- signal: controller.signal,
417
- });
418
- clearTimeout(timeoutId);
419
- if (isDev())
420
- console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
421
- if (response.ok) {
422
- const data = await response.json();
417
+ const maxAttempts = 2;
418
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
419
+ try {
420
+ if (isDev())
421
+ console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
422
+ // Use AbortController for timeout (more compatible than AbortSignal.timeout)
423
+ const controller = new AbortController();
424
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
425
+ const response = await fetch(`${this.baseURL}/csrf-token`, {
426
+ method: 'GET',
427
+ headers: { 'Accept': 'application/json' },
428
+ credentials: 'include', // Required to receive and send cookies
429
+ signal: controller.signal,
430
+ });
431
+ clearTimeout(timeoutId);
432
+ if (isDev())
433
+ console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
434
+ if (response.ok) {
435
+ const data = await response.json();
436
+ if (isDev())
437
+ console.log('[HttpService] CSRF response data:', data);
438
+ const token = data.csrfToken || null;
439
+ this.tokenStore.setCsrfToken(token);
440
+ this.logger.debug('CSRF token fetched');
441
+ return token;
442
+ }
443
+ // Also check response header for CSRF token
444
+ const headerToken = response.headers.get('X-CSRF-Token');
445
+ if (headerToken) {
446
+ this.tokenStore.setCsrfToken(headerToken);
447
+ this.logger.debug('CSRF token from header');
448
+ return headerToken;
449
+ }
423
450
  if (isDev())
424
- console.log('[HttpService] CSRF response data:', data);
425
- const token = data.csrfToken || null;
426
- this.tokenStore.setCsrfToken(token);
427
- this.logger.debug('CSRF token fetched');
428
- return token;
451
+ console.log('[HttpService] CSRF fetch failed with status:', response.status);
452
+ this.logger.warn('Failed to fetch CSRF token:', response.status);
429
453
  }
430
- // Also check response header for CSRF token
431
- const headerToken = response.headers.get('X-CSRF-Token');
432
- if (headerToken) {
433
- this.tokenStore.setCsrfToken(headerToken);
434
- this.logger.debug('CSRF token from header');
435
- return headerToken;
454
+ catch (error) {
455
+ if (isDev())
456
+ console.log('[HttpService] CSRF fetch error:', error);
457
+ this.logger.warn('CSRF token fetch error:', error);
458
+ }
459
+ // Wait before retry (500ms)
460
+ if (attempt < maxAttempts) {
461
+ await new Promise(resolve => setTimeout(resolve, 500));
436
462
  }
437
- if (isDev())
438
- console.log('[HttpService] CSRF fetch failed with status:', response.status);
439
- this.logger.warn('Failed to fetch CSRF token:', response.status);
440
- return null;
441
- }
442
- catch (error) {
443
- if (isDev())
444
- console.log('[HttpService] CSRF fetch error:', error);
445
- this.logger.warn('CSRF token fetch error:', error);
446
- return null;
447
- }
448
- finally {
449
- this.tokenStore.setCsrfTokenFetchPromise(null);
450
463
  }
451
- })();
464
+ return null;
465
+ })().finally(() => {
466
+ this.tokenStore.setCsrfTokenFetchPromise(null);
467
+ });
452
468
  this.tokenStore.setCsrfTokenFetchPromise(fetchPromise);
453
469
  return fetchPromise;
454
470
  }
@@ -465,18 +481,25 @@ export class HttpService {
465
481
  const currentTime = Math.floor(Date.now() / 1000);
466
482
  // If token expires in less than 60 seconds, refresh it
467
483
  if (decoded.exp && decoded.exp - currentTime < 60 && decoded.sessionId) {
468
- // Deduplicate concurrent refresh attempts
469
- if (!this.tokenRefreshPromise) {
470
- this.tokenRefreshPromise = this._refreshTokenFromSession(decoded.sessionId);
484
+ // Skip if we recently failed a refresh (5s cooldown to prevent storms)
485
+ if (Date.now() < this.tokenRefreshCooldownUntil) {
486
+ return `Bearer ${accessToken}`;
471
487
  }
472
- try {
473
- const result = await this.tokenRefreshPromise;
474
- if (result)
488
+ // Deduplicate concurrent refresh attempts. The promise is shared
489
+ // across all concurrent callers and cleared only after it settles,
490
+ // so every awaiter receives the same result.
491
+ if (!this.tokenRefreshPromise) {
492
+ this.tokenRefreshPromise = this._refreshTokenFromSession(decoded.sessionId)
493
+ .then((result) => {
494
+ if (!result)
495
+ this.tokenRefreshCooldownUntil = Date.now() + 5000;
475
496
  return result;
497
+ })
498
+ .finally(() => { this.tokenRefreshPromise = null; });
476
499
  }
477
- finally {
478
- this.tokenRefreshPromise = null;
479
- }
500
+ const result = await this.tokenRefreshPromise;
501
+ if (result)
502
+ return result;
480
503
  }
481
504
  return `Bearer ${accessToken}`;
482
505
  }
@@ -487,7 +510,7 @@ export class HttpService {
487
510
  }
488
511
  async _refreshTokenFromSession(sessionId) {
489
512
  try {
490
- const refreshUrl = `${this.baseURL}/api/session/token/${sessionId}`;
513
+ const refreshUrl = `${this.baseURL}/session/token/${sessionId}`;
491
514
  const response = await fetch(refreshUrl, {
492
515
  method: 'GET',
493
516
  headers: { 'Accept': 'application/json' },
@@ -640,14 +663,9 @@ export class HttpService {
640
663
  getMetrics() {
641
664
  return { ...this.requestMetrics };
642
665
  }
643
- // Test-only utility
644
- static __resetTokensForTests() {
645
- try {
646
- TokenStore.getInstance().clearTokens();
647
- }
648
- catch (error) {
649
- // Silently fail in test cleanup - this is expected behavior
650
- // TokenStore might not be initialized in some test scenarios
651
- }
666
+ // Test-only utility — clears tokens on this instance
667
+ __resetTokensForTests() {
668
+ this.tokenStore.clearTokens();
669
+ this.tokenStore.clearCsrfToken();
652
670
  }
653
671
  }
@@ -23,9 +23,10 @@ export class OxyServicesBase {
23
23
  // Initialize unified HTTP service (handles auth, caching, deduplication, queuing, retry)
24
24
  this.httpService = new HttpService(config);
25
25
  }
26
- // Test-only utility to reset global tokens between jest tests
27
- static __resetTokensForTests() {
28
- HttpService.__resetTokensForTests();
26
+ // Test-only utility to reset tokens on this instance between jest tests
27
+ // Note: tokens are now per-instance, so create new instances in tests for isolation
28
+ __resetTokensForTests() {
29
+ this.httpService.__resetTokensForTests();
29
30
  }
30
31
  /**
31
32
  * Make a request with all performance optimizations
@@ -231,7 +232,7 @@ export class OxyServicesBase {
231
232
  return false;
232
233
  }
233
234
  try {
234
- const res = await this.makeRequest('GET', '/api/auth/validate', undefined, {
235
+ const res = await this.makeRequest('GET', '/auth/validate', undefined, {
235
236
  cache: false,
236
237
  retry: false,
237
238
  });
@@ -5,7 +5,7 @@
5
5
  * Private keys are stored securely using expo-secure-store and never leave the device.
6
6
  */
7
7
  import { ec as EC } from 'elliptic';
8
- import { isWeb, isIOS, isAndroid } from '../utils/platform';
8
+ import { isWeb, isIOS, isAndroid, isReactNative, isNodeJS } from '../utils/platform';
9
9
  import { logger } from '../utils/loggerUtils';
10
10
  import { isDev } from '../shared/utils/debugUtils';
11
11
  // Lazy imports for React Native specific modules
@@ -56,18 +56,6 @@ async function initSecureStore() {
56
56
  }
57
57
  return SecureStore;
58
58
  }
59
- /**
60
- * Check if we're in a React Native environment
61
- */
62
- function isReactNative() {
63
- return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
64
- }
65
- /**
66
- * Check if we're in a Node.js environment
67
- */
68
- function isNodeJS() {
69
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
70
- }
71
59
  /**
72
60
  * Check if we're on web platform
73
61
  * Identity storage is only available on native platforms (iOS/Android)
@@ -6,21 +6,10 @@
6
6
  */
7
7
  import { ec as EC } from 'elliptic';
8
8
  import { KeyManager } from './keyManager';
9
+ import { isReactNative, isNodeJS } from '../utils/platform';
9
10
  // Lazy import for expo-crypto
10
11
  let ExpoCrypto = null;
11
12
  const ec = new EC('secp256k1');
12
- /**
13
- * Check if we're in a React Native environment
14
- */
15
- function isReactNative() {
16
- return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
17
- }
18
- /**
19
- * Check if we're in a Node.js environment
20
- */
21
- function isNodeJS() {
22
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
23
- }
24
13
  /**
25
14
  * Initialize expo-crypto module
26
15
  */
@@ -44,9 +33,7 @@ async function sha256(message) {
44
33
  // In Node.js, use Node's crypto module
45
34
  if (isNodeJS()) {
46
35
  try {
47
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
48
- const getCrypto = new Function('return require("crypto")');
49
- const nodeCrypto = getCrypto();
36
+ const nodeCrypto = await import('crypto');
50
37
  return nodeCrypto.createHash('sha256').update(message).digest('hex');
51
38
  }
52
39
  catch {
package/dist/esm/index.js CHANGED
@@ -40,6 +40,8 @@ export { DEFAULT_CIRCUIT_BREAKER_CONFIG, createCircuitBreakerState, calculateBac
40
40
  export { isDev, debugLog, debugWarn, debugError, createDebugLogger, } from './shared/utils/debugUtils';
41
41
  // --- i18n ---
42
42
  export { translate } from './i18n';
43
+ // --- Auth Helpers ---
44
+ export { SessionSyncRequiredError, AuthenticationFailedError, ensureValidToken, isAuthenticationError, withAuthErrorHandling, authenticatedApiCall, } from './utils/authHelpers';
43
45
  // --- Session Utilities ---
44
46
  export { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual } from './utils/sessionUtils';
45
47
  // --- Constants ---
@@ -11,7 +11,7 @@ export function OxyServicesAnalyticsMixin(Base) {
11
11
  */
12
12
  async trackEvent(eventName, properties) {
13
13
  try {
14
- await this.makeRequest('POST', '/api/analytics/events', {
14
+ await this.makeRequest('POST', '/analytics/events', {
15
15
  event: eventName,
16
16
  properties
17
17
  }, { cache: false, retry: false }); // Don't retry analytics events
@@ -33,7 +33,7 @@ export function OxyServicesAnalyticsMixin(Base) {
33
33
  params.startDate = startDate;
34
34
  if (endDate)
35
35
  params.endDate = endDate;
36
- return await this.makeRequest('GET', '/api/analytics', params, {
36
+ return await this.makeRequest('GET', '/analytics', params, {
37
37
  cache: true,
38
38
  cacheTTL: CACHE_TIMES.LONG,
39
39
  });
@@ -8,7 +8,7 @@ export function OxyServicesAssetsMixin(Base) {
8
8
  */
9
9
  async deleteFile(fileId) {
10
10
  try {
11
- return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
11
+ return await this.makeRequest('DELETE', `/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
12
12
  }
13
13
  catch (error) {
14
14
  throw this.handleError(error);
@@ -29,7 +29,7 @@ export function OxyServicesAssetsMixin(Base) {
29
29
  if (token)
30
30
  params.set('token', token);
31
31
  const qs = params.toString();
32
- return `${base}/api/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
32
+ return `${base}/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
33
33
  }
34
34
  /**
35
35
  * Get file download URL asynchronously (returns signed URL directly from CDN)
@@ -53,7 +53,7 @@ export function OxyServicesAssetsMixin(Base) {
53
53
  paramsObj.limit = limit;
54
54
  if (offset)
55
55
  paramsObj.offset = offset;
56
- return await this.makeRequest('GET', '/api/assets', paramsObj, {
56
+ return await this.makeRequest('GET', '/assets', paramsObj, {
57
57
  cache: false,
58
58
  });
59
59
  }
@@ -66,7 +66,7 @@ export function OxyServicesAssetsMixin(Base) {
66
66
  */
67
67
  async getAccountStorageUsage() {
68
68
  try {
69
- return await this.makeRequest('GET', '/api/storage/usage', undefined, {
69
+ return await this.makeRequest('GET', '/storage/usage', undefined, {
70
70
  cache: false,
71
71
  });
72
72
  }
@@ -109,7 +109,7 @@ export function OxyServicesAssetsMixin(Base) {
109
109
  */
110
110
  async getBatchFileAccess(fileIds, context) {
111
111
  try {
112
- return await this.makeRequest('POST', '/api/assets/batch-access', {
112
+ return await this.makeRequest('POST', '/assets/batch-access', {
113
113
  fileIds,
114
114
  context
115
115
  });
@@ -170,7 +170,7 @@ export function OxyServicesAssetsMixin(Base) {
170
170
  }
171
171
  const response = await this.getClient().request({
172
172
  method: 'POST',
173
- url: '/api/assets/upload',
173
+ url: '/assets/upload',
174
174
  data: formData,
175
175
  cache: false,
176
176
  });
@@ -228,7 +228,7 @@ export function OxyServicesAssetsMixin(Base) {
228
228
  body.visibility = visibility;
229
229
  if (webhookUrl)
230
230
  body.webhookUrl = webhookUrl;
231
- return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, { cache: false });
231
+ return await this.makeRequest('POST', `/assets/${fileId}/links`, body, { cache: false });
232
232
  }
233
233
  catch (error) {
234
234
  throw this.handleError(error);
@@ -239,7 +239,7 @@ export function OxyServicesAssetsMixin(Base) {
239
239
  */
240
240
  async assetUnlink(fileId, app, entityType, entityId) {
241
241
  try {
242
- return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
242
+ return await this.makeRequest('DELETE', `/assets/${fileId}/links`, {
243
243
  app,
244
244
  entityType,
245
245
  entityId
@@ -254,7 +254,7 @@ export function OxyServicesAssetsMixin(Base) {
254
254
  */
255
255
  async assetGet(fileId) {
256
256
  try {
257
- return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
257
+ return await this.makeRequest('GET', `/assets/${fileId}`, undefined, {
258
258
  cache: true,
259
259
  cacheTTL: 5 * 60 * 1000,
260
260
  });
@@ -273,7 +273,7 @@ export function OxyServicesAssetsMixin(Base) {
273
273
  params.variant = variant;
274
274
  if (expiresIn)
275
275
  params.expiresIn = expiresIn;
276
- return await this.makeRequest('GET', `/api/assets/${fileId}/url`, params, {
276
+ return await this.makeRequest('GET', `/assets/${fileId}/url`, params, {
277
277
  cache: true,
278
278
  cacheTTL: 10 * 60 * 1000,
279
279
  });
@@ -287,7 +287,7 @@ export function OxyServicesAssetsMixin(Base) {
287
287
  */
288
288
  async assetRestore(fileId) {
289
289
  try {
290
- return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, { cache: false });
290
+ return await this.makeRequest('POST', `/assets/${fileId}/restore`, undefined, { cache: false });
291
291
  }
292
292
  catch (error) {
293
293
  throw this.handleError(error);
@@ -299,7 +299,7 @@ export function OxyServicesAssetsMixin(Base) {
299
299
  async assetDelete(fileId, force = false) {
300
300
  try {
301
301
  const params = force ? { force: 'true' } : undefined;
302
- return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, { cache: false });
302
+ return await this.makeRequest('DELETE', `/assets/${fileId}`, params, { cache: false });
303
303
  }
304
304
  catch (error) {
305
305
  throw this.handleError(error);
@@ -322,7 +322,7 @@ export function OxyServicesAssetsMixin(Base) {
322
322
  */
323
323
  async assetUpdateVisibility(fileId, visibility) {
324
324
  try {
325
- return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
325
+ return await this.makeRequest('PATCH', `/assets/${fileId}/visibility`, {
326
326
  visibility
327
327
  }, { cache: false });
328
328
  }
@@ -360,7 +360,7 @@ export function OxyServicesAssetsMixin(Base) {
360
360
  params.variant = variant;
361
361
  if (expiresIn)
362
362
  params.expiresIn = expiresIn;
363
- const urlRes = await this.makeRequest('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, Object.keys(params).length ? params : undefined, {
363
+ const urlRes = await this.makeRequest('GET', `/assets/${encodeURIComponent(fileId)}/url`, Object.keys(params).length ? params : undefined, {
364
364
  cache: true,
365
365
  cacheTTL: cacheTTL ?? 10 * 60 * 1000,
366
366
  });
@@ -40,7 +40,7 @@ export function OxyServicesAuthMixin(Base) {
40
40
  if (this._serviceToken && this._serviceTokenExp > Date.now() + 60000) {
41
41
  return this._serviceToken;
42
42
  }
43
- const response = await this.makeRequest('POST', '/api/auth/service-token', { apiKey: key, apiSecret: secret }, { cache: false, retry: false });
43
+ const response = await this.makeRequest('POST', '/auth/service-token', { apiKey: key, apiSecret: secret }, { cache: false, retry: false });
44
44
  this._serviceToken = response.token;
45
45
  this._serviceTokenExp = Date.now() + response.expiresIn * 1000;
46
46
  return this._serviceToken;
@@ -74,7 +74,7 @@ export function OxyServicesAuthMixin(Base) {
74
74
  */
75
75
  async register(publicKey, signature, timestamp) {
76
76
  try {
77
- const res = await this.makeRequest('POST', '/api/auth/register', {
77
+ const res = await this.makeRequest('POST', '/auth/register', {
78
78
  publicKey,
79
79
  signature,
80
80
  timestamp,
@@ -96,7 +96,7 @@ export function OxyServicesAuthMixin(Base) {
96
96
  */
97
97
  async requestChallenge(publicKey) {
98
98
  try {
99
- return await this.makeRequest('POST', '/api/auth/challenge', {
99
+ return await this.makeRequest('POST', '/auth/challenge', {
100
100
  publicKey,
101
101
  }, { cache: false });
102
102
  }
@@ -116,7 +116,7 @@ export function OxyServicesAuthMixin(Base) {
116
116
  */
117
117
  async verifyChallenge(publicKey, challenge, signature, timestamp, deviceName, deviceFingerprint) {
118
118
  try {
119
- return await this.makeRequest('POST', '/api/auth/verify', {
119
+ return await this.makeRequest('POST', '/auth/verify', {
120
120
  publicKey,
121
121
  challenge,
122
122
  signature,
@@ -134,7 +134,7 @@ export function OxyServicesAuthMixin(Base) {
134
134
  */
135
135
  async checkPublicKeyRegistered(publicKey) {
136
136
  try {
137
- return await this.makeRequest('GET', `/api/auth/check-publickey/${encodeURIComponent(publicKey)}`, undefined, { cache: false });
137
+ return await this.makeRequest('GET', `/auth/check-publickey/${encodeURIComponent(publicKey)}`, undefined, { cache: false });
138
138
  }
139
139
  catch (error) {
140
140
  throw this.handleError(error);
@@ -145,7 +145,7 @@ export function OxyServicesAuthMixin(Base) {
145
145
  */
146
146
  async getUserByPublicKey(publicKey) {
147
147
  try {
148
- return await this.makeRequest('GET', `/api/auth/user/${encodeURIComponent(publicKey)}`, undefined, { cache: true, cacheTTL: 2 * 60 * 1000 });
148
+ return await this.makeRequest('GET', `/auth/user/${encodeURIComponent(publicKey)}`, undefined, { cache: true, cacheTTL: 2 * 60 * 1000 });
149
149
  }
150
150
  catch (error) {
151
151
  throw this.handleError(error);
@@ -156,7 +156,7 @@ export function OxyServicesAuthMixin(Base) {
156
156
  */
157
157
  async getUserBySession(sessionId) {
158
158
  try {
159
- return await this.makeRequest('GET', `/api/session/user/${sessionId}`, undefined, {
159
+ return await this.makeRequest('GET', `/session/user/${sessionId}`, undefined, {
160
160
  cache: true,
161
161
  cacheTTL: 2 * 60 * 1000,
162
162
  });
@@ -174,7 +174,7 @@ export function OxyServicesAuthMixin(Base) {
174
174
  return [];
175
175
  }
176
176
  const uniqueSessionIds = Array.from(new Set(sessionIds)).sort();
177
- return await this.makeRequest('POST', '/api/session/users/batch', { sessionIds: uniqueSessionIds }, {
177
+ return await this.makeRequest('POST', '/session/users/batch', { sessionIds: uniqueSessionIds }, {
178
178
  cache: true,
179
179
  cacheTTL: 2 * 60 * 1000,
180
180
  deduplicate: true,
@@ -189,7 +189,7 @@ export function OxyServicesAuthMixin(Base) {
189
189
  */
190
190
  async getTokenBySession(sessionId) {
191
191
  try {
192
- const res = await this.makeRequest('GET', `/api/session/token/${sessionId}`, undefined, { cache: false, retry: false });
192
+ const res = await this.makeRequest('GET', `/session/token/${sessionId}`, undefined, { cache: false, retry: false });
193
193
  this.setTokens(res.accessToken);
194
194
  return res;
195
195
  }
@@ -202,7 +202,7 @@ export function OxyServicesAuthMixin(Base) {
202
202
  */
203
203
  async getSessionsBySessionId(sessionId) {
204
204
  try {
205
- return await this.makeRequest('GET', `/api/session/sessions/${sessionId}`, undefined, {
205
+ return await this.makeRequest('GET', `/session/sessions/${sessionId}`, undefined, {
206
206
  cache: false,
207
207
  });
208
208
  }
@@ -216,8 +216,8 @@ export function OxyServicesAuthMixin(Base) {
216
216
  async logoutSession(sessionId, targetSessionId) {
217
217
  try {
218
218
  const url = targetSessionId
219
- ? `/api/session/logout/${sessionId}/${targetSessionId}`
220
- : `/api/session/logout/${sessionId}`;
219
+ ? `/session/logout/${sessionId}/${targetSessionId}`
220
+ : `/session/logout/${sessionId}`;
221
221
  await this.makeRequest('POST', url, undefined, { cache: false });
222
222
  }
223
223
  catch (error) {
@@ -229,7 +229,7 @@ export function OxyServicesAuthMixin(Base) {
229
229
  */
230
230
  async logoutAllSessions(sessionId) {
231
231
  try {
232
- await this.makeRequest('POST', `/api/session/logout-all/${sessionId}`, undefined, { cache: false });
232
+ await this.makeRequest('POST', `/session/logout-all/${sessionId}`, undefined, { cache: false });
233
233
  }
234
234
  catch (error) {
235
235
  throw this.handleError(error);
@@ -245,11 +245,11 @@ export function OxyServicesAuthMixin(Base) {
245
245
  urlParams.deviceFingerprint = options.deviceFingerprint;
246
246
  if (options.useHeaderValidation)
247
247
  urlParams.useHeaderValidation = 'true';
248
- return await this.makeRequest('GET', `/api/session/validate/${sessionId}`, urlParams, { cache: false });
248
+ return await this.makeRequest('GET', `/session/validate/${sessionId}`, urlParams, { cache: false });
249
249
  }
250
250
  catch (error) {
251
251
  // Session is invalid — clear any cached user data for this session (#196)
252
- this.clearCacheEntry(`GET:/api/session/user/${sessionId}`);
252
+ this.clearCacheEntry(`GET:/session/user/${sessionId}`);
253
253
  throw this.handleError(error);
254
254
  }
255
255
  }
@@ -258,7 +258,7 @@ export function OxyServicesAuthMixin(Base) {
258
258
  */
259
259
  async checkUsernameAvailability(username) {
260
260
  try {
261
- return await this.makeRequest('GET', `/api/auth/check-username/${username}`, undefined, { cache: false });
261
+ return await this.makeRequest('GET', `/auth/check-username/${username}`, undefined, { cache: false });
262
262
  }
263
263
  catch (error) {
264
264
  throw this.handleError(error);
@@ -269,7 +269,7 @@ export function OxyServicesAuthMixin(Base) {
269
269
  */
270
270
  async checkEmailAvailability(email) {
271
271
  try {
272
- return await this.makeRequest('GET', `/api/auth/check-email/${email}`, undefined, { cache: false });
272
+ return await this.makeRequest('GET', `/auth/check-email/${email}`, undefined, { cache: false });
273
273
  }
274
274
  catch (error) {
275
275
  throw this.handleError(error);
@@ -280,7 +280,7 @@ export function OxyServicesAuthMixin(Base) {
280
280
  */
281
281
  async signUp(username, email, password, deviceName, deviceFingerprint) {
282
282
  try {
283
- return await this.makeRequest('POST', '/api/auth/signup', {
283
+ return await this.makeRequest('POST', '/auth/signup', {
284
284
  username,
285
285
  email,
286
286
  password,
@@ -297,7 +297,7 @@ export function OxyServicesAuthMixin(Base) {
297
297
  */
298
298
  async signIn(identifier, password, deviceName, deviceFingerprint) {
299
299
  try {
300
- return await this.makeRequest('POST', '/api/auth/login', {
300
+ return await this.makeRequest('POST', '/auth/login', {
301
301
  identifier,
302
302
  password,
303
303
  deviceName,