@oxyhq/core 1.5.0 → 1.6.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 (84) hide show
  1. package/dist/cjs/AuthManager.js +1 -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/mixins/OxyServices.analytics.js +2 -2
  7. package/dist/cjs/mixins/OxyServices.assets.js +14 -14
  8. package/dist/cjs/mixins/OxyServices.auth.js +19 -19
  9. package/dist/cjs/mixins/OxyServices.developer.js +6 -6
  10. package/dist/cjs/mixins/OxyServices.devices.js +7 -7
  11. package/dist/cjs/mixins/OxyServices.features.js +23 -23
  12. package/dist/cjs/mixins/OxyServices.fedcm.js +1 -1
  13. package/dist/cjs/mixins/OxyServices.karma.js +6 -6
  14. package/dist/cjs/mixins/OxyServices.location.js +2 -2
  15. package/dist/cjs/mixins/OxyServices.payment.js +6 -6
  16. package/dist/cjs/mixins/OxyServices.popup.js +1 -1
  17. package/dist/cjs/mixins/OxyServices.privacy.js +6 -6
  18. package/dist/cjs/mixins/OxyServices.security.js +3 -3
  19. package/dist/cjs/mixins/OxyServices.user.js +22 -22
  20. package/dist/cjs/mixins/OxyServices.utility.js +16 -7
  21. package/dist/cjs/utils/platform.js +14 -0
  22. package/dist/esm/AuthManager.js +1 -1
  23. package/dist/esm/HttpService.js +87 -69
  24. package/dist/esm/OxyServices.base.js +5 -4
  25. package/dist/esm/crypto/keyManager.js +1 -13
  26. package/dist/esm/crypto/signatureService.js +2 -15
  27. package/dist/esm/mixins/OxyServices.analytics.js +2 -2
  28. package/dist/esm/mixins/OxyServices.assets.js +14 -14
  29. package/dist/esm/mixins/OxyServices.auth.js +19 -19
  30. package/dist/esm/mixins/OxyServices.developer.js +6 -6
  31. package/dist/esm/mixins/OxyServices.devices.js +7 -7
  32. package/dist/esm/mixins/OxyServices.features.js +23 -23
  33. package/dist/esm/mixins/OxyServices.fedcm.js +1 -1
  34. package/dist/esm/mixins/OxyServices.karma.js +6 -6
  35. package/dist/esm/mixins/OxyServices.location.js +2 -2
  36. package/dist/esm/mixins/OxyServices.payment.js +6 -6
  37. package/dist/esm/mixins/OxyServices.popup.js +1 -1
  38. package/dist/esm/mixins/OxyServices.privacy.js +6 -6
  39. package/dist/esm/mixins/OxyServices.security.js +3 -3
  40. package/dist/esm/mixins/OxyServices.user.js +22 -22
  41. package/dist/esm/mixins/OxyServices.utility.js +16 -7
  42. package/dist/esm/utils/platform.js +12 -0
  43. package/dist/types/HttpService.d.ts +4 -1
  44. package/dist/types/OxyServices.base.d.ts +1 -1
  45. package/dist/types/mixins/OxyServices.analytics.d.ts +1 -1
  46. package/dist/types/mixins/OxyServices.assets.d.ts +1 -1
  47. package/dist/types/mixins/OxyServices.auth.d.ts +1 -1
  48. package/dist/types/mixins/OxyServices.developer.d.ts +1 -1
  49. package/dist/types/mixins/OxyServices.devices.d.ts +1 -1
  50. package/dist/types/mixins/OxyServices.features.d.ts +1 -1
  51. package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -1
  52. package/dist/types/mixins/OxyServices.karma.d.ts +1 -1
  53. package/dist/types/mixins/OxyServices.language.d.ts +1 -1
  54. package/dist/types/mixins/OxyServices.location.d.ts +1 -1
  55. package/dist/types/mixins/OxyServices.payment.d.ts +1 -1
  56. package/dist/types/mixins/OxyServices.popup.d.ts +1 -1
  57. package/dist/types/mixins/OxyServices.privacy.d.ts +1 -1
  58. package/dist/types/mixins/OxyServices.redirect.d.ts +1 -1
  59. package/dist/types/mixins/OxyServices.security.d.ts +1 -1
  60. package/dist/types/mixins/OxyServices.user.d.ts +1 -1
  61. package/dist/types/mixins/OxyServices.utility.d.ts +7 -6
  62. package/dist/types/utils/platform.d.ts +8 -0
  63. package/package.json +1 -1
  64. package/src/AuthManager.ts +1 -1
  65. package/src/HttpService.ts +85 -67
  66. package/src/OxyServices.base.ts +5 -4
  67. package/src/crypto/keyManager.ts +1 -15
  68. package/src/crypto/signatureService.ts +2 -17
  69. package/src/mixins/OxyServices.analytics.ts +2 -2
  70. package/src/mixins/OxyServices.assets.ts +14 -14
  71. package/src/mixins/OxyServices.auth.ts +19 -19
  72. package/src/mixins/OxyServices.developer.ts +6 -6
  73. package/src/mixins/OxyServices.devices.ts +7 -7
  74. package/src/mixins/OxyServices.features.ts +23 -23
  75. package/src/mixins/OxyServices.fedcm.ts +1 -1
  76. package/src/mixins/OxyServices.karma.ts +6 -6
  77. package/src/mixins/OxyServices.location.ts +2 -2
  78. package/src/mixins/OxyServices.payment.ts +6 -6
  79. package/src/mixins/OxyServices.popup.ts +1 -1
  80. package/src/mixins/OxyServices.privacy.ts +6 -6
  81. package/src/mixins/OxyServices.security.ts +3 -3
  82. package/src/mixins/OxyServices.user.ts +22 -22
  83. package/src/mixins/OxyServices.utility.ts +18 -8
  84. package/src/utils/platform.ts +14 -0
@@ -78,6 +78,7 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
78
78
  httpService: import("../HttpService").HttpService;
79
79
  cloudURL: string;
80
80
  config: import("../OxyServices.base").OxyConfig;
81
+ __resetTokensForTests(): void;
81
82
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
82
83
  getBaseURL(): string;
83
84
  getClient(): import("../HttpService").HttpService;
@@ -120,5 +121,4 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
120
121
  [key: string]: any;
121
122
  }>;
122
123
  };
123
- __resetTokensForTests(): void;
124
124
  } & T;
@@ -194,6 +194,7 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
194
194
  httpService: import("../HttpService").HttpService;
195
195
  cloudURL: string;
196
196
  config: import("../OxyServices.base").OxyConfig;
197
+ __resetTokensForTests(): void;
197
198
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
198
199
  getBaseURL(): string;
199
200
  getClient(): import("../HttpService").HttpService;
@@ -242,6 +243,5 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
242
243
  readonly STATE_STORAGE_KEY: "oxy_auth_state";
243
244
  readonly PRE_AUTH_URL_KEY: "oxy_pre_auth_url";
244
245
  readonly NONCE_STORAGE_KEY: "oxy_auth_nonce";
245
- __resetTokensForTests(): void;
246
246
  } & T;
247
247
  export { OxyServicesRedirectAuthMixin as RedirectAuthMixin };
@@ -34,6 +34,7 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
34
34
  httpService: import("../HttpService").HttpService;
35
35
  cloudURL: string;
36
36
  config: import("../OxyServices.base").OxyConfig;
37
+ __resetTokensForTests(): void;
37
38
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
38
39
  getBaseURL(): string;
39
40
  getClient(): import("../HttpService").HttpService;
@@ -76,5 +77,4 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
76
77
  [key: string]: any;
77
78
  }>;
78
79
  };
79
- __resetTokensForTests(): void;
80
80
  } & T;
@@ -138,6 +138,7 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
138
138
  httpService: import("../HttpService").HttpService;
139
139
  cloudURL: string;
140
140
  config: import("../OxyServices.base").OxyConfig;
141
+ __resetTokensForTests(): void;
141
142
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
142
143
  getBaseURL(): string;
143
144
  getClient(): import("../HttpService").HttpService;
@@ -180,5 +181,4 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
180
181
  [key: string]: any;
181
182
  }>;
182
183
  };
183
- __resetTokensForTests(): void;
184
184
  } & T;
@@ -49,19 +49,19 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
49
49
  *
50
50
  * const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
51
51
  *
52
- * // Protect all routes under /api/protected
53
- * app.use('/api/protected', oxy.auth());
52
+ * // Protect all routes under /protected
53
+ * app.use('/protected', oxy.auth());
54
54
  *
55
55
  * // Access user in route handler
56
- * app.get('/api/protected/me', (req, res) => {
56
+ * app.get('/protected/me', (req, res) => {
57
57
  * res.json({ userId: req.userId, user: req.user });
58
58
  * });
59
59
  *
60
60
  * // Load full user profile from API
61
- * app.use('/api/admin', oxy.auth({ loadUser: true }));
61
+ * app.use('/admin', oxy.auth({ loadUser: true }));
62
62
  *
63
63
  * // Optional auth - attach user if present, don't block if absent
64
- * app.use('/api/public', oxy.auth({ optional: true }));
64
+ * app.use('/public', oxy.auth({ optional: true }));
65
65
  * ```
66
66
  *
67
67
  * @param options Optional configuration
@@ -111,10 +111,12 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
111
111
  */
112
112
  serviceAuth(options?: {
113
113
  debug?: boolean;
114
+ jwtSecret?: string;
114
115
  }): (req: any, res: any, next: any) => Promise<void>;
115
116
  httpService: import("../HttpService").HttpService;
116
117
  cloudURL: string;
117
118
  config: import("../OxyServices.base").OxyConfig;
119
+ __resetTokensForTests(): void;
118
120
  makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
119
121
  getBaseURL(): string;
120
122
  getClient(): import("../HttpService").HttpService;
@@ -157,6 +159,5 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
157
159
  [key: string]: any;
158
160
  }>;
159
161
  };
160
- __resetTokensForTests(): void;
161
162
  } & T;
162
163
  export {};
@@ -27,6 +27,14 @@ export declare function isIOS(): boolean;
27
27
  * Check if running on Android
28
28
  */
29
29
  export declare function isAndroid(): boolean;
30
+ /**
31
+ * Check if running in React Native
32
+ */
33
+ export declare function isReactNative(): boolean;
34
+ /**
35
+ * Check if running in Node.js
36
+ */
37
+ export declare function isNodeJS(): boolean;
30
38
  /**
31
39
  * Set the platform OS explicitly
32
40
  * Called by React Native entry point to register the platform
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -308,7 +308,7 @@ export class AuthManager {
308
308
  // Use session-based token endpoint which handles auto-refresh server-side
309
309
  const response = await httpService.request<{ accessToken: string; expiresAt: string }>({
310
310
  method: 'GET',
311
- url: `/api/session/token/${sessionId}`,
311
+ url: `/session/token/${sessionId}`,
312
312
  cache: false,
313
313
  retry: false,
314
314
  });
@@ -54,27 +54,21 @@ interface RequestConfig extends RequestOptions {
54
54
  params?: Record<string, unknown>;
55
55
  /** @internal Used to prevent infinite auth retry loops */
56
56
  _isAuthRetry?: boolean;
57
+ /** @internal Used to prevent infinite CSRF retry loops */
58
+ _isCsrfRetry?: boolean;
57
59
  }
58
60
 
59
61
  /**
60
- * Token store for authentication (singleton)
62
+ * Token store for authentication (instance-based)
63
+ * Each HttpService gets its own TokenStore to prevent conflicts
64
+ * when multiple OxyServices instances coexist server-side.
61
65
  */
62
66
  class TokenStore {
63
- private static instance: TokenStore;
64
67
  private accessToken: string | null = null;
65
68
  private refreshToken: string | null = null;
66
69
  private csrfToken: string | null = null;
67
70
  private csrfTokenFetchPromise: Promise<string | null> | null = null;
68
71
 
69
- private constructor() {}
70
-
71
- static getInstance(): TokenStore {
72
- if (!TokenStore.instance) {
73
- TokenStore.instance = new TokenStore();
74
- }
75
- return TokenStore.instance;
76
- }
77
-
78
72
  setTokens(accessToken: string, refreshToken = ''): void {
79
73
  this.accessToken = accessToken;
80
74
  this.refreshToken = refreshToken;
@@ -134,6 +128,7 @@ export class HttpService {
134
128
  private logger: SimpleLogger;
135
129
  private config: OxyConfig;
136
130
  private tokenRefreshPromise: Promise<string | null> | null = null;
131
+ private tokenRefreshCooldownUntil: number = 0;
137
132
  private _onTokenRefreshed: ((accessToken: string) => void) | null = null;
138
133
 
139
134
  // Performance monitoring
@@ -149,7 +144,7 @@ export class HttpService {
149
144
  constructor(config: OxyConfig) {
150
145
  this.config = config;
151
146
  this.baseURL = config.baseURL;
152
- this.tokenStore = TokenStore.getInstance();
147
+ this.tokenStore = new TokenStore();
153
148
 
154
149
  this.logger = new SimpleLogger(
155
150
  config.enableLogging || false,
@@ -346,6 +341,20 @@ export class HttpService {
346
341
  this.tokenStore.clearTokens();
347
342
  }
348
343
 
344
+ // On 403 with CSRF error, clear cached token and retry once
345
+ if (response.status === 403 && !config._isCsrfRetry) {
346
+ try {
347
+ const clonedResponse = response.clone();
348
+ const errBody = await clonedResponse.json() as { code?: string } | null;
349
+ if (errBody?.code === 'CSRF_TOKEN_INVALID' || errBody?.code === 'CSRF_TOKEN_MISSING') {
350
+ this.tokenStore.clearCsrfToken();
351
+ return this.request<T>({ ...config, _isCsrfRetry: true, retry: false });
352
+ }
353
+ } catch {
354
+ // Failed to parse error body — not a CSRF error
355
+ }
356
+ }
357
+
349
358
  // Try to parse error response (handle empty/malformed JSON)
350
359
  let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
351
360
  const contentType = response.headers.get('content-type');
@@ -518,52 +527,58 @@ export class HttpService {
518
527
  }
519
528
 
520
529
  const fetchPromise = (async () => {
521
- try {
522
- if (isDev()) console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/api/csrf-token`);
530
+ const maxAttempts = 2;
531
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
532
+ try {
533
+ if (isDev()) console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
523
534
 
524
- // Use AbortController for timeout (more compatible than AbortSignal.timeout)
525
- const controller = new AbortController();
526
- const timeoutId = setTimeout(() => controller.abort(), 5000);
535
+ // Use AbortController for timeout (more compatible than AbortSignal.timeout)
536
+ const controller = new AbortController();
537
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
527
538
 
528
- const response = await fetch(`${this.baseURL}/api/csrf-token`, {
529
- method: 'GET',
530
- headers: { 'Accept': 'application/json' },
531
- credentials: 'include', // Required to receive and send cookies
532
- signal: controller.signal,
533
- });
539
+ const response = await fetch(`${this.baseURL}/csrf-token`, {
540
+ method: 'GET',
541
+ headers: { 'Accept': 'application/json' },
542
+ credentials: 'include', // Required to receive and send cookies
543
+ signal: controller.signal,
544
+ });
534
545
 
535
- clearTimeout(timeoutId);
546
+ clearTimeout(timeoutId);
536
547
 
537
- if (isDev()) console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
548
+ if (isDev()) console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
538
549
 
539
- if (response.ok) {
540
- const data = await response.json() as { csrfToken?: string };
541
- if (isDev()) console.log('[HttpService] CSRF response data:', data);
542
- const token = data.csrfToken || null;
543
- this.tokenStore.setCsrfToken(token);
544
- this.logger.debug('CSRF token fetched');
545
- return token;
546
- }
550
+ if (response.ok) {
551
+ const data = await response.json() as { csrfToken?: string };
552
+ if (isDev()) console.log('[HttpService] CSRF response data:', data);
553
+ const token = data.csrfToken || null;
554
+ this.tokenStore.setCsrfToken(token);
555
+ this.logger.debug('CSRF token fetched');
556
+ return token;
557
+ }
547
558
 
548
- // Also check response header for CSRF token
549
- const headerToken = response.headers.get('X-CSRF-Token');
550
- if (headerToken) {
551
- this.tokenStore.setCsrfToken(headerToken);
552
- this.logger.debug('CSRF token from header');
553
- return headerToken;
554
- }
559
+ // Also check response header for CSRF token
560
+ const headerToken = response.headers.get('X-CSRF-Token');
561
+ if (headerToken) {
562
+ this.tokenStore.setCsrfToken(headerToken);
563
+ this.logger.debug('CSRF token from header');
564
+ return headerToken;
565
+ }
555
566
 
556
- if (isDev()) console.log('[HttpService] CSRF fetch failed with status:', response.status);
557
- this.logger.warn('Failed to fetch CSRF token:', response.status);
558
- return null;
559
- } catch (error) {
560
- if (isDev()) console.log('[HttpService] CSRF fetch error:', error);
561
- this.logger.warn('CSRF token fetch error:', error);
562
- return null;
563
- } finally {
564
- this.tokenStore.setCsrfTokenFetchPromise(null);
567
+ if (isDev()) console.log('[HttpService] CSRF fetch failed with status:', response.status);
568
+ this.logger.warn('Failed to fetch CSRF token:', response.status);
569
+ } catch (error) {
570
+ if (isDev()) console.log('[HttpService] CSRF fetch error:', error);
571
+ this.logger.warn('CSRF token fetch error:', error);
572
+ }
573
+ // Wait before retry (500ms)
574
+ if (attempt < maxAttempts) {
575
+ await new Promise(resolve => setTimeout(resolve, 500));
576
+ }
565
577
  }
566
- })();
578
+ return null;
579
+ })().finally(() => {
580
+ this.tokenStore.setCsrfTokenFetchPromise(null);
581
+ });
567
582
 
568
583
  this.tokenStore.setCsrfTokenFetchPromise(fetchPromise);
569
584
  return fetchPromise;
@@ -584,16 +599,23 @@ export class HttpService {
584
599
 
585
600
  // If token expires in less than 60 seconds, refresh it
586
601
  if (decoded.exp && decoded.exp - currentTime < 60 && decoded.sessionId) {
587
- // Deduplicate concurrent refresh attempts
588
- if (!this.tokenRefreshPromise) {
589
- this.tokenRefreshPromise = this._refreshTokenFromSession(decoded.sessionId);
602
+ // Skip if we recently failed a refresh (5s cooldown to prevent storms)
603
+ if (Date.now() < this.tokenRefreshCooldownUntil) {
604
+ return `Bearer ${accessToken}`;
590
605
  }
591
- try {
592
- const result = await this.tokenRefreshPromise;
593
- if (result) return result;
594
- } finally {
595
- this.tokenRefreshPromise = null;
606
+ // Deduplicate concurrent refresh attempts. The promise is shared
607
+ // across all concurrent callers and cleared only after it settles,
608
+ // so every awaiter receives the same result.
609
+ if (!this.tokenRefreshPromise) {
610
+ this.tokenRefreshPromise = this._refreshTokenFromSession(decoded.sessionId)
611
+ .then((result) => {
612
+ if (!result) this.tokenRefreshCooldownUntil = Date.now() + 5000;
613
+ return result;
614
+ })
615
+ .finally(() => { this.tokenRefreshPromise = null; });
596
616
  }
617
+ const result = await this.tokenRefreshPromise;
618
+ if (result) return result;
597
619
  }
598
620
 
599
621
  return `Bearer ${accessToken}`;
@@ -605,7 +627,7 @@ export class HttpService {
605
627
 
606
628
  private async _refreshTokenFromSession(sessionId: string): Promise<string | null> {
607
629
  try {
608
- const refreshUrl = `${this.baseURL}/api/session/token/${sessionId}`;
630
+ const refreshUrl = `${this.baseURL}/session/token/${sessionId}`;
609
631
  const response = await fetch(refreshUrl, {
610
632
  method: 'GET',
611
633
  headers: { 'Accept': 'application/json' },
@@ -778,14 +800,10 @@ export class HttpService {
778
800
  return { ...this.requestMetrics };
779
801
  }
780
802
 
781
- // Test-only utility
782
- static __resetTokensForTests(): void {
783
- try {
784
- TokenStore.getInstance().clearTokens();
785
- } catch (error) {
786
- // Silently fail in test cleanup - this is expected behavior
787
- // TokenStore might not be initialized in some test scenarios
788
- }
803
+ // Test-only utility — clears tokens on this instance
804
+ __resetTokensForTests(): void {
805
+ this.tokenStore.clearTokens();
806
+ this.tokenStore.clearCsrfToken();
789
807
  }
790
808
  }
791
809
 
@@ -41,9 +41,10 @@ export class OxyServicesBase {
41
41
  this.httpService = new HttpService(config);
42
42
  }
43
43
 
44
- // Test-only utility to reset global tokens between jest tests
45
- static __resetTokensForTests(): void {
46
- HttpService.__resetTokensForTests();
44
+ // Test-only utility to reset tokens on this instance between jest tests
45
+ // Note: tokens are now per-instance, so create new instances in tests for isolation
46
+ __resetTokensForTests(): void {
47
+ this.httpService.__resetTokensForTests();
47
48
  }
48
49
 
49
50
  /**
@@ -304,7 +305,7 @@ export class OxyServicesBase {
304
305
  }
305
306
 
306
307
  try {
307
- const res = await this.makeRequest<{ valid: boolean }>('GET', '/api/auth/validate', undefined, {
308
+ const res = await this.makeRequest<{ valid: boolean }>('GET', '/auth/validate', undefined, {
308
309
  cache: false,
309
310
  retry: false,
310
311
  });
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { ec as EC } from 'elliptic';
9
9
  import type { ECKeyPair } from 'elliptic';
10
- import { isWeb, isIOS, isAndroid } from '../utils/platform';
10
+ import { isWeb, isIOS, isAndroid, isReactNative, isNodeJS } from '../utils/platform';
11
11
  import { logger } from '../utils/loggerUtils';
12
12
  import { isDev } from '../shared/utils/debugUtils';
13
13
 
@@ -64,20 +64,6 @@ async function initSecureStore(): Promise<typeof import('expo-secure-store')> {
64
64
  return SecureStore;
65
65
  }
66
66
 
67
- /**
68
- * Check if we're in a React Native environment
69
- */
70
- function isReactNative(): boolean {
71
- return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
72
- }
73
-
74
- /**
75
- * Check if we're in a Node.js environment
76
- */
77
- function isNodeJS(): boolean {
78
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
79
- }
80
-
81
67
  /**
82
68
  * Check if we're on web platform
83
69
  * Identity storage is only available on native platforms (iOS/Android)
@@ -7,26 +7,13 @@
7
7
 
8
8
  import { ec as EC } from 'elliptic';
9
9
  import { KeyManager } from './keyManager';
10
+ import { isReactNative, isNodeJS } from '../utils/platform';
10
11
 
11
12
  // Lazy import for expo-crypto
12
13
  let ExpoCrypto: typeof import('expo-crypto') | null = null;
13
14
 
14
15
  const ec = new EC('secp256k1');
15
16
 
16
- /**
17
- * Check if we're in a React Native environment
18
- */
19
- function isReactNative(): boolean {
20
- return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
21
- }
22
-
23
- /**
24
- * Check if we're in a Node.js environment
25
- */
26
- function isNodeJS(): boolean {
27
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
28
- }
29
-
30
17
  /**
31
18
  * Initialize expo-crypto module
32
19
  */
@@ -55,9 +42,7 @@ async function sha256(message: string): Promise<string> {
55
42
  // In Node.js, use Node's crypto module
56
43
  if (isNodeJS()) {
57
44
  try {
58
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
59
- const getCrypto = new Function('return require("crypto")');
60
- const nodeCrypto = getCrypto();
45
+ const nodeCrypto = await import('crypto');
61
46
  return nodeCrypto.createHash('sha256').update(message).digest('hex');
62
47
  } catch {
63
48
  // Fall through to Web Crypto API
@@ -19,7 +19,7 @@ export function OxyServicesAnalyticsMixin<T extends typeof OxyServicesBase>(Base
19
19
  */
20
20
  async trackEvent(eventName: string, properties?: Record<string, any>): Promise<void> {
21
21
  try {
22
- await this.makeRequest('POST', '/api/analytics/events', {
22
+ await this.makeRequest('POST', '/analytics/events', {
23
23
  event: eventName,
24
24
  properties
25
25
  }, { cache: false, retry: false }); // Don't retry analytics events
@@ -40,7 +40,7 @@ export function OxyServicesAnalyticsMixin<T extends typeof OxyServicesBase>(Base
40
40
  if (startDate) params.startDate = startDate;
41
41
  if (endDate) params.endDate = endDate;
42
42
 
43
- return await this.makeRequest('GET', '/api/analytics', params, {
43
+ return await this.makeRequest('GET', '/analytics', params, {
44
44
  cache: true,
45
45
  cacheTTL: CACHE_TIMES.LONG,
46
46
  });
@@ -12,7 +12,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
12
12
  */
13
13
  async deleteFile(fileId: string): Promise<any> {
14
14
  try {
15
- return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
15
+ return await this.makeRequest('DELETE', `/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
16
16
  } catch (error) {
17
17
  throw this.handleError(error);
18
18
  }
@@ -31,7 +31,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
31
31
  if (token) params.set('token', token);
32
32
 
33
33
  const qs = params.toString();
34
- return `${base}/api/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
34
+ return `${base}/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
35
35
  }
36
36
 
37
37
  /**
@@ -60,7 +60,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
60
60
  const paramsObj: any = {};
61
61
  if (limit) paramsObj.limit = limit;
62
62
  if (offset) paramsObj.offset = offset;
63
- return await this.makeRequest('GET', '/api/assets', paramsObj, {
63
+ return await this.makeRequest('GET', '/assets', paramsObj, {
64
64
  cache: false,
65
65
  });
66
66
  } catch (error) {
@@ -73,7 +73,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
73
73
  */
74
74
  async getAccountStorageUsage(): Promise<AccountStorageUsageResponse> {
75
75
  try {
76
- return await this.makeRequest<AccountStorageUsageResponse>('GET', '/api/storage/usage', undefined, {
76
+ return await this.makeRequest<AccountStorageUsageResponse>('GET', '/storage/usage', undefined, {
77
77
  cache: false,
78
78
  });
79
79
  } catch (error) {
@@ -128,7 +128,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
128
128
  */
129
129
  async getBatchFileAccess(fileIds: string[], context?: string): Promise<Record<string, any>> {
130
130
  try {
131
- return await this.makeRequest('POST', '/api/assets/batch-access', {
131
+ return await this.makeRequest('POST', '/assets/batch-access', {
132
132
  fileIds,
133
133
  context
134
134
  });
@@ -191,7 +191,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
191
191
 
192
192
  const response = await this.getClient().request<{ file: any }>({
193
193
  method: 'POST',
194
- url: '/api/assets/upload',
194
+ url: '/assets/upload',
195
195
  data: formData,
196
196
  cache: false,
197
197
  });
@@ -250,7 +250,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
250
250
  const body: any = { app, entityType, entityId };
251
251
  if (visibility) body.visibility = visibility;
252
252
  if (webhookUrl) body.webhookUrl = webhookUrl;
253
- return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, { cache: false });
253
+ return await this.makeRequest('POST', `/assets/${fileId}/links`, body, { cache: false });
254
254
  } catch (error) {
255
255
  throw this.handleError(error);
256
256
  }
@@ -261,7 +261,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
261
261
  */
262
262
  async assetUnlink(fileId: string, app: string, entityType: string, entityId: string): Promise<any> {
263
263
  try {
264
- return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
264
+ return await this.makeRequest('DELETE', `/assets/${fileId}/links`, {
265
265
  app,
266
266
  entityType,
267
267
  entityId
@@ -276,7 +276,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
276
276
  */
277
277
  async assetGet(fileId: string): Promise<any> {
278
278
  try {
279
- return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
279
+ return await this.makeRequest('GET', `/assets/${fileId}`, undefined, {
280
280
  cache: true,
281
281
  cacheTTL: 5 * 60 * 1000,
282
282
  });
@@ -294,7 +294,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
294
294
  if (variant) params.variant = variant;
295
295
  if (expiresIn) params.expiresIn = expiresIn;
296
296
 
297
- return await this.makeRequest<AssetUrlResponse>('GET', `/api/assets/${fileId}/url`, params, {
297
+ return await this.makeRequest<AssetUrlResponse>('GET', `/assets/${fileId}/url`, params, {
298
298
  cache: true,
299
299
  cacheTTL: 10 * 60 * 1000,
300
300
  });
@@ -308,7 +308,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
308
308
  */
309
309
  async assetRestore(fileId: string): Promise<any> {
310
310
  try {
311
- return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, { cache: false });
311
+ return await this.makeRequest('POST', `/assets/${fileId}/restore`, undefined, { cache: false });
312
312
  } catch (error) {
313
313
  throw this.handleError(error);
314
314
  }
@@ -320,7 +320,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
320
320
  async assetDelete(fileId: string, force: boolean = false): Promise<any> {
321
321
  try {
322
322
  const params: any = force ? { force: 'true' } : undefined;
323
- return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, { cache: false });
323
+ return await this.makeRequest('DELETE', `/assets/${fileId}`, params, { cache: false });
324
324
  } catch (error) {
325
325
  throw this.handleError(error);
326
326
  }
@@ -343,7 +343,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
343
343
  */
344
344
  async assetUpdateVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<any> {
345
345
  try {
346
- return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
346
+ return await this.makeRequest('PATCH', `/assets/${fileId}/visibility`, {
347
347
  visibility
348
348
  }, { cache: false });
349
349
  } catch (error) {
@@ -388,7 +388,7 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
388
388
 
389
389
  const urlRes = await this.makeRequest<{ url: string }>(
390
390
  'GET',
391
- `/api/assets/${encodeURIComponent(fileId)}/url`,
391
+ `/assets/${encodeURIComponent(fileId)}/url`,
392
392
  Object.keys(params).length ? params : undefined,
393
393
  {
394
394
  cache: true,