@oxyhq/core 1.11.12 → 1.11.13

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 (125) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/CrossDomainAuth.js +3 -1
  3. package/dist/cjs/HttpService.js +214 -33
  4. package/dist/cjs/OxyServices.base.js +9 -0
  5. package/dist/cjs/OxyServices.js +8 -3
  6. package/dist/cjs/crypto/index.js +3 -1
  7. package/dist/cjs/crypto/keyManager.js +476 -172
  8. package/dist/cjs/crypto/polyfill.js +14 -65
  9. package/dist/cjs/crypto/recoveryPhrase.js +30 -11
  10. package/dist/cjs/crypto/signatureService.js +25 -60
  11. package/dist/cjs/i18n/locales/en-US.json +46 -1
  12. package/dist/cjs/i18n/locales/es-ES.json +46 -1
  13. package/dist/cjs/i18n/locales/locales/en-US.json +46 -1
  14. package/dist/cjs/i18n/locales/locales/es-ES.json +46 -1
  15. package/dist/cjs/index.js +7 -2
  16. package/dist/cjs/mixins/OxyServices.assets.js +9 -4
  17. package/dist/cjs/mixins/OxyServices.auth.js +27 -0
  18. package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
  19. package/dist/cjs/mixins/OxyServices.features.js +0 -11
  20. package/dist/cjs/mixins/OxyServices.fedcm.js +4 -3
  21. package/dist/cjs/mixins/OxyServices.language.js +5 -36
  22. package/dist/cjs/mixins/OxyServices.redirect.js +6 -2
  23. package/dist/cjs/mixins/OxyServices.security.js +13 -2
  24. package/dist/cjs/mixins/OxyServices.user.js +59 -38
  25. package/dist/cjs/mixins/OxyServices.utility.js +19 -43
  26. package/dist/cjs/mixins/index.js +11 -3
  27. package/dist/cjs/utils/accountUtils.js +71 -2
  28. package/dist/cjs/utils/deviceManager.js +5 -36
  29. package/dist/cjs/utils/platformCrypto.js +165 -0
  30. package/dist/cjs/utils/platformCrypto.native.js +123 -0
  31. package/dist/esm/.tsbuildinfo +1 -1
  32. package/dist/esm/CrossDomainAuth.js +3 -1
  33. package/dist/esm/HttpService.js +215 -34
  34. package/dist/esm/OxyServices.base.js +9 -0
  35. package/dist/esm/OxyServices.js +8 -3
  36. package/dist/esm/crypto/index.js +1 -1
  37. package/dist/esm/crypto/keyManager.js +473 -138
  38. package/dist/esm/crypto/polyfill.js +14 -32
  39. package/dist/esm/crypto/recoveryPhrase.js +30 -11
  40. package/dist/esm/crypto/signatureService.js +25 -27
  41. package/dist/esm/i18n/locales/en-US.json +46 -1
  42. package/dist/esm/i18n/locales/es-ES.json +46 -1
  43. package/dist/esm/i18n/locales/locales/en-US.json +46 -1
  44. package/dist/esm/i18n/locales/locales/es-ES.json +46 -1
  45. package/dist/esm/index.js +2 -2
  46. package/dist/esm/mixins/OxyServices.assets.js +9 -4
  47. package/dist/esm/mixins/OxyServices.auth.js +27 -0
  48. package/dist/esm/mixins/OxyServices.contacts.js +47 -0
  49. package/dist/esm/mixins/OxyServices.features.js +0 -11
  50. package/dist/esm/mixins/OxyServices.fedcm.js +4 -3
  51. package/dist/esm/mixins/OxyServices.language.js +5 -3
  52. package/dist/esm/mixins/OxyServices.redirect.js +6 -2
  53. package/dist/esm/mixins/OxyServices.security.js +13 -2
  54. package/dist/esm/mixins/OxyServices.user.js +59 -38
  55. package/dist/esm/mixins/OxyServices.utility.js +19 -10
  56. package/dist/esm/mixins/index.js +11 -3
  57. package/dist/esm/utils/accountUtils.js +67 -1
  58. package/dist/esm/utils/deviceManager.js +5 -3
  59. package/dist/esm/utils/platformCrypto.js +125 -0
  60. package/dist/esm/utils/platformCrypto.native.js +80 -0
  61. package/dist/types/.tsbuildinfo +1 -1
  62. package/dist/types/HttpService.d.ts +47 -3
  63. package/dist/types/OxyServices.base.d.ts +7 -0
  64. package/dist/types/OxyServices.d.ts +36 -3
  65. package/dist/types/crypto/index.d.ts +1 -1
  66. package/dist/types/crypto/keyManager.d.ts +110 -9
  67. package/dist/types/crypto/polyfill.d.ts +3 -1
  68. package/dist/types/crypto/recoveryPhrase.d.ts +31 -7
  69. package/dist/types/crypto/signatureService.d.ts +4 -0
  70. package/dist/types/index.d.ts +4 -3
  71. package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
  72. package/dist/types/mixins/OxyServices.assets.d.ts +6 -10
  73. package/dist/types/mixins/OxyServices.auth.d.ts +16 -0
  74. package/dist/types/mixins/OxyServices.contacts.d.ts +99 -0
  75. package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
  76. package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
  77. package/dist/types/mixins/OxyServices.features.d.ts +2 -7
  78. package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -0
  79. package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
  80. package/dist/types/mixins/OxyServices.language.d.ts +1 -0
  81. package/dist/types/mixins/OxyServices.location.d.ts +1 -0
  82. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
  83. package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
  84. package/dist/types/mixins/OxyServices.popup.d.ts +1 -0
  85. package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
  86. package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
  87. package/dist/types/mixins/OxyServices.security.d.ts +1 -0
  88. package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
  89. package/dist/types/mixins/OxyServices.user.d.ts +28 -11
  90. package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
  91. package/dist/types/mixins/index.d.ts +52 -4
  92. package/dist/types/models/interfaces.d.ts +62 -3
  93. package/dist/types/utils/accountUtils.d.ts +41 -1
  94. package/dist/types/utils/platformCrypto.d.ts +87 -0
  95. package/dist/types/utils/platformCrypto.native.d.ts +54 -0
  96. package/package.json +28 -1
  97. package/src/CrossDomainAuth.ts +12 -10
  98. package/src/HttpService.ts +251 -40
  99. package/src/OxyServices.base.ts +10 -0
  100. package/src/OxyServices.ts +9 -4
  101. package/src/crypto/__tests__/keyManager.test.ts +336 -0
  102. package/src/crypto/index.ts +6 -1
  103. package/src/crypto/keyManager.ts +529 -151
  104. package/src/crypto/polyfill.ts +14 -34
  105. package/src/crypto/recoveryPhrase.ts +56 -17
  106. package/src/crypto/signatureService.ts +25 -30
  107. package/src/i18n/locales/en-US.json +46 -1
  108. package/src/i18n/locales/es-ES.json +46 -1
  109. package/src/index.ts +16 -3
  110. package/src/mixins/OxyServices.assets.ts +15 -11
  111. package/src/mixins/OxyServices.auth.ts +28 -0
  112. package/src/mixins/OxyServices.contacts.ts +73 -0
  113. package/src/mixins/OxyServices.features.ts +2 -12
  114. package/src/mixins/OxyServices.fedcm.ts +4 -3
  115. package/src/mixins/OxyServices.language.ts +6 -4
  116. package/src/mixins/OxyServices.redirect.ts +6 -2
  117. package/src/mixins/OxyServices.security.ts +18 -8
  118. package/src/mixins/OxyServices.user.ts +72 -49
  119. package/src/mixins/OxyServices.utility.ts +19 -10
  120. package/src/mixins/index.ts +58 -7
  121. package/src/models/interfaces.ts +65 -3
  122. package/src/utils/accountUtils.ts +82 -2
  123. package/src/utils/deviceManager.ts +7 -4
  124. package/src/utils/platformCrypto.native.ts +101 -0
  125. package/src/utils/platformCrypto.ts +145 -0
@@ -164,7 +164,9 @@ export class CrossDomainAuth {
164
164
  * Check if FedCM is supported in current browser
165
165
  */
166
166
  isFedCMSupported() {
167
- return this.oxyServices.constructor.isFedCMSupported?.() || false;
167
+ // FedCM support is exposed both as a static and an instance method on
168
+ // OxyServices; the instance method is reliable across mixin composition.
169
+ return this.oxyServices.isFedCMSupported?.() || false;
168
170
  }
169
171
  /**
170
172
  * Get recommended authentication method for current environment
@@ -17,12 +17,38 @@ import { RequestDeduplicator, RequestQueue, SimpleLogger } from './utils/request
17
17
  import { retryAsync } from './utils/asyncUtils.js';
18
18
  import { handleHttpError } from './utils/errorUtils.js';
19
19
  import { jwtDecode } from 'jwt-decode';
20
- import { isNative, getPlatformOS } from './utils/platform.js';
20
+ import { isNative, isReactNative, getPlatformOS } from './utils/platform.js';
21
21
  /**
22
22
  * Check if we're running in a native app environment (React Native, not web)
23
23
  * This is used to determine CSRF handling mode
24
24
  */
25
25
  const isNativeApp = isNative();
26
+ /**
27
+ * FNV-1a 32-bit non-cryptographic hash.
28
+ *
29
+ * Used by the cache-key generator for large payloads where full JSON
30
+ * inclusion would balloon the cache map keys. Content-addressed: every
31
+ * byte of the input contributes to the digest, so two payloads with the
32
+ * same top-level shape but different field values produce different keys
33
+ * (the previous `keys + length` heuristic collided on these).
34
+ *
35
+ * Trade-offs:
36
+ * - 32 bits is ample for an in-process cache (collision risk negligible
37
+ * at our key counts; we also prefix with method + url which further
38
+ * partitions the keyspace).
39
+ * - Not cryptographically secure — never use for security decisions.
40
+ * - Zero dependencies, branch-free hot loop, ~1 GiB/s on V8.
41
+ */
42
+ function fnv1a32(str) {
43
+ let h = 0x811c9dc5;
44
+ for (let i = 0; i < str.length; i++) {
45
+ h ^= str.charCodeAt(i);
46
+ // h * 16777619 mod 2^32, written as shift-and-add for portability and
47
+ // to avoid 53-bit JS number truncation in the intermediate multiply.
48
+ h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
49
+ }
50
+ return h.toString(16).padStart(8, '0');
51
+ }
26
52
  /**
27
53
  * Token store for authentication (instance-based)
28
54
  * Each HttpService gets its own TokenStore to prevent conflicts
@@ -102,31 +128,53 @@ export class HttpService {
102
128
  this.requestQueue = new RequestQueue(config.maxConcurrentRequests || 10, config.requestQueueSize || 100);
103
129
  }
104
130
  /**
105
- * Robust FormData detection that works in browser and Node.js environments
106
- * Checks multiple conditions to handle different FormData implementations
131
+ * Robust FormData detection that works in browser, React Native, and
132
+ * Node.js polyfill environments.
133
+ *
134
+ * Why we don't use `instanceof FormData` alone:
135
+ * - React Native's FormData is a separate class, not the browser one —
136
+ * `instanceof FormData` is true only inside the JS runtime that
137
+ * instantiated the value (browser-side polyfills also have their own).
138
+ * - The Node.js `form-data` polyfill ships its own constructor.
139
+ *
140
+ * Why we explicitly reject `URLSearchParams`:
141
+ * - `URLSearchParams` ALSO exposes `append` / `get` / `has`, so the
142
+ * duck-type fallback below would have misidentified it as FormData.
143
+ * - We want urlencoded payloads to take the JSON-stringify path so the
144
+ * server receives them as `application/x-www-form-urlencoded` instead
145
+ * of an empty multipart body.
107
146
  */
108
147
  isFormData(data) {
109
- if (!data) {
148
+ if (!data || typeof data !== 'object') {
110
149
  return false;
111
150
  }
112
- // Primary check: instanceof FormData (works in browser and Node.js with proper polyfills)
113
- if (data instanceof FormData) {
151
+ // Reject URLSearchParams up front: it shares the duck-typed surface
152
+ // (append / get / has) but is a fundamentally different content type.
153
+ // The caller routes URLSearchParams through the regular body path.
154
+ if (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) {
155
+ return false;
156
+ }
157
+ // Primary check: instanceof FormData. Works whenever the value was
158
+ // constructed by the same runtime/realm that exposes `FormData`.
159
+ if (typeof FormData !== 'undefined' && data instanceof FormData) {
114
160
  return true;
115
161
  }
116
- // Fallback: Check constructor name (handles Node.js polyfills like form-data)
117
- if (typeof data === 'object' && data !== null) {
118
- const constructorName = data.constructor?.name;
119
- if (constructorName === 'FormData' || constructorName === 'FormDataImpl') {
120
- return true;
121
- }
122
- // Additional check: Look for FormData-like methods
123
- if (typeof data.append === 'function' &&
124
- typeof data.get === 'function' &&
125
- typeof data.has === 'function') {
126
- return true;
127
- }
162
+ // Fallback: detect Node / RN polyfills by constructor name. Limited to
163
+ // the small handful of known names so we don't accept arbitrary
164
+ // user-supplied objects with a coincidental `name`.
165
+ const constructorName = data.constructor?.name;
166
+ if (constructorName === 'FormData' || constructorName === 'FormDataImpl') {
167
+ return true;
128
168
  }
129
- return false;
169
+ // Last-resort duck typing — require the full FormData write surface
170
+ // (`append`, `get`, `has`, `getAll`, `delete`) so plain objects with
171
+ // an `append` method don't accidentally match.
172
+ const candidate = data;
173
+ return (typeof candidate.append === 'function' &&
174
+ typeof candidate.get === 'function' &&
175
+ typeof candidate.has === 'function' &&
176
+ typeof candidate.getAll === 'function' &&
177
+ typeof candidate.delete === 'function');
130
178
  }
131
179
  /**
132
180
  * Main request method - handles everything in one place
@@ -220,13 +268,22 @@ export class HttpService {
220
268
  const bodyValue = method !== 'GET' && data
221
269
  ? (isFormData ? data : JSON.stringify(data))
222
270
  : undefined;
223
- const response = await fetch(fullUrl, {
224
- method,
225
- headers,
226
- body: bodyValue,
227
- signal: controller.signal,
228
- credentials: 'include', // Include cookies for cross-origin requests (CSRF, session)
229
- });
271
+ // React Native FormData workaround:
272
+ // Expo SDK 56's "winter fetch" rejects RN file descriptors `{uri, type, name}`
273
+ // in FormDataPart conversion (`Unsupported FormDataPart implementation`).
274
+ // RN's native XMLHttpRequest handles those descriptors correctly, so we
275
+ // route multipart uploads through XHR on RN only. JSON, text, etc. still
276
+ // use fetch on every platform.
277
+ const useXhrForUpload = isFormData && isReactNative() && typeof XMLHttpRequest !== 'undefined';
278
+ const response = useXhrForUpload
279
+ ? await this.uploadViaXHR(fullUrl, method, headers, bodyValue, controller.signal, timeout)
280
+ : await fetch(fullUrl, {
281
+ method,
282
+ headers,
283
+ body: bodyValue,
284
+ signal: controller.signal,
285
+ credentials: 'include', // Include cookies for cross-origin requests (CSRF, session)
286
+ });
230
287
  if (timeoutId)
231
288
  clearTimeout(timeoutId);
232
289
  // Handle response
@@ -366,25 +423,131 @@ export class HttpService {
366
423
  }
367
424
  return result;
368
425
  }
426
+ /**
427
+ * Upload via XMLHttpRequest (React Native FormData workaround).
428
+ *
429
+ * Expo SDK 56's "winter fetch" cannot serialize RN file descriptors
430
+ * (`{uri, type, name}`) — `convertFormDataAsync` rejects them as
431
+ * `Unsupported FormDataPart implementation`. RN's native XHR streams
432
+ * the file from disk correctly, so multipart uploads go through XHR
433
+ * on RN only.
434
+ *
435
+ * Returns a standard `Response` so downstream parsing in `request()`
436
+ * (status checks, 401/403 retries, JSON/blob/text parsing) is identical
437
+ * to the fetch path.
438
+ */
439
+ uploadViaXHR(url, method, headers, body, abortSignal, timeout) {
440
+ return new Promise((resolve, reject) => {
441
+ const xhr = new XMLHttpRequest();
442
+ xhr.open(method, url, true);
443
+ // withCredentials mirrors fetch's `credentials: 'include'` so the
444
+ // session cookie and CSRF cookie continue to flow.
445
+ xhr.withCredentials = true;
446
+ // Forward headers but skip Content-Type — XHR sets the multipart
447
+ // boundary automatically and overriding it breaks the upload.
448
+ for (const [key, value] of Object.entries(headers)) {
449
+ if (key.toLowerCase() === 'content-type')
450
+ continue;
451
+ try {
452
+ xhr.setRequestHeader(key, value);
453
+ }
454
+ catch (headerError) {
455
+ // Some headers (e.g. forbidden header names) cannot be set —
456
+ // log and continue rather than failing the whole upload.
457
+ this.logger.warn('XHR setRequestHeader failed:', key, headerError);
458
+ }
459
+ }
460
+ xhr.responseType = 'text';
461
+ if (timeout > 0) {
462
+ xhr.timeout = timeout;
463
+ }
464
+ const onAbort = () => {
465
+ try {
466
+ xhr.abort();
467
+ }
468
+ catch { /* xhr already finished */ }
469
+ };
470
+ if (abortSignal.aborted) {
471
+ reject(new DOMException('The user aborted a request.', 'AbortError'));
472
+ return;
473
+ }
474
+ abortSignal.addEventListener('abort', onAbort);
475
+ const cleanup = () => {
476
+ abortSignal.removeEventListener('abort', onAbort);
477
+ };
478
+ xhr.onload = () => {
479
+ cleanup();
480
+ const responseHeaders = HttpService.parseXHRHeaders(xhr.getAllResponseHeaders());
481
+ resolve(new Response(xhr.responseText, {
482
+ status: xhr.status,
483
+ statusText: xhr.statusText,
484
+ headers: responseHeaders,
485
+ }));
486
+ };
487
+ xhr.onerror = () => {
488
+ cleanup();
489
+ reject(new TypeError('Network request failed'));
490
+ };
491
+ xhr.ontimeout = () => {
492
+ cleanup();
493
+ reject(new DOMException('The request timed out.', 'TimeoutError'));
494
+ };
495
+ xhr.onabort = () => {
496
+ cleanup();
497
+ reject(new DOMException('The user aborted a request.', 'AbortError'));
498
+ };
499
+ xhr.send(body);
500
+ });
501
+ }
502
+ /**
503
+ * Parse raw header string from `XMLHttpRequest.getAllResponseHeaders()`
504
+ * into a `Headers`-compatible object.
505
+ */
506
+ static parseXHRHeaders(rawHeaders) {
507
+ const headers = new Headers();
508
+ if (!rawHeaders)
509
+ return headers;
510
+ // RFC 7230 line terminator is CRLF; some XHR implementations use LF only.
511
+ const lines = rawHeaders.trim().split(/\r?\n/);
512
+ for (const line of lines) {
513
+ const colonIndex = line.indexOf(':');
514
+ if (colonIndex <= 0)
515
+ continue;
516
+ const key = line.slice(0, colonIndex).trim();
517
+ const value = line.slice(colonIndex + 1).trim();
518
+ if (key) {
519
+ try {
520
+ headers.append(key, value);
521
+ }
522
+ catch {
523
+ // Invalid header name/value — skip.
524
+ }
525
+ }
526
+ }
527
+ return headers;
528
+ }
369
529
  /**
370
530
  * Generate cache key efficiently
371
- * Uses simple hash for large objects to avoid expensive JSON.stringify
531
+ * Uses a content-addressed hash for large payloads so two requests with
532
+ * the same shape but different values never collide on the same key
533
+ * (which would silently serve stale data — e.g. paginated search results,
534
+ * large object updates).
372
535
  */
373
536
  generateCacheKey(method, url, data) {
374
537
  if (!data || (typeof data === 'object' && Object.keys(data).length === 0)) {
375
538
  return `${method}:${url}`;
376
539
  }
377
- // For small objects, use JSON.stringify
540
+ // For small objects, the full serialization IS the key — fastest and
541
+ // guaranteed to be content-addressed.
378
542
  const dataStr = JSON.stringify(data);
379
543
  if (dataStr.length < 1000) {
380
544
  return `${method}:${url}:${dataStr}`;
381
545
  }
382
- // For large objects, use a simple hash based on keys and values length
383
- // This avoids expensive serialization while still being unique enough
384
- const hash = typeof data === 'object' && data !== null
385
- ? Object.keys(data).sort().join(',') + ':' + dataStr.length
386
- : String(data).substring(0, 100);
387
- return `${method}:${url}:${hash}`;
546
+ // For large payloads, hash the full serialized string so the key remains
547
+ // content-addressed (any byte change yields a different hash). Previous
548
+ // implementation hashed `keys + length` which collided for any two
549
+ // payloads with the same top-level keys and serialized length.
550
+ return `${method}:${url}:${fnv1a32(dataStr)}`;
388
551
  }
389
552
  /**
390
553
  * Build full URL with query params
@@ -614,6 +777,24 @@ export class HttpService {
614
777
  clearCacheEntry(key) {
615
778
  this.cache.delete(key);
616
779
  }
780
+ /**
781
+ * Delete every cache entry whose key starts with `prefix`.
782
+ *
783
+ * Used by mutations that don't know the exact downstream cache keys —
784
+ * e.g. `updateProfile` invalidating all `GET:/session/user/*` entries
785
+ * without having to track every active session ID. Returns the number of
786
+ * deleted entries (for observability in tests).
787
+ */
788
+ clearCacheByPrefix(prefix) {
789
+ let removed = 0;
790
+ for (const key of this.cache.keys()) {
791
+ if (key.startsWith(prefix)) {
792
+ this.cache.delete(key);
793
+ removed++;
794
+ }
795
+ }
796
+ return removed;
797
+ }
617
798
  getCacheStats() {
618
799
  const cacheStats = this.cache.getStats();
619
800
  const total = this.requestMetrics.cacheHits + this.requestMetrics.cacheMisses;
@@ -75,6 +75,15 @@ export class OxyServicesBase {
75
75
  clearCacheEntry(key) {
76
76
  this.httpService.clearCacheEntry(key);
77
77
  }
78
+ /**
79
+ * Clear every cache entry whose key starts with `prefix`.
80
+ * Useful for mutations that invalidate a family of GET responses
81
+ * without enumerating each one (e.g. all session-user lookups after
82
+ * a profile update).
83
+ */
84
+ clearCacheByPrefix(prefix) {
85
+ return this.httpService.clearCacheByPrefix(prefix);
86
+ }
78
87
  /**
79
88
  * Get cache statistics
80
89
  */
@@ -34,9 +34,14 @@ import { composeOxyServices } from './mixins/index.js';
34
34
  * });
35
35
  * ```
36
36
  */
37
- // Compose all mixins into the final OxyServices class
37
+ // Compose all mixins into the final OxyServices class. The composed runtime
38
+ // class augments OxyServicesBase with every mixin's methods (see mixins/index.ts).
39
+ // Statically, TypeScript sees this as a constructor producing OxyServicesBase;
40
+ // the additional methods are exposed via interface merging on `OxyServices` below.
38
41
  const OxyServicesComposed = composeOxyServices();
39
- // Export as a named class to avoid TypeScript issues with anonymous class types
42
+ // Export as a named class to avoid TypeScript issues with anonymous class types.
43
+ // We extend the composed constructor directly — its public surface is broadened
44
+ // to the full mixin set via the interface declaration that follows.
40
45
  export class OxyServices extends OxyServicesComposed {
41
46
  constructor(config) {
42
47
  super(config);
@@ -45,7 +50,7 @@ export class OxyServices extends OxyServicesComposed {
45
50
  // Re-export error classes for convenience
46
51
  export { OxyAuthenticationError, OxyAuthenticationTimeoutError };
47
52
  /**
48
- * Export the default Oxy Cloud URL (for backward compatibility)
53
+ * Default Oxy Cloud URL used when no `cloudURL` is provided to OxyServices.
49
54
  */
50
55
  export const OXY_CLOUD_URL = 'https://cloud.oxy.so';
51
56
  /**
@@ -6,7 +6,7 @@
6
6
  */
7
7
  // Import polyfills first - this ensures Buffer is available for bip39 and other libraries
8
8
  import './polyfill.js';
9
- export { KeyManager } from './keyManager.js';
9
+ export { KeyManager, IdentityAlreadyExistsError, IdentityPersistError, } from './keyManager.js';
10
10
  export { SignatureService } from './signatureService.js';
11
11
  export { RecoveryPhraseService } from './recoveryPhrase.js';
12
12
  // Re-export for convenience