@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.
@@ -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);
@@ -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.TopicType = 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.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
22
- exports.withRetry = 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 = void 0;
23
- exports.formatPublicKeyHandle = 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 = void 0;
24
- exports.refreshDeviceSecretArm = 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 = void 0;
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
- * Resilience: chunks are independent. A failed chunk is logged and skipped —
307
- * the method returns every entry that resolved successfully rather than
308
- * discarding the whole call on one chunk's failure. An empty/whitespace-only
309
- * input resolves immediately with `[]` and performs no network call.
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
- // Run chunks concurrently; a single chunk failure must not sink the rest.
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
- logger_1.logger.warn('getServiceAssetMetadataByIds: chunk failed, continuing with remaining chunks', {
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: (0, errorUtils_1.extractErrorStatus)(error),
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), so as of this writing NOTHING server-side
160
- * refuses a browser caller that presents a valid bearer. Until a server-side
161
- * check lands, caller discipline is the only control which is also why a
162
- * doc note cannot be the whole answer to browser XSS minting a long-lived
163
- * credential with the victim's bearer.
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 displayName = (0, exports.getAccountDisplayName)(userData);
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;