@oxy.so/core 1.1.1 → 1.2.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/index.js CHANGED
@@ -19,10 +19,10 @@
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  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.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_CATEGORY_IDS = 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.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 = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = void 0;
23
- 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_UNFLANKED_SEPARATOR_SOURCE = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.DISPLAY_NAME_INVALID_MESSAGE = exports.MAX_DISPLAY_NAME_LENGTH = exports.PASSWORD_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.parseHttpErrorBody = exports.isHttpRequestError = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = exports.buildUrl = exports.buildSearchParams = exports.buildQueryParams = exports.trustTierLabel = exports.reputationCategoryLabel = exports.accountRoleLabel = exports.accountCategoryLabel = exports.translate = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = void 0;
24
- exports.resolveDeviceContext = exports.resolveActiveContext = exports.projectDevicePrincipals = exports.directoryHandle = exports.directoryDisplayName = exports.canActivateContext = 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 = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = void 0;
25
- exports.packageInfo = exports.runSessionColdBoot = exports.OXY_INFERENCE_BASE_URL = exports.OxyInferenceProtocolError = exports.OxyInferenceError = exports.OxyInferenceClient = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.readLocalDeviceCredential = exports.publishProvenDeviceCredential = exports.normalizeSharedDeviceSessionRead = exports.decideSharedDevicePublish = exports.decideSharedDeviceJoin = exports.createSharedMirroringAuthStateStore = exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.resolveAccountDelegationAccess = exports.canSwitchIntoAccount = exports.isSwitchTargetAccount = exports.showsPrincipalHeaders = exports.buildSwitcherRows = void 0;
22
+ 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.coerceToSupportedLocale = 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 = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = void 0;
23
+ 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_UNFLANKED_SEPARATOR_SOURCE = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.DISPLAY_NAME_INVALID_MESSAGE = exports.MAX_DISPLAY_NAME_LENGTH = exports.PASSWORD_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.parseHttpErrorBody = exports.isHttpRequestError = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = exports.buildUrl = exports.buildSearchParams = exports.buildQueryParams = exports.trustTierLabel = exports.reputationCategoryLabel = exports.accountRoleLabel = exports.accountCategoryLabel = exports.translate = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = void 0;
24
+ exports.resolveActiveContext = exports.projectDevicePrincipals = exports.directoryHandle = exports.directoryDisplayName = exports.canActivateContext = 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 = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = void 0;
25
+ exports.packageInfo = exports.runSessionColdBoot = exports.OXY_INFERENCE_BASE_URL = exports.OxyInferenceProtocolError = exports.OxyInferenceError = exports.OxyInferenceClient = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.readLocalDeviceCredential = exports.publishProvenDeviceCredential = exports.normalizeSharedDeviceSessionRead = exports.decideSharedDevicePublish = exports.decideSharedDeviceJoin = exports.createSharedMirroringAuthStateStore = exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.resolveAccountDelegationAccess = exports.canSwitchIntoAccount = exports.isSwitchTargetAccount = exports.showsPrincipalHeaders = exports.buildSwitcherRows = exports.resolveDeviceContext = void 0;
26
26
  // Ensure crypto polyfills are loaded before anything else
27
27
  require("./crypto/polyfill");
28
28
  // ---------------------------------------------------------------------------
