@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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/CrossDomainAuth.js +3 -1
- package/dist/cjs/HttpService.js +214 -33
- package/dist/cjs/OxyServices.base.js +9 -0
- package/dist/cjs/OxyServices.js +8 -3
- package/dist/cjs/crypto/index.js +3 -1
- package/dist/cjs/crypto/keyManager.js +476 -172
- package/dist/cjs/crypto/polyfill.js +14 -65
- package/dist/cjs/crypto/recoveryPhrase.js +30 -11
- package/dist/cjs/crypto/signatureService.js +25 -60
- package/dist/cjs/i18n/locales/en-US.json +46 -1
- package/dist/cjs/i18n/locales/es-ES.json +46 -1
- package/dist/cjs/i18n/locales/locales/en-US.json +46 -1
- package/dist/cjs/i18n/locales/locales/es-ES.json +46 -1
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/mixins/OxyServices.assets.js +9 -4
- package/dist/cjs/mixins/OxyServices.auth.js +27 -0
- package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
- package/dist/cjs/mixins/OxyServices.features.js +0 -11
- package/dist/cjs/mixins/OxyServices.fedcm.js +4 -3
- package/dist/cjs/mixins/OxyServices.language.js +5 -36
- package/dist/cjs/mixins/OxyServices.redirect.js +6 -2
- package/dist/cjs/mixins/OxyServices.security.js +13 -2
- package/dist/cjs/mixins/OxyServices.user.js +59 -38
- package/dist/cjs/mixins/OxyServices.utility.js +19 -43
- package/dist/cjs/mixins/index.js +11 -3
- package/dist/cjs/utils/accountUtils.js +71 -2
- package/dist/cjs/utils/deviceManager.js +5 -36
- package/dist/cjs/utils/platformCrypto.js +165 -0
- package/dist/cjs/utils/platformCrypto.native.js +123 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/CrossDomainAuth.js +3 -1
- package/dist/esm/HttpService.js +215 -34
- package/dist/esm/OxyServices.base.js +9 -0
- package/dist/esm/OxyServices.js +8 -3
- package/dist/esm/crypto/index.js +1 -1
- package/dist/esm/crypto/keyManager.js +473 -138
- package/dist/esm/crypto/polyfill.js +14 -32
- package/dist/esm/crypto/recoveryPhrase.js +30 -11
- package/dist/esm/crypto/signatureService.js +25 -27
- package/dist/esm/i18n/locales/en-US.json +46 -1
- package/dist/esm/i18n/locales/es-ES.json +46 -1
- package/dist/esm/i18n/locales/locales/en-US.json +46 -1
- package/dist/esm/i18n/locales/locales/es-ES.json +46 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/mixins/OxyServices.assets.js +9 -4
- package/dist/esm/mixins/OxyServices.auth.js +27 -0
- package/dist/esm/mixins/OxyServices.contacts.js +47 -0
- package/dist/esm/mixins/OxyServices.features.js +0 -11
- package/dist/esm/mixins/OxyServices.fedcm.js +4 -3
- package/dist/esm/mixins/OxyServices.language.js +5 -3
- package/dist/esm/mixins/OxyServices.redirect.js +6 -2
- package/dist/esm/mixins/OxyServices.security.js +13 -2
- package/dist/esm/mixins/OxyServices.user.js +59 -38
- package/dist/esm/mixins/OxyServices.utility.js +19 -10
- package/dist/esm/mixins/index.js +11 -3
- package/dist/esm/utils/accountUtils.js +67 -1
- package/dist/esm/utils/deviceManager.js +5 -3
- package/dist/esm/utils/platformCrypto.js +125 -0
- package/dist/esm/utils/platformCrypto.native.js +80 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +47 -3
- package/dist/types/OxyServices.base.d.ts +7 -0
- package/dist/types/OxyServices.d.ts +36 -3
- package/dist/types/crypto/index.d.ts +1 -1
- package/dist/types/crypto/keyManager.d.ts +110 -9
- package/dist/types/crypto/polyfill.d.ts +3 -1
- package/dist/types/crypto/recoveryPhrase.d.ts +31 -7
- package/dist/types/crypto/signatureService.d.ts +4 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +6 -10
- package/dist/types/mixins/OxyServices.auth.d.ts +16 -0
- package/dist/types/mixins/OxyServices.contacts.d.ts +99 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
- package/dist/types/mixins/OxyServices.features.d.ts +2 -7
- package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -0
- package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
- package/dist/types/mixins/OxyServices.language.d.ts +1 -0
- package/dist/types/mixins/OxyServices.location.d.ts +1 -0
- package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +1 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
- package/dist/types/mixins/OxyServices.security.d.ts +1 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.user.d.ts +28 -11
- package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
- package/dist/types/mixins/index.d.ts +52 -4
- package/dist/types/models/interfaces.d.ts +62 -3
- package/dist/types/utils/accountUtils.d.ts +41 -1
- package/dist/types/utils/platformCrypto.d.ts +87 -0
- package/dist/types/utils/platformCrypto.native.d.ts +54 -0
- package/package.json +28 -1
- package/src/CrossDomainAuth.ts +12 -10
- package/src/HttpService.ts +251 -40
- package/src/OxyServices.base.ts +10 -0
- package/src/OxyServices.ts +9 -4
- package/src/crypto/__tests__/keyManager.test.ts +336 -0
- package/src/crypto/index.ts +6 -1
- package/src/crypto/keyManager.ts +529 -151
- package/src/crypto/polyfill.ts +14 -34
- package/src/crypto/recoveryPhrase.ts +56 -17
- package/src/crypto/signatureService.ts +25 -30
- package/src/i18n/locales/en-US.json +46 -1
- package/src/i18n/locales/es-ES.json +46 -1
- package/src/index.ts +16 -3
- package/src/mixins/OxyServices.assets.ts +15 -11
- package/src/mixins/OxyServices.auth.ts +28 -0
- package/src/mixins/OxyServices.contacts.ts +73 -0
- package/src/mixins/OxyServices.features.ts +2 -12
- package/src/mixins/OxyServices.fedcm.ts +4 -3
- package/src/mixins/OxyServices.language.ts +6 -4
- package/src/mixins/OxyServices.redirect.ts +6 -2
- package/src/mixins/OxyServices.security.ts +18 -8
- package/src/mixins/OxyServices.user.ts +72 -49
- package/src/mixins/OxyServices.utility.ts +19 -10
- package/src/mixins/index.ts +58 -7
- package/src/models/interfaces.ts +65 -3
- package/src/utils/accountUtils.ts +82 -2
- package/src/utils/deviceManager.ts +7 -4
- package/src/utils/platformCrypto.native.ts +101 -0
- 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
|
-
|
|
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
|
package/dist/esm/HttpService.js
CHANGED
|
@@ -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
|
|
106
|
-
*
|
|
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
|
-
//
|
|
113
|
-
|
|
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:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
|
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,
|
|
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
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
*/
|
package/dist/esm/OxyServices.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
/**
|
package/dist/esm/crypto/index.js
CHANGED
|
@@ -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
|