@oxyhq/core 3.4.8 → 3.4.10

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.
@@ -207,9 +207,11 @@ class HttpService {
207
207
  const fullUrl = this.buildURL(url, params);
208
208
  // Get auth token (with auto-refresh)
209
209
  const authHeader = await this.getAuthHeader();
210
- // Get CSRF token for state-changing requests
210
+ // CSRF protects cookie-authenticated browser writes. Bearer-authenticated
211
+ // SDK clients are not vulnerable to ambient-cookie CSRF, and linked app
212
+ // APIs should not need to implement a duplicate `/csrf-token` route.
211
213
  const isStateChangingMethod = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
212
- const csrfToken = isStateChangingMethod ? await this.fetchCsrfToken() : null;
214
+ const csrfToken = isStateChangingMethod && !authHeader ? await this.fetchCsrfToken() : null;
213
215
  // Determine if data is FormData using robust detection
214
216
  const isFormData = this.isFormData(data);
215
217
  // Make fetch request
@@ -240,10 +242,8 @@ class HttpService {
240
242
  if (isNativeApp && isStateChangingMethod) {
241
243
  headers['X-Native-App'] = 'true';
242
244
  }
243
- // Debug logging for CSRF issues routed through the SimpleLogger so
244
- // it only fires when consumers opt in via `enableLogging`. Previously
245
- // this was a bare console.log that leaked noise into every host app's
246
- // stdout in development.
245
+ // Debug logging for CSRF issues, routed through SimpleLogger so it only
246
+ // fires when consumers opt in via `enableLogging`.
247
247
  if (isStateChangingMethod) {
248
248
  this.logger.debug('CSRF Debug:', {
249
249
  url,
package/dist/cjs/index.js CHANGED
@@ -18,9 +18,10 @@
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.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.isRTLLocale = exports.normalizeLanguageCode = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.RecoveryPhraseService = exports.SignatureService = 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.OxyAppDataIdentifierError = exports.ServiceCredentialMismatchError = exports.createCrossDomainAuth = exports.CrossDomainAuth = exports.createAuthManager = exports.AuthManager = exports.oxyClient = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyServices = void 0;
22
- exports.isValidURL = exports.isValidUUID = exports.isValidObject = exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = 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.translate = exports.createDebugLogger = exports.debugError = exports.debugWarn = exports.debugLog = exports.isDev = 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 = void 0;
23
- exports.packageInfo = exports.runColdBoot = exports.guardActive = exports.isCentralIdPOrigin = exports.buildSsoBounceUrl = exports.getSsoCallbackBootstrapScript = exports.ssoNavigate = exports.ssoCallbackBootstrapKey = exports.ssoAttemptedKey = exports.ssoNoSessionKey = exports.ssoDestKey = exports.ssoGuardKey = exports.ssoStateKey = exports.SSO_GUARD_TTL_MS = exports.SSO_CALLBACK_PATH = exports.generateSsoState = exports.consumeSsoReturn = exports.parseSsoReturnFragment = exports.resolveCentralAuthUrl = exports.CENTRAL_IDP_APEX = exports.CENTRAL_AUTH_URL = exports.MULTIPART_TLDS = exports.registrableApex = exports.autoDetectAuthWebUrl = exports.getAccountColor = exports.mergeAccountsFromRefreshAll = exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.logPerformance = exports.logPayment = exports.logDevice = exports.logUser = exports.logSession = exports.logApi = exports.logAuth = exports.LogLevel = exports.logger = exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = void 0;
21
+ exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.isRTLLocale = exports.normalizeLanguageCode = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.RecoveryPhraseService = exports.SignatureService = 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.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.OxyAppDataIdentifierError = exports.ServiceCredentialMismatchError = exports.createCrossDomainAuth = exports.CrossDomainAuth = exports.createAuthManager = exports.AuthManager = exports.oxyClient = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyServices = void 0;
22
+ exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = 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.translate = exports.createDebugLogger = exports.debugError = exports.debugWarn = exports.debugLog = exports.isDev = 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 = void 0;
23
+ exports.runColdBoot = exports.guardActive = exports.isCentralIdPOrigin = exports.buildSsoBounceUrl = exports.getSsoCallbackBootstrapScript = exports.ssoNavigate = exports.ssoCallbackBootstrapKey = exports.ssoAttemptedKey = exports.ssoNoSessionKey = exports.ssoDestKey = exports.ssoGuardKey = exports.ssoStateKey = exports.SSO_GUARD_TTL_MS = exports.SSO_CALLBACK_PATH = exports.generateSsoState = exports.consumeSsoReturn = exports.parseSsoReturnFragment = exports.resolveCentralAuthUrl = exports.CENTRAL_IDP_APEX = exports.CENTRAL_AUTH_URL = exports.MULTIPART_TLDS = exports.registrableApex = exports.autoDetectAuthWebUrl = exports.getAccountColor = exports.mergeAccountsFromRefreshAll = exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.logPerformance = exports.logPayment = exports.logDevice = exports.logUser = exports.logSession = exports.logApi = exports.logAuth = exports.LogLevel = exports.logger = exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = exports.isValidURL = exports.isValidUUID = exports.isValidObject = void 0;
24
+ exports.packageInfo = void 0;
24
25
  // Ensure crypto polyfills are loaded before anything else
25
26
  require("./crypto/polyfill");
26
27
  // ---------------------------------------------------------------------------
@@ -46,6 +47,10 @@ var OxyServices_auth_1 = require("./mixins/OxyServices.auth");
46
47
  Object.defineProperty(exports, "ServiceCredentialMismatchError", { enumerable: true, get: function () { return OxyServices_auth_1.ServiceCredentialMismatchError; } });
47
48
  var OxyServices_appData_1 = require("./mixins/OxyServices.appData");
48
49
  Object.defineProperty(exports, "OxyAppDataIdentifierError", { enumerable: true, get: function () { return OxyServices_appData_1.OxyAppDataIdentifierError; } });
50
+ var userIdentity_1 = require("./utils/userIdentity");
51
+ Object.defineProperty(exports, "getNormalizedUserId", { enumerable: true, get: function () { return userIdentity_1.getNormalizedUserId; } });
52
+ Object.defineProperty(exports, "normalizeUserIdentity", { enumerable: true, get: function () { return userIdentity_1.normalizeUserIdentity; } });
53
+ Object.defineProperty(exports, "normalizeUserIdentityOrNull", { enumerable: true, get: function () { return userIdentity_1.normalizeUserIdentityOrNull; } });
49
54
  // ---------------------------------------------------------------------------
50
55
  // Auth helpers (token refresh, error normalisation, retry policies)
51
56
  // ---------------------------------------------------------------------------
@@ -5,6 +5,7 @@ exports.OxyServicesAuthMixin = OxyServicesAuthMixin;
5
5
  const OxyServices_errors_1 = require("../OxyServices.errors");
6
6
  const platformCrypto_1 = require("../utils/platformCrypto");
7
7
  const loggerUtils_1 = require("../utils/loggerUtils");
8
+ const userIdentity_1 = require("../utils/userIdentity");
8
9
  /**
9
10
  * Sentinel error raised when getServiceToken() is called with a known apiKey
10
11
  * but a non-matching secret. Indicates either credential drift in the caller
@@ -313,7 +314,10 @@ function OxyServicesAuthMixin(Base) {
313
314
  if (res?.accessToken) {
314
315
  this.setTokens(res.accessToken);
315
316
  }
316
- return res;
317
+ return {
318
+ ...res,
319
+ user: (0, userIdentity_1.normalizeUserIdentity)(res.user),
320
+ };
317
321
  }
318
322
  catch (error) {
319
323
  throw this.handleError(error);
@@ -335,7 +339,8 @@ function OxyServicesAuthMixin(Base) {
335
339
  */
336
340
  async getUserByPublicKey(publicKey) {
337
341
  try {
338
- return await this.makeRequest('GET', `/auth/user/${encodeURIComponent(publicKey)}`, undefined, { cache: true, cacheTTL: 2 * 60 * 1000 });
342
+ const user = await this.makeRequest('GET', `/auth/user/${encodeURIComponent(publicKey)}`, undefined, { cache: true, cacheTTL: 2 * 60 * 1000 });
343
+ return (0, userIdentity_1.normalizeUserIdentity)(user);
339
344
  }
340
345
  catch (error) {
341
346
  throw this.handleError(error);
@@ -346,10 +351,11 @@ function OxyServicesAuthMixin(Base) {
346
351
  */
347
352
  async getUserBySession(sessionId) {
348
353
  try {
349
- return await this.makeRequest('GET', `/session/user/${sessionId}`, undefined, {
354
+ const user = await this.makeRequest('GET', `/session/user/${sessionId}`, undefined, {
350
355
  cache: true,
351
356
  cacheTTL: 2 * 60 * 1000,
352
357
  });
358
+ return (0, userIdentity_1.normalizeUserIdentity)(user);
353
359
  }
354
360
  catch (error) {
355
361
  throw this.handleError(error);
@@ -364,11 +370,15 @@ function OxyServicesAuthMixin(Base) {
364
370
  return [];
365
371
  }
366
372
  const uniqueSessionIds = Array.from(new Set(sessionIds)).sort();
367
- return await this.makeRequest('POST', '/session/users/batch', { sessionIds: uniqueSessionIds }, {
373
+ const users = await this.makeRequest('POST', '/session/users/batch', { sessionIds: uniqueSessionIds }, {
368
374
  cache: true,
369
375
  cacheTTL: 2 * 60 * 1000,
370
376
  deduplicate: true,
371
377
  });
378
+ return users.map((entry) => ({
379
+ ...entry,
380
+ user: (0, userIdentity_1.normalizeUserIdentityOrNull)(entry.user),
381
+ }));
372
382
  }
373
383
  catch (error) {
374
384
  throw this.handleError(error);
@@ -693,7 +703,11 @@ function OxyServicesAuthMixin(Base) {
693
703
  urlParams.deviceFingerprint = options.deviceFingerprint;
694
704
  if (options.useHeaderValidation)
695
705
  urlParams.useHeaderValidation = 'true';
696
- return await this.makeRequest('GET', `/session/validate/${sessionId}`, urlParams, { cache: false });
706
+ const validation = await this.makeRequest('GET', `/session/validate/${sessionId}`, urlParams, { cache: false });
707
+ return {
708
+ ...validation,
709
+ user: (0, userIdentity_1.normalizeUserIdentity)(validation.user),
710
+ };
697
711
  }
698
712
  catch (error) {
699
713
  // Session is invalid — clear any cached user data for this session (#196)
@@ -728,13 +742,17 @@ function OxyServicesAuthMixin(Base) {
728
742
  */
729
743
  async signUp(username, email, password, deviceName, deviceFingerprint) {
730
744
  try {
731
- return await this.makeRequest('POST', '/auth/signup', {
745
+ const session = await this.makeRequest('POST', '/auth/signup', {
732
746
  username,
733
747
  email,
734
748
  password,
735
749
  deviceName,
736
750
  deviceFingerprint,
737
751
  }, { cache: false });
752
+ return {
753
+ ...session,
754
+ user: (0, userIdentity_1.normalizeUserIdentity)(session.user),
755
+ };
738
756
  }
739
757
  catch (error) {
740
758
  throw this.handleError(error);
@@ -745,12 +763,16 @@ function OxyServicesAuthMixin(Base) {
745
763
  */
746
764
  async signIn(identifier, password, deviceName, deviceFingerprint) {
747
765
  try {
748
- return await this.makeRequest('POST', '/auth/login', {
766
+ const session = await this.makeRequest('POST', '/auth/login', {
749
767
  identifier,
750
768
  password,
751
769
  deviceName,
752
770
  deviceFingerprint,
753
771
  }, { cache: false });
772
+ return {
773
+ ...session,
774
+ user: (0, userIdentity_1.normalizeUserIdentity)(session.user),
775
+ };
754
776
  }
755
777
  catch (error) {
756
778
  throw this.handleError(error);
@@ -4,6 +4,7 @@ exports.OxyServicesFedCMMixin = OxyServicesFedCMMixin;
4
4
  exports.FedCMMixin = OxyServicesFedCMMixin;
5
5
  const OxyServices_errors_1 = require("../OxyServices.errors");
6
6
  const debugUtils_1 = require("../shared/utils/debugUtils");
7
+ const userIdentity_1 = require("../utils/userIdentity");
7
8
  const debug = (0, debugUtils_1.createDebugLogger)('FedCM');
8
9
  // Modern (W3C spec) → legacy (Chrome 125–131) mode value mapping. Used to
9
10
  // retry a credential request when an older browser rejects the modern enum.
@@ -138,13 +139,13 @@ function OxyServicesFedCMMixin(Base) {
138
139
  * @throws {OxyAuthenticationError} If FedCM not supported or user cancels
139
140
  *
140
141
  * @example
141
- * ```typescript
142
- * try {
143
- * const session = await oxyServices.signInWithFedCM();
144
- * console.log('Signed in:', session.user);
145
- * } catch (error) {
146
- * // Fallback to redirect auth
147
- * oxyServices.signInWithRedirect();
142
+ * ```typescript
143
+ * try {
144
+ * const session = await oxyServices.signInWithFedCM();
145
+ * const user = session.user;
146
+ * } catch (error) {
147
+ * // Fallback to redirect auth
148
+ * oxyServices.signInWithRedirect();
148
149
  * }
149
150
  * ```
150
151
  */
@@ -589,7 +590,10 @@ function OxyServicesFedCMMixin(Base) {
589
590
  hasSession: !!response?.sessionId,
590
591
  hasUser: !!response?.user,
591
592
  });
592
- return response;
593
+ return {
594
+ ...response,
595
+ user: (0, userIdentity_1.normalizeUserIdentity)(response.user),
596
+ };
593
597
  }
594
598
  catch (error) {
595
599
  debug.error('Token exchange failed:', error instanceof Error ? error.message : String(error));
@@ -4,6 +4,7 @@ exports.OxyServicesUserMixin = OxyServicesUserMixin;
4
4
  const apiUtils_1 = require("../utils/apiUtils");
5
5
  const keyManager_1 = require("../crypto/keyManager");
6
6
  const signatureService_1 = require("../crypto/signatureService");
7
+ const userIdentity_1 = require("../utils/userIdentity");
7
8
  function OxyServicesUserMixin(Base) {
8
9
  return class extends Base {
9
10
  constructor(...args) {
@@ -14,10 +15,11 @@ function OxyServicesUserMixin(Base) {
14
15
  */
15
16
  async getProfileByUsername(username) {
16
17
  try {
17
- return await this.makeRequest('GET', `/profiles/username/${username}`, undefined, {
18
+ const user = await this.makeRequest('GET', `/profiles/username/${username}`, undefined, {
18
19
  cache: true,
19
20
  cacheTTL: 5 * 60 * 1000, // 5 minutes cache for profiles
20
21
  });
22
+ return (0, userIdentity_1.normalizeUserIdentity)(user);
21
23
  }
22
24
  catch (error) {
23
25
  throw this.handleError(error);
@@ -80,7 +82,7 @@ function OxyServicesUserMixin(Base) {
80
82
  cache: true,
81
83
  cacheTTL: 24 * 60 * 60 * 1000, // 24h cache — matches server-side staleness window
82
84
  });
83
- return result ?? null;
85
+ return (0, userIdentity_1.normalizeUserIdentityOrNull)(result);
84
86
  }
85
87
  catch {
86
88
  return null;
@@ -92,7 +94,7 @@ function OxyServicesUserMixin(Base) {
92
94
  * method — calling services never write user data directly.
93
95
  */
94
96
  async resolveExternalUser(data) {
95
- return this.makeRequest('PUT', '/users/resolve', data);
97
+ return (0, userIdentity_1.normalizeUserIdentity)(await this.makeRequest('PUT', '/users/resolve', data));
96
98
  }
97
99
  /**
98
100
  * Get profile recommendations, optionally filtering out specific user types.
@@ -122,20 +124,22 @@ function OxyServicesUserMixin(Base) {
122
124
  const params = {};
123
125
  if (limit)
124
126
  params.limit = String(limit);
125
- return await this.makeRequest('GET', `/profiles/${userId}/similar`, params, {
127
+ const users = await this.makeRequest('GET', `/profiles/${userId}/similar`, params, {
126
128
  cache: true,
127
129
  cacheTTL: 5 * 60 * 1000, // 5 min cache
128
130
  });
131
+ return users.map((user) => (0, userIdentity_1.normalizeUserIdentity)(user));
129
132
  }
130
133
  /**
131
134
  * Get user by ID
132
135
  */
133
136
  async getUserById(userId) {
134
137
  try {
135
- return await this.makeRequest('GET', `/users/${userId}`, undefined, {
138
+ const user = await this.makeRequest('GET', `/users/${userId}`, undefined, {
136
139
  cache: true,
137
140
  cacheTTL: 5 * 60 * 1000, // 5 minutes cache
138
141
  });
142
+ return (0, userIdentity_1.normalizeUserIdentity)(user);
139
143
  }
140
144
  catch (error) {
141
145
  throw this.handleError(error);
@@ -146,10 +150,11 @@ function OxyServicesUserMixin(Base) {
146
150
  */
147
151
  async getCurrentUser() {
148
152
  return this.withAuthRetry(async () => {
149
- return await this.makeRequest('GET', '/users/me', undefined, {
153
+ const user = await this.makeRequest('GET', '/users/me', undefined, {
150
154
  cache: true,
151
155
  cacheTTL: 1 * 60 * 1000, // 1 minute cache for current user
152
156
  });
157
+ return (0, userIdentity_1.normalizeUserIdentity)(user);
153
158
  }, 'getCurrentUser');
154
159
  }
155
160
  /**
@@ -167,7 +172,7 @@ function OxyServicesUserMixin(Base) {
167
172
  */
168
173
  async updateProfile(updates) {
169
174
  try {
170
- const result = await this.makeRequest('PUT', '/users/me', updates, { cache: false });
175
+ const result = (0, userIdentity_1.normalizeUserIdentity)(await this.makeRequest('PUT', '/users/me', updates, { cache: false }));
171
176
  // Bust every cached representation of the current user. We use a
172
177
  // prefix sweep rather than an enumeration because the SDK never
173
178
  // tracks the set of active session IDs centrally.
@@ -180,9 +185,15 @@ function OxyServicesUserMixin(Base) {
180
185
  return result;
181
186
  }
182
187
  catch (error) {
183
- const errorAny = error;
184
188
  const errorMessage = error instanceof Error ? error.message : String(error);
185
- const status = errorAny?.status || errorAny?.response?.status;
189
+ const errorRecord = error && typeof error === 'object'
190
+ ? error
191
+ : null;
192
+ const status = typeof errorRecord?.status === 'number'
193
+ ? errorRecord.status
194
+ : typeof errorRecord?.response?.status === 'number'
195
+ ? errorRecord.response.status
196
+ : undefined;
186
197
  // Check if it's an authentication error (401)
187
198
  const isAuthError = status === 401 ||
188
199
  errorMessage.includes('Authentication required') ||
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNormalizedUserId = getNormalizedUserId;
4
+ exports.normalizeUserIdentity = normalizeUserIdentity;
5
+ exports.normalizeUserIdentityOrNull = normalizeUserIdentityOrNull;
6
+ function stringifyIdentity(value) {
7
+ if (typeof value === 'string') {
8
+ const trimmed = value.trim();
9
+ return trimmed.length > 0 ? trimmed : null;
10
+ }
11
+ if (typeof value === 'number' && Number.isFinite(value)) {
12
+ return String(value);
13
+ }
14
+ if (value && typeof value === 'object' && 'toString' in value) {
15
+ const toStringFn = value.toString;
16
+ if (typeof toStringFn === 'function' && toStringFn !== Object.prototype.toString) {
17
+ const rendered = toStringFn.call(value);
18
+ if (typeof rendered === 'string') {
19
+ const trimmed = rendered.trim();
20
+ return trimmed.length > 0 && trimmed !== '[object Object]' ? trimmed : null;
21
+ }
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ function getNormalizedUserId(user) {
27
+ if (!user) {
28
+ return null;
29
+ }
30
+ return stringifyIdentity(user.id) ?? stringifyIdentity(user._id);
31
+ }
32
+ function normalizeUserIdentity(user) {
33
+ const id = getNormalizedUserId(user);
34
+ if (!id) {
35
+ throw new Error('User response missing id');
36
+ }
37
+ return { ...user, id };
38
+ }
39
+ function normalizeUserIdentityOrNull(user) {
40
+ return user ? normalizeUserIdentity(user) : null;
41
+ }