@@ -166,6 +166,7 @@ Object.defineProperty(exports, "getNativeLanguageName", { enumerable: true, get:
166
166
  Object.defineProperty(exports, "isRTLLocale", { enumerable: true, get: function () { return languageUtils_1.isRTLLocale; } });
167
167
  Object.defineProperty(exports, "getUserLanguages", { enumerable: true, get: function () { return languageUtils_1.getUserLanguages; } });
168
168
  Object.defineProperty(exports, "getPrimaryLanguage", { enumerable: true, get: function () { return languageUtils_1.getPrimaryLanguage; } });
169
+ Object.defineProperty(exports, "coerceToSupportedLocale", { enumerable: true, get: function () { return languageUtils_1.coerceToSupportedLocale; } });
169
170
  // ---------------------------------------------------------------------------
170
171
  // Platform detection
171
172
  // ---------------------------------------------------------------------------
@@ -85,6 +85,8 @@ class TTLCache {
85
85
  const effectiveTTL = ttl === undefined ? this.defaultTTL : ttl;
86
86
  const expiresAt = now + effectiveTTL;
87
87
  this.cache.set(key, { data, timestamp: now, expiresAt });
88
+ if (!cleanupInterval && activeCaches.has(this))
89
+ updateCleanupInterval();
88
90
  }
89
91
  /**
90
92
  * Delete a specific cache entry
@@ -92,7 +94,10 @@ class TTLCache {
92
94
  * @returns true if entry was deleted, false if not found
93
95
  */
94
96
  delete(key) {
95
- return this.cache.delete(key);
97
+ const deleted = this.cache.delete(key);
98
+ if (activeCaches.has(this))
99
+ updateCleanupInterval();
100
+ return deleted;
96
101
  }
97
102
  /**
98
103
  * Clear all cache entries
@@ -101,6 +106,8 @@ class TTLCache {
101
106
  this.cache.clear();
102
107
  this.hits = 0;
103
108
  this.misses = 0;
109
+ if (activeCaches.has(this))
110
+ updateCleanupInterval();
104
111
  }
105
112
  /**
106
113
  * Check if a key exists and is not expired
@@ -199,28 +206,30 @@ const activeCaches = new Set();
199
206
  */
200
207
  function registerCacheForCleanup(cache) {
201
208
  activeCaches.add(cache);
202
- // Start cleanup interval if not already running
203
- if (!cleanupInterval) {
204
- cleanupInterval = setInterval(() => {
205
- for (const cache of activeCaches) {
206
- cache.cleanup();
207
- }
208
- }, 60000); // Every minute
209
- // Never block process/worker exit on a housekeeping timer.
210
- cleanupInterval.unref?.();
209
+ updateCleanupInterval();
210
+ }
211
+ /** Empty registered caches need no timer; importing a client must stay inert. */
212
+ function updateCleanupInterval() {
213
+ const hasEntries = [...activeCaches].some(cache => cache.size() > 0);
214
+ if (!hasEntries) {
215
+ if (cleanupInterval)
216
+ clearInterval(cleanupInterval);
217
+ cleanupInterval = null;
218
+ return;
211
219
  }
220
+ if (cleanupInterval)
221
+ return;
222
+ cleanupInterval = setInterval(() => {
223
+ for (const cache of activeCaches)
224
+ cache.cleanup();
225
+ updateCleanupInterval();
226
+ }, 60000);
227
+ cleanupInterval.unref?.();
212
228
  }
213
- /**
214
- * Unregister a cache from automatic cleanup
215
- * @param cache Cache instance to unregister
216
- */
229
+ /** Unregister a cache without changing its entries or TTL behavior. */
217
230
  function unregisterCacheFromCleanup(cache) {
218
231
  activeCaches.delete(cache);
219
- // Stop cleanup interval if no caches are registered
220
- if (activeCaches.size === 0 && cleanupInterval) {
221
- clearInterval(cleanupInterval);
222
- cleanupInterval = null;
223
- }
232
+ updateCleanupInterval();
224
233
  }
225
234
  /**
226
235
  * Stop all cleanup intervals (useful for testing)
@@ -24,6 +24,7 @@ exports.getNativeLanguageName = getNativeLanguageName;
24
24
  exports.isRTLLocale = isRTLLocale;
25
25
  exports.getUserLanguages = getUserLanguages;
26
26
  exports.getPrimaryLanguage = getPrimaryLanguage;
27
+ exports.coerceToSupportedLocale = coerceToSupportedLocale;
27
28
  /**
28
29
  * The supported-locale catalog. Ordered by prominence for display; the order
29
30
  * is not otherwise significant. Every entry is a full `language-REGION` tag,
@@ -222,3 +223,28 @@ function getUserLanguages(user) {
222
223
  function getPrimaryLanguage(user) {
223
224
  return getUserLanguages(user)[0];
224
225
  }
226
+ /**
227
+ * Coerce a locale down to one from a CALLER-provided catalog: an exact
228
+ * canonical match wins, else the closest entry sharing the same base
229
+ * language (an `es-MX` account reads a host app's `es-ES` catalog), else
230
+ * `fallback`.
231
+ *
232
+ * A host app's own shipped translation catalog is rarely {@link SUPPORTED_LANGUAGES}
233
+ * itself — Oxy's account-locale catalog is broader than any one app's
234
+ * translated strings — so this is the one place that gap is bridged. Every Oxy
235
+ * app was independently re-deriving this exact algorithm against its own
236
+ * catalog before it moved here.
237
+ */
238
+ function coerceToSupportedLocale(locale, supportedLocales, fallback) {
239
+ if (locale) {
240
+ const canonical = normalizeLocale(locale);
241
+ if (canonical && supportedLocales.includes(canonical)) {
242
+ return canonical;
243
+ }
244
+ const base = getBaseLanguage(locale);
245
+ const byBase = supportedLocales.find((entry) => getBaseLanguage(entry) === base);
246
+ if (byBase)
247
+ return byBase;
248
+ }
249
+ return fallback;
250
+ }