@oxyhq/core 1.11.11 → 1.11.12

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.
@@ -19,7 +19,6 @@ const cache_1 = require("./utils/cache");
19
19
  const requestUtils_1 = require("./utils/requestUtils");
20
20
  const asyncUtils_1 = require("./utils/asyncUtils");
21
21
  const errorUtils_1 = require("./utils/errorUtils");
22
- const debugUtils_1 = require("./shared/utils/debugUtils");
23
22
  const jwt_decode_1 = require("jwt-decode");
24
23
  const platform_1 = require("./utils/platform");
25
24
  /**
@@ -190,9 +189,12 @@ class HttpService {
190
189
  if (isNativeApp && isStateChangingMethod) {
191
190
  headers['X-Native-App'] = 'true';
192
191
  }
193
- // Debug logging for CSRF issues
194
- if (isStateChangingMethod && (0, debugUtils_1.isDev)()) {
195
- console.log('[HttpService] CSRF Debug:', {
192
+ // Debug logging for CSRF issues — routed through the SimpleLogger so
193
+ // it only fires when consumers opt in via `enableLogging`. Previously
194
+ // this was a bare console.log that leaked noise into every host app's
195
+ // stdout in development.
196
+ if (isStateChangingMethod) {
197
+ this.logger.debug('CSRF Debug:', {
196
198
  url,
197
199
  method,
198
200
  isNativeApp,
@@ -412,23 +414,20 @@ class HttpService {
412
414
  // Return cached token if available
413
415
  const cachedToken = this.tokenStore.getCsrfToken();
414
416
  if (cachedToken) {
415
- if ((0, debugUtils_1.isDev)())
416
- console.log('[HttpService] Using cached CSRF token');
417
+ this.logger.debug('Using cached CSRF token');
417
418
  return cachedToken;
418
419
  }
419
420
  // Deduplicate concurrent CSRF token fetches
420
421
  const existingPromise = this.tokenStore.getCsrfTokenFetchPromise();
421
422
  if (existingPromise) {
422
- if ((0, debugUtils_1.isDev)())
423
- console.log('[HttpService] Waiting for existing CSRF fetch');
423
+ this.logger.debug('Waiting for existing CSRF fetch');
424
424
  return existingPromise;
425
425
  }
426
426
  const fetchPromise = (async () => {
427
427
  const maxAttempts = 2;
428
428
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
429
429
  try {
430
- if ((0, debugUtils_1.isDev)())
431
- console.log('[HttpService] Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
430
+ this.logger.debug('Fetching CSRF token from:', `${this.baseURL}/csrf-token`, `(attempt ${attempt})`);
432
431
  // Use AbortController for timeout (more compatible than AbortSignal.timeout)
433
432
  const controller = new AbortController();
434
433
  const timeoutId = setTimeout(() => controller.abort(), 5000);
@@ -439,12 +438,10 @@ class HttpService {
439
438
  signal: controller.signal,
440
439
  });
441
440
  clearTimeout(timeoutId);
442
- if ((0, debugUtils_1.isDev)())
443
- console.log('[HttpService] CSRF fetch response:', response.status, response.ok);
441
+ this.logger.debug('CSRF fetch response:', response.status, response.ok);
444
442
  if (response.ok) {
445
443
  const data = await response.json();
446
- if ((0, debugUtils_1.isDev)())
447
- console.log('[HttpService] CSRF response data:', data);
444
+ this.logger.debug('CSRF response data:', data);
448
445
  const token = data.csrfToken || null;
449
446
  this.tokenStore.setCsrfToken(token);
450
447
  this.logger.debug('CSRF token fetched');
@@ -457,13 +454,11 @@ class HttpService {
457
454
  this.logger.debug('CSRF token from header');
458
455
  return headerToken;
459
456
  }
460
- if ((0, debugUtils_1.isDev)())
461
- console.log('[HttpService] CSRF fetch failed with status:', response.status);
457
+ this.logger.debug('CSRF fetch failed with status:', response.status);
462
458
  this.logger.warn('Failed to fetch CSRF token:', response.status);
463
459
  }
464
460
  catch (error) {
465
- if ((0, debugUtils_1.isDev)())
466
- console.log('[HttpService] CSRF fetch error:', error);
461
+ this.logger.debug('CSRF fetch error:', error);
467
462
  this.logger.warn('CSRF token fetch error:', error);
468
463
  }
469
464
  // Wait before retry (500ms)
@@ -43,20 +43,24 @@ exports.SignatureService = void 0;
43
43
  const elliptic_1 = require("elliptic");
44
44
  const keyManager_1 = require("./keyManager");
45
45
  const platform_1 = require("../utils/platform");
46
- // Lazy import for expo-crypto
46
+ // Lazy imports for platform-specific crypto
47
47
  let ExpoCrypto = null;
48
+ let NodeCrypto = null;
48
49
  const ec = new elliptic_1.ec('secp256k1');
49
- /**
50
- * Initialize expo-crypto module
51
- */
52
50
  async function initExpoCrypto() {
53
51
  if (!ExpoCrypto) {
54
- // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
55
52
  const moduleName = 'expo-crypto';
56
53
  ExpoCrypto = await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
57
54
  }
58
55
  return ExpoCrypto;
59
56
  }
57
+ async function initNodeCrypto() {
58
+ if (!NodeCrypto) {
59
+ const moduleName = 'crypto';
60
+ NodeCrypto = await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
61
+ }
62
+ return NodeCrypto;
63
+ }
60
64
  /**
61
65
  * Compute SHA-256 hash of a string
62
66
  */
@@ -66,10 +70,9 @@ async function sha256(message) {
66
70
  const Crypto = await initExpoCrypto();
67
71
  return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, message);
68
72
  }
69
- // In Node.js, use Node's crypto module
70
73
  if ((0, platform_1.isNodeJS)()) {
71
74
  try {
72
- const nodeCrypto = await Promise.resolve().then(() => __importStar(require('crypto')));
75
+ const nodeCrypto = await initNodeCrypto();
73
76
  return nodeCrypto.createHash('sha256').update(message).digest('hex');
74
77
  }
75
78
  catch {
@@ -97,12 +100,9 @@ class SignatureService {
97
100
  .map((b) => b.toString(16).padStart(2, '0'))
98
101
  .join('');
99
102
  }
100
- // In Node.js, use Node's crypto module
101
- // Variable indirection prevents bundlers (Vite, webpack) from statically resolving this
102
103
  if ((0, platform_1.isNodeJS)()) {
103
104
  try {
104
- const cryptoModuleName = 'crypto';
105
- const nodeCrypto = await Promise.resolve(`${cryptoModuleName}`).then(s => __importStar(require(s)));
105
+ const nodeCrypto = await initNodeCrypto();
106
106
  return nodeCrypto.randomBytes(32).toString('hex');
107
107
  }
108
108
  catch {
@@ -21,6 +21,17 @@ function OxyServicesUserMixin(Base) {
21
21
  throw this.handleError(error);
22
22
  }
23
23
  }
24
+ /**
25
+ * Lightweight username lookup for login flows.
26
+ * Returns minimal public info: exists, color, avatar, displayName.
27
+ * Faster than getProfileByUsername — no stats, no formatting.
28
+ */
29
+ async lookupUsername(username) {
30
+ return await this.makeRequest('GET', `/auth/lookup/${encodeURIComponent(username)}`, undefined, {
31
+ cache: true,
32
+ cacheTTL: 60 * 1000, // 1 minute cache
33
+ });
34
+ }
24
35
  /**
25
36
  * Search user profiles
26
37
  */
@@ -44,18 +44,47 @@ async function parallelWithErrorHandling(operations, errorHandler) {
44
44
  const results = await Promise.allSettled(operations.map((op, index) => withErrorHandling(op, error => errorHandler?.(error, index))));
45
45
  return results.map(result => result.status === 'fulfilled' ? result.value : null);
46
46
  }
47
+ /**
48
+ * Extract an HTTP status code from an error value, tolerating both the
49
+ * axios-style nested shape (`error.response.status`) and the flat shape
50
+ * produced by {@link handleHttpError} / fetch-based clients (`error.status`).
51
+ *
52
+ * Centralising this lookup prevents retry predicates from silently falling
53
+ * through when one of the two shapes is missing, which previously caused
54
+ * @oxyhq/core to retry 4xx responses and turn sub-10ms failures into
55
+ * multi-second stalls for every missing-resource lookup.
56
+ */
57
+ function extractHttpStatus(error) {
58
+ if (!error || typeof error !== 'object')
59
+ return undefined;
60
+ const candidate = error;
61
+ const flat = candidate.status;
62
+ if (typeof flat === 'number' && Number.isFinite(flat))
63
+ return flat;
64
+ const nested = candidate.response?.status;
65
+ if (typeof nested === 'number' && Number.isFinite(nested))
66
+ return nested;
67
+ return undefined;
68
+ }
47
69
  /**
48
70
  * Retry an async operation with exponential backoff
49
71
  *
50
- * By default, does not retry on 4xx errors (client errors).
51
- * Use shouldRetry callback to customize retry behavior.
72
+ * By default, does not retry on 4xx errors (client errors). The default
73
+ * predicate accepts both the axios-style `error.response.status` and the
74
+ * flat `error.status` shape produced by {@link handleHttpError}, so callers
75
+ * never accidentally retry a deterministic client failure.
76
+ *
77
+ * Use the `shouldRetry` callback to customize retry behavior.
52
78
  */
53
79
  async function retryAsync(operation, maxRetries = 3, baseDelay = 1000, shouldRetry) {
54
80
  let lastError;
55
- // Default shouldRetry: don't retry on 4xx errors
81
+ // Default shouldRetry: don't retry on 4xx errors (client errors).
82
+ // Checks BOTH `error.status` (flat shape from handleHttpError / fetch
83
+ // clients) AND `error.response.status` (axios-style shape) so neither
84
+ // representation can leak a client error into the retry loop.
56
85
  const defaultShouldRetry = (error) => {
57
- // Don't retry on 4xx errors (client errors)
58
- if (error?.response?.status >= 400 && error?.response?.status < 500) {
86
+ const status = extractHttpStatus(error);
87
+ if (status !== undefined && status >= 400 && status < 500) {
59
88
  return false;
60
89
  }
61
90
  return true;