@oxyhq/core 15.0.1 → 16.0.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/OxyServices.errors.js +33 -1
- package/dist/cjs/OxyServices.js +2 -1
- package/dist/cjs/index.js +6 -5
- package/dist/cjs/mixins/OxyServices.assets.js +31 -8
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +5 -5
- package/dist/cjs/utils/accountUtils.js +6 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/OxyServices.errors.js +31 -0
- package/dist/esm/OxyServices.js +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/mixins/OxyServices.assets.js +32 -9
- package/dist/esm/mixins/OxyServices.deviceBoot.js +5 -5
- package/dist/esm/utils/accountUtils.js +6 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/OxyServices.d.ts +2 -2
- package/dist/types/OxyServices.errors.d.ts +33 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/mixins/OxyServices.assets.d.ts +17 -5
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +5 -5
- package/package.json +2 -2
- package/src/OxyServices.errors.ts +45 -0
- package/src/OxyServices.ts +2 -2
- package/src/index.ts +1 -1
- package/src/mixins/OxyServices.assets.ts +36 -9
- package/src/mixins/OxyServices.deviceBoot.ts +5 -5
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +52 -3
- package/src/utils/__tests__/accountUtils.test.ts +22 -0
- package/src/utils/accountUtils.ts +9 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OxyAuthenticationTimeoutError = exports.AssetUrlResolutionError = exports.OxyAuthenticationError = void 0;
|
|
3
|
+
exports.OxyAuthenticationTimeoutError = exports.ServiceAssetMetadataError = exports.AssetUrlResolutionError = exports.OxyAuthenticationError = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Custom error types for better error handling
|
|
6
6
|
*/
|
|
@@ -54,6 +54,38 @@ class AssetUrlResolutionError extends Error {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
exports.AssetUrlResolutionError = AssetUrlResolutionError;
|
|
57
|
+
/**
|
|
58
|
+
* Thrown when one or more chunks of `getServiceAssetMetadataByIds` could not be
|
|
59
|
+
* resolved.
|
|
60
|
+
*
|
|
61
|
+
* Exists because for this endpoint a FAILED request and an ABSENT asset produce
|
|
62
|
+
* the same observable result. The server legitimately omits unknown/deleted ids,
|
|
63
|
+
* so callers are documented to map the response by `id` and treat a missing
|
|
64
|
+
* entry as "no such asset" — which means a chunk that 429s, times out or 5xxs
|
|
65
|
+
* reads as authoritative absence unless it is raised.
|
|
66
|
+
*
|
|
67
|
+
* That was not hypothetical: a metadata backfill counted every throttled asset
|
|
68
|
+
* as needing no update and exited 0, and the MTN signed-record builder embedded
|
|
69
|
+
* media with no content hash into records that are immutable once signed. Both
|
|
70
|
+
* paths reported success and wrote nothing.
|
|
71
|
+
*
|
|
72
|
+
* `unresolvedIds` carries every id in a failed chunk — not the subset the server
|
|
73
|
+
* would have omitted anyway, which is unknowable when the request never landed.
|
|
74
|
+
* A caller that wants best-effort passes `{ partial: true }` and never sees this.
|
|
75
|
+
*/
|
|
76
|
+
class ServiceAssetMetadataError extends Error {
|
|
77
|
+
constructor(unresolvedIds, statuses, cause) {
|
|
78
|
+
const uniqueStatuses = Array.from(new Set(statuses)).sort((a, b) => a - b);
|
|
79
|
+
const statusSuffix = uniqueStatuses.length > 0 ? ` — status ${uniqueStatuses.join(', ')}` : '';
|
|
80
|
+
super(`Could not resolve asset metadata for ${unresolvedIds.length} id(s)${statusSuffix}. Treat this as unknown, not as absent; pass { partial: true } for best-effort.`);
|
|
81
|
+
this.code = 'SERVICE_ASSET_METADATA_UNRESOLVED';
|
|
82
|
+
this.name = 'ServiceAssetMetadataError';
|
|
83
|
+
this.unresolvedIds = unresolvedIds;
|
|
84
|
+
this.statuses = uniqueStatuses;
|
|
85
|
+
this.cause = cause;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.ServiceAssetMetadataError = ServiceAssetMetadataError;
|
|
57
89
|
class OxyAuthenticationTimeoutError extends OxyAuthenticationError {
|
|
58
90
|
constructor(operationName, timeoutMs) {
|
|
59
91
|
super(`Authentication timeout (${timeoutMs}ms): ${operationName} requires user authentication. Please ensure the user is logged in before calling this method.`, 'AUTH_TIMEOUT', 408);
|
package/dist/cjs/OxyServices.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.oxyClient = exports.OXY_API_URL = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
|
|
3
|
+
exports.oxyClient = exports.OXY_API_URL = exports.OXY_CLOUD_URL = exports.ServiceAssetMetadataError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
|
|
4
4
|
const OxyServices_errors_1 = require("./OxyServices.errors");
|
|
5
5
|
Object.defineProperty(exports, "AssetUrlResolutionError", { enumerable: true, get: function () { return OxyServices_errors_1.AssetUrlResolutionError; } });
|
|
6
6
|
Object.defineProperty(exports, "OxyAuthenticationError", { enumerable: true, get: function () { return OxyServices_errors_1.OxyAuthenticationError; } });
|
|
7
7
|
Object.defineProperty(exports, "OxyAuthenticationTimeoutError", { enumerable: true, get: function () { return OxyServices_errors_1.OxyAuthenticationTimeoutError; } });
|
|
8
|
+
Object.defineProperty(exports, "ServiceAssetMetadataError", { enumerable: true, get: function () { return OxyServices_errors_1.ServiceAssetMetadataError; } });
|
|
8
9
|
// Import mixin composition helper
|
|
9
10
|
const mixins_1 = require("./mixins");
|
|
10
11
|
/**
|
package/dist/cjs/index.js
CHANGED
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
* If a symbol does not appear here, it is NOT part of the public API.
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.packageInfo = exports.runSessionColdBoot = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = void 0;
|
|
21
|
+
exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = exports.AEAD_KEY_LENGTH = exports.decryptAead = exports.encryptAead = exports.hkdfSha256 = exports.RecoveryPhraseService = exports.SignatureService = exports.updateIdentityMarker = exports.readIdentityMarker = exports.IdentityUnavailableError = exports.IdentityPersistError = exports.IdentityAlreadyExistsError = exports.KeyManager = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.verifyPublicCardAttestation = exports.parseAttestPayload = exports.parseIdPayload = exports.buildUserDid = exports.ORGANIZATION_CATEGORIES = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.OxyAppDataIdentifierError = exports.commonsDeliveryPlatform = exports.pushTargetsFromDelivery = exports.selectCommonsDelivery = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.ServiceCredentialMismatchError = exports.oxyClient = exports.OXY_CLOUD_URL = exports.ServiceAssetMetadataError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
|
|
22
|
+
exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = exports.isNetworkError = exports.isServerError = exports.isRateLimitError = exports.isNotFoundError = exports.isForbiddenError = exports.isUnauthorizedError = exports.isAlreadyRegisteredError = exports.getErrorMessage = exports.getErrorStatus = exports.HttpStatus = exports.getSystemColorScheme = exports.systemPrefersDarkMode = exports.getOppositeTheme = exports.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isWebBrowser = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = exports.getBaseLanguage = exports.FALLBACK_LOCALE = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = void 0;
|
|
23
|
+
exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = exports.normalizeMultilineText = exports.normalizeInlineText = exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = exports.isValidURL = exports.isValidUUID = exports.isValidObject = exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.isValidDisplayName = exports.isValidPassword = exports.isValidUsername = exports.isValidEmail = exports.PASSWORD_REGEX = exports.USERNAME_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = exports.buildUrl = exports.buildSearchParams = exports.buildQueryParams = exports.translate = exports.withRetry = void 0;
|
|
24
|
+
exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.switchableAccountIds = exports.projectSwitchableAccounts = exports.accountIdsOf = exports.activeUserOf = exports.activeSessionIdOf = exports.deviceStateToClientSessions = exports.createSessionClient = exports.createSessionClientHost = exports.SessionClient = exports.isAllowedDeviceJoinOrigin = exports.isOfficialWebOrigin = exports.isLoopbackOrigin = exports.consumeOAuthReturnPath = exports.persistOAuthReturnPath = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.canonicalizeOAuthRedirectUri = exports.normalizeOAuthRedirectUri = exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.runColdBoot = exports.isOxyRpOrigin = exports.CENTRAL_IDP_APEX = exports.registrableApex = exports.getAccountColor = exports.formatPublicKeyHandle = void 0;
|
|
25
|
+
exports.packageInfo = exports.runSessionColdBoot = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = void 0;
|
|
26
26
|
// Ensure crypto polyfills are loaded before anything else
|
|
27
27
|
require("./crypto/polyfill");
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "OxyServices", { enumerable: true, get: function
|
|
|
33
33
|
Object.defineProperty(exports, "AssetUrlResolutionError", { enumerable: true, get: function () { return OxyServices_1.AssetUrlResolutionError; } });
|
|
34
34
|
Object.defineProperty(exports, "OxyAuthenticationError", { enumerable: true, get: function () { return OxyServices_1.OxyAuthenticationError; } });
|
|
35
35
|
Object.defineProperty(exports, "OxyAuthenticationTimeoutError", { enumerable: true, get: function () { return OxyServices_1.OxyAuthenticationTimeoutError; } });
|
|
36
|
+
Object.defineProperty(exports, "ServiceAssetMetadataError", { enumerable: true, get: function () { return OxyServices_1.ServiceAssetMetadataError; } });
|
|
36
37
|
var OxyServices_2 = require("./OxyServices");
|
|
37
38
|
Object.defineProperty(exports, "OXY_CLOUD_URL", { enumerable: true, get: function () { return OxyServices_2.OXY_CLOUD_URL; } });
|
|
38
39
|
Object.defineProperty(exports, "oxyClient", { enumerable: true, get: function () { return OxyServices_2.oxyClient; } });
|
|
@@ -303,15 +303,25 @@ function OxyServicesAssetsMixin(Base) {
|
|
|
303
303
|
* throws because no credentials are available. A plain user-session request
|
|
304
304
|
* is rejected by the route's service-auth guard.
|
|
305
305
|
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
306
|
+
* FAILURE IS NOT ABSENCE. The server legitimately omits unknown/deleted ids,
|
|
307
|
+
* so a short result is normal — which means a chunk that FAILED (a 429, a
|
|
308
|
+
* timeout, a 5xx) is indistinguishable from "those assets don't exist" if it
|
|
309
|
+
* simply contributes nothing. This method used to swallow a failed chunk and
|
|
310
|
+
* return the rest, so every caller silently read a throttled request as "no
|
|
311
|
+
* metadata": a backfill counted the asset as needing no update, and the
|
|
312
|
+
* signed-record builder embedded a media item with no hash. Both reported
|
|
313
|
+
* success while writing nothing, and the MTN chain's records are immutable.
|
|
314
|
+
*
|
|
315
|
+
* So a failed chunk THROWS {@link ServiceAssetMetadataError} by default,
|
|
316
|
+
* carrying the ids it could not resolve. A caller that genuinely wants
|
|
317
|
+
* best-effort opts in with `{ partial: true }` and gets the old behaviour
|
|
318
|
+
* explicitly. An empty/whitespace-only input resolves immediately with `[]`
|
|
319
|
+
* and performs no network call.
|
|
310
320
|
*
|
|
311
321
|
* Not cached at the SDK layer: it's a POST keyed on a multi-id body (low hit
|
|
312
322
|
* rate), mirroring the sibling service/POST methods which never cache.
|
|
313
323
|
*/
|
|
314
|
-
async getServiceAssetMetadataByIds(ids) {
|
|
324
|
+
async getServiceAssetMetadataByIds(ids, options = {}) {
|
|
315
325
|
const uniqueIds = Array.from(new Set(ids.filter((id) => typeof id === 'string' && id.trim().length > 0)));
|
|
316
326
|
if (uniqueIds.length === 0) {
|
|
317
327
|
return [];
|
|
@@ -320,22 +330,35 @@ function OxyServicesAssetsMixin(Base) {
|
|
|
320
330
|
for (let i = 0; i < uniqueIds.length; i += SERVICE_ASSET_METADATA_CHUNK_SIZE) {
|
|
321
331
|
chunks.push(uniqueIds.slice(i, i + SERVICE_ASSET_METADATA_CHUNK_SIZE));
|
|
322
332
|
}
|
|
323
|
-
//
|
|
333
|
+
// Chunks stay independent so one failure never cancels work already in
|
|
334
|
+
// flight; the failures are collected and re-raised together below.
|
|
335
|
+
const unresolvedIds = [];
|
|
336
|
+
const statuses = [];
|
|
337
|
+
let firstError;
|
|
324
338
|
const settled = await Promise.all(chunks.map(async (chunk) => {
|
|
325
339
|
try {
|
|
326
340
|
const entries = await this.makeServiceRequest('POST', '/assets/service/by-ids', { ids: chunk });
|
|
327
341
|
return Array.isArray(entries) ? entries : [];
|
|
328
342
|
}
|
|
329
343
|
catch (error) {
|
|
330
|
-
|
|
344
|
+
const status = (0, errorUtils_1.extractErrorStatus)(error);
|
|
345
|
+
logger_1.logger.warn('getServiceAssetMetadataByIds: chunk failed', {
|
|
331
346
|
method: 'getServiceAssetMetadataByIds',
|
|
332
347
|
chunkSize: chunk.length,
|
|
333
|
-
status
|
|
348
|
+
status,
|
|
349
|
+
partial: options.partial === true,
|
|
334
350
|
error: error instanceof Error ? error.message : String(error),
|
|
335
351
|
});
|
|
352
|
+
unresolvedIds.push(...chunk);
|
|
353
|
+
if (typeof status === 'number')
|
|
354
|
+
statuses.push(status);
|
|
355
|
+
firstError ?? (firstError = error);
|
|
336
356
|
return [];
|
|
337
357
|
}
|
|
338
358
|
}));
|
|
359
|
+
if (unresolvedIds.length > 0 && options.partial !== true) {
|
|
360
|
+
throw new OxyServices_errors_1.ServiceAssetMetadataError(unresolvedIds, statuses, firstError);
|
|
361
|
+
}
|
|
339
362
|
return settled.flat();
|
|
340
363
|
}
|
|
341
364
|
/**
|
|
@@ -156,11 +156,11 @@ function OxyServicesDeviceBootMixin(Base) {
|
|
|
156
156
|
*
|
|
157
157
|
* That paragraph is LOAD-BEARING, not belt-and-braces: the route sits above
|
|
158
158
|
* oxy-api's router-wide origin guard (deliberately, so a native client with
|
|
159
|
-
* no `Origin` is not rejected)
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
159
|
+
* no `Origin` is not rejected). oxy-api additionally refuses callers that
|
|
160
|
+
* carry browser context signals (`Origin` or `Sec-Fetch-Site`) with
|
|
161
|
+
* `403 browser_not_allowed` — native HTTP clients send neither. Gate the
|
|
162
|
+
* caller by platform on the client as well; do not widen the 404 degrade to
|
|
163
|
+
* 403, which would also swallow a genuine origin misconfiguration.
|
|
164
164
|
*
|
|
165
165
|
* @returns the provisioned credential, or `null` when the endpoint is absent
|
|
166
166
|
* (404). The API deploy leads the SDK release, so a client on a newer SDK
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.getAccountColor = exports.createQuickAccount = exports.buildAccountsArray = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.formatPublicKeyHandle = void 0;
|
|
8
8
|
const i18n_1 = require("../i18n");
|
|
9
|
+
const userHandle_1 = require("./userHandle");
|
|
9
10
|
/**
|
|
10
11
|
* Truncate a long public key for display, e.g. `0x12345678…`.
|
|
11
12
|
* Falls back to the raw key if it's too short to truncate.
|
|
@@ -103,7 +104,11 @@ exports.buildAccountsArray = buildAccountsArray;
|
|
|
103
104
|
* @param getFileDownloadUrl - Function to generate avatar download URL from file ID
|
|
104
105
|
*/
|
|
105
106
|
const createQuickAccount = (sessionId, userData, existingAccount, getFileDownloadUrl) => {
|
|
106
|
-
const
|
|
107
|
+
const nameObj = userData.name && typeof userData.name === 'object' ? userData.name : undefined;
|
|
108
|
+
const apiDisplayName = typeof nameObj?.displayName === 'string' ? nameObj.displayName.trim() : '';
|
|
109
|
+
const displayName = apiDisplayName ||
|
|
110
|
+
(0, userHandle_1.getNormalizedUserHandle)(userData) ||
|
|
111
|
+
(0, exports.getAccountDisplayName)(null);
|
|
107
112
|
const userId = userData.id || (typeof userData._id === 'string' ? userData._id : userData._id?.toString());
|
|
108
113
|
// Preserve existing avatarUrl if avatar hasn't changed (prevents image reload)
|
|
109
114
|
let avatarUrl;
|