@oxy.so/core 7.11.1 → 7.13.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.
Files changed (56) hide show
  1. package/dist/cjs/HttpService.js +70 -1
  2. package/dist/cjs/OxyServices.js +1 -1
  3. package/dist/cjs/api/apps.js +1 -1
  4. package/dist/cjs/api/auth.js +1 -1
  5. package/dist/cjs/api/billing.js +4 -3
  6. package/dist/cjs/api/devices.js +5 -4
  7. package/dist/cjs/api/follows.js +11 -10
  8. package/dist/cjs/api/holderEvidence.js +12 -0
  9. package/dist/cjs/api/privacy.js +8 -7
  10. package/dist/cjs/api/session.js +128 -7
  11. package/dist/cjs/api/topics.js +4 -3
  12. package/dist/cjs/api/users.js +5 -4
  13. package/dist/cjs/i18n/locales/compact/es-ES.generated.js +1 -1
  14. package/dist/cjs/i18n/locales/en-US.js +40 -15
  15. package/dist/cjs/i18n/locales/es-ES.js +40 -15
  16. package/dist/cjs/index.js +6 -4
  17. package/dist/esm/HttpService.js +70 -1
  18. package/dist/esm/OxyServices.js +1 -1
  19. package/dist/esm/api/apps.js +1 -1
  20. package/dist/esm/api/auth.js +1 -1
  21. package/dist/esm/api/billing.js +4 -3
  22. package/dist/esm/api/devices.js +5 -4
  23. package/dist/esm/api/follows.js +11 -10
  24. package/dist/esm/api/holderEvidence.js +9 -0
  25. package/dist/esm/api/privacy.js +8 -7
  26. package/dist/esm/api/session.js +128 -7
  27. package/dist/esm/api/topics.js +4 -3
  28. package/dist/esm/api/users.js +5 -4
  29. package/dist/esm/i18n/locales/compact/es-ES.generated.js +1 -1
  30. package/dist/esm/i18n/locales/en-US.js +40 -15
  31. package/dist/esm/i18n/locales/es-ES.js +40 -15
  32. package/dist/esm/index.js +1 -0
  33. package/dist/types/HttpService.d.ts +25 -1
  34. package/dist/types/api/holderEvidence.d.ts +45 -0
  35. package/dist/types/api/session.d.ts +21 -0
  36. package/dist/types/i18n/locales/en-US.d.ts +37 -12
  37. package/dist/types/i18n/locales/es-ES.d.ts +37 -12
  38. package/dist/types/index.d.ts +3 -1
  39. package/dist/types/server/safety.d.ts +9 -9
  40. package/package.json +2 -2
  41. package/src/HttpService.ts +108 -1
  42. package/src/OxyServices.ts +4 -1
  43. package/src/api/apps.ts +1 -1
  44. package/src/api/auth.ts +9 -4
  45. package/src/api/billing.ts +12 -5
  46. package/src/api/devices.ts +6 -4
  47. package/src/api/follows.ts +12 -10
  48. package/src/api/holderEvidence.ts +66 -0
  49. package/src/api/privacy.ts +9 -7
  50. package/src/api/session.ts +163 -9
  51. package/src/api/topics.ts +5 -3
  52. package/src/api/users.ts +6 -4
  53. package/src/i18n/locales/compact/es-ES.generated.ts +1 -1
  54. package/src/i18n/locales/en-US.ts +41 -15
  55. package/src/i18n/locales/es-ES.ts +42 -15
  56. package/src/index.ts +8 -0
@@ -163,6 +163,9 @@ const dict = {
163
163
  requestExpired: 'This sign-in expired. Send a new email.',
164
164
  secondFactorInvalid: "That code isn't right. Try the current one.",
165
165
  originNotAllowed: "Sign-in isn't available here.",
166
+ network: "Couldn't reach Oxy. Check your connection and try again.",
167
+ invalidInput: "Something you entered isn't valid. Check it and try again.",
168
+ server: "Oxy couldn't finish this right now. Try again in a moment.",
166
169
  generic: 'Something went wrong. Please try again.',
167
170
  },
168
171
  subtitleToApp: 'to continue to {{app}}',
@@ -225,6 +228,31 @@ const dict = {
225
228
  invalidDescription: 'It expired or was already used. Ask for a new email in the app, or type the code from the latest one.',
226
229
  },
227
230
  },
231
+ actionErrors: {
232
+ updateProfile: "Couldn't update your profile. Please try again.",
233
+ uploadAvatar: "Couldn't upload the picture. Please try again.",
234
+ updateSettings: "Couldn't save your settings. Please try again.",
235
+ updatePrivacy: "Couldn't save your privacy settings. Please try again.",
236
+ updateNotifications: "Couldn't save your notification preferences. Please try again.",
237
+ updatePreferences: "Couldn't save your preferences. Please try again.",
238
+ revokeConnectedApp: "Couldn't disconnect the app. Please try again.",
239
+ switchSession: "Couldn't switch accounts. Please try again.",
240
+ logout: "Couldn't sign out. Please try again.",
241
+ logoutAll: "Couldn't sign out everywhere. Please try again.",
242
+ updateDeviceName: "Couldn't rename the device. Please try again.",
243
+ removeDevice: "Couldn't remove the device. Please try again.",
244
+ },
245
+ usernamePolicy: {
246
+ hint: 'Letters, numbers, - and _. No spaces.',
247
+ required: 'Choose a username.',
248
+ tooShort: 'Use at least {{min}} characters.',
249
+ tooLong: 'Use at most {{max}} characters.',
250
+ noSpaces: "Usernames can't contain spaces. Use - or _ instead.",
251
+ characters: 'Use only letters, numbers, - and _. Start and end with a letter or number, and never put two of - or _ together.',
252
+ onlyNumbers: "A username can't be only numbers. Add a letter.",
253
+ reserved: 'That username is reserved. Choose another one.',
254
+ mustEndInBot: 'A bot account’s username must end in “bot”.',
255
+ },
228
256
  signup: {
229
257
  title: 'Create your account',
230
258
  subtitle: 'Choose your username. Then we confirm your email.',
@@ -233,7 +261,6 @@ const dict = {
233
261
  username: {
234
262
  label: 'Username',
235
263
  placeholder: 'yourname',
236
- required: 'Choose a username.',
237
264
  taken: 'That username is taken.',
238
265
  },
239
266
  email: {
@@ -281,7 +308,6 @@ const dict = {
281
308
  firstPlaceholder: 'Your first name',
282
309
  lastLabel: 'Last name',
283
310
  lastPlaceholder: 'Your last name',
284
- saveFailed: 'Could not save your name',
285
311
  },
286
312
  avatar: {
287
313
  title: 'Make It Yours',
@@ -604,8 +630,6 @@ const dict = {
604
630
  label: 'Username',
605
631
  placeholder: 'Choose a username',
606
632
  choose: 'Choose a username',
607
- required: 'Username is required',
608
- tooShort: 'Username must be at least 3 characters',
609
633
  },
610
634
  email: {
611
635
  title: 'Email',
@@ -1260,7 +1284,7 @@ const dict = {
1260
1284
  },
1261
1285
  rules: {
1262
1286
  title: 'Username rules',
1263
- content: 'Usernames can only contain lowercase letters (a-z) and numbers (0-9). They must be at least 4 characters long. Special characters, spaces, and uppercase letters are not allowed to keep usernames simple and easy to remember.',
1287
+ content: 'Usernames are 3 to 30 characters long and use letters, numbers, hyphens (-) and underscores (_). They start and end with a letter or number, never put two of - or _ together, and are not only numbers. Spaces are not allowed. Capital letters are kept as you type them, but Alice and alice are the same username.',
1264
1288
  },
1265
1289
  unique: {
1266
1290
  title: 'Why must it be unique?',
@@ -1839,9 +1863,6 @@ const dict = {
1839
1863
  label: 'Username',
1840
1864
  available: 'Username is available',
1841
1865
  taken: 'Username is taken',
1842
- tooShort: 'Username must be at least 3 characters',
1843
- invalidChars: 'Only letters, numbers, hyphens, and underscores',
1844
- mustEndInBot: 'A bot account’s username must end in “bot”',
1845
1866
  checkFailed: 'Could not check availability',
1846
1867
  },
1847
1868
  accountCategory: {
@@ -1876,16 +1897,16 @@ const dict = {
1876
1897
  transfer: 'Transfer ownership',
1877
1898
  },
1878
1899
  errors: {
1879
- identifierRequired: 'Enter a username or email to invite',
1900
+ emailRequired: 'Enter an email to invite.',
1901
+ emailInvalid: 'Enter a valid email address, like name@example.com.',
1880
1902
  missingAccount: 'No account selected.',
1881
1903
  noPermission: 'You do not have permission to view members.',
1882
- userNotFound: 'User not found',
1883
1904
  },
1884
1905
  invite: {
1885
- title: 'Add a member',
1886
- identifierLabel: 'Username or email',
1906
+ title: 'Invite a member',
1907
+ emailLabel: 'Email',
1887
1908
  roleLabel: 'Role',
1888
- submit: 'Add member',
1909
+ submit: 'Send invitation',
1889
1910
  },
1890
1911
  removeConfirm: {
1891
1912
  title: 'Remove member',
@@ -1896,8 +1917,9 @@ const dict = {
1896
1917
  description: 'Transfer ownership of this account to this member? You will be demoted to admin. This cannot be undone.',
1897
1918
  },
1898
1919
  toasts: {
1899
- added: 'Member added',
1900
- addFailed: 'Failed to add member',
1920
+ invited: 'Invitation sent',
1921
+ inviteFailed: "Couldn't send the invitation. Please try again.",
1922
+ invitationUpdateFailed: "Couldn't update the invitation. Please try again.",
1901
1923
  removed: 'Member removed',
1902
1924
  removeFailed: 'Failed to remove member',
1903
1925
  roleUpdated: 'Role updated',
@@ -1959,6 +1981,7 @@ const dict = {
1959
1981
  title: 'Delete Account',
1960
1982
  warning: 'This action cannot be undone. Your account and all associated data will be permanently deleted.',
1961
1983
  confirmLabel: 'Type "{{username}}" to confirm',
1984
+ confirmMismatch: 'That isn\'t your username. Type "{{username}}" exactly.',
1962
1985
  confirm: 'Delete Forever',
1963
1986
  error: 'Failed to delete account',
1964
1987
  handoff: {
@@ -2311,6 +2334,7 @@ const dict = {
2311
2334
  newLabel: 'New password',
2312
2335
  repeatLabel: 'Repeat the password',
2313
2336
  tooShort: 'Use at least {{min}} characters.',
2337
+ tooLong: 'Use at most {{max}} characters.',
2314
2338
  mismatch: "The passwords don't match.",
2315
2339
  signOutOthers: 'Sign out everywhere else',
2316
2340
  save: 'Save password',
@@ -2373,6 +2397,7 @@ const dict = {
2373
2397
  request: "Couldn't start the verification. Check the domain and try again.",
2374
2398
  notYet: 'Not verified yet. Changes to DNS can take a while to show up.',
2375
2399
  remove: "Couldn't remove the domain",
2400
+ invalid: 'Enter just the domain, like example.com — no https://, slashes or spaces.',
2376
2401
  },
2377
2402
  remove: {
2378
2403
  title: 'Remove domain',
@@ -163,6 +163,9 @@ const dict = {
163
163
  requestExpired: 'Este inicio de sesión ha caducado. Envía un email nuevo.',
164
164
  secondFactorInvalid: 'Ese código no es correcto. Prueba con el actual.',
165
165
  originNotAllowed: 'Aquí no se puede iniciar sesión.',
166
+ network: 'No se ha podido conectar con Oxy. Comprueba tu conexión e inténtalo de nuevo.',
167
+ invalidInput: 'Algo de lo que has escrito no es válido. Revísalo e inténtalo de nuevo.',
168
+ server: 'Oxy no ha podido terminar esto ahora. Inténtalo de nuevo en un momento.',
166
169
  generic: 'Algo ha ido mal. Inténtalo de nuevo.',
167
170
  },
168
171
  subtitleToApp: 'para continuar en {{app}}',
@@ -225,6 +228,31 @@ const dict = {
225
228
  invalidDescription: 'Ha caducado o ya se ha usado. Pide un email nuevo en la app o escribe el código del último.',
226
229
  },
227
230
  },
231
+ actionErrors: {
232
+ updateProfile: 'No se ha podido actualizar tu perfil. Inténtalo de nuevo.',
233
+ uploadAvatar: 'No se ha podido subir la foto. Inténtalo de nuevo.',
234
+ updateSettings: 'No se han podido guardar tus ajustes. Inténtalo de nuevo.',
235
+ updatePrivacy: 'No se han podido guardar tus ajustes de privacidad. Inténtalo de nuevo.',
236
+ updateNotifications: 'No se han podido guardar tus preferencias de notificaciones. Inténtalo de nuevo.',
237
+ updatePreferences: 'No se han podido guardar tus preferencias. Inténtalo de nuevo.',
238
+ revokeConnectedApp: 'No se ha podido desconectar la app. Inténtalo de nuevo.',
239
+ switchSession: 'No se ha podido cambiar de cuenta. Inténtalo de nuevo.',
240
+ logout: 'No se ha podido cerrar la sesión. Inténtalo de nuevo.',
241
+ logoutAll: 'No se ha podido cerrar la sesión en todas partes. Inténtalo de nuevo.',
242
+ updateDeviceName: 'No se ha podido cambiar el nombre del dispositivo. Inténtalo de nuevo.',
243
+ removeDevice: 'No se ha podido quitar el dispositivo. Inténtalo de nuevo.',
244
+ },
245
+ usernamePolicy: {
246
+ hint: 'Letras, números, - y _. Sin espacios.',
247
+ required: 'Elige un nombre de usuario.',
248
+ tooShort: 'Usa al menos {{min}} caracteres.',
249
+ tooLong: 'Usa como máximo {{max}} caracteres.',
250
+ noSpaces: 'El nombre de usuario no puede tener espacios. Usa - o _ en su lugar.',
251
+ characters: 'Usa solo letras, números, - y _. Empieza y termina con una letra o un número, y no pongas dos - o _ seguidos.',
252
+ onlyNumbers: 'El nombre de usuario no puede ser solo números. Añade una letra.',
253
+ reserved: 'Ese nombre de usuario está reservado. Elige otro.',
254
+ mustEndInBot: 'El nombre de usuario de una cuenta bot debe terminar en «bot».',
255
+ },
228
256
  signup: {
229
257
  title: 'Crea tu cuenta',
230
258
  subtitle: 'Elige tu nombre de usuario. Después confirmamos tu email.',
@@ -233,7 +261,6 @@ const dict = {
233
261
  username: {
234
262
  label: 'Nombre de usuario',
235
263
  placeholder: 'tunombre',
236
- required: 'Elige un nombre de usuario.',
237
264
  taken: 'Ese nombre de usuario ya está en uso.',
238
265
  },
239
266
  email: {
@@ -305,8 +332,6 @@ const dict = {
305
332
  label: 'Usuario',
306
333
  placeholder: 'Elige un usuario',
307
334
  choose: 'Elige un usuario',
308
- required: 'El usuario es obligatorio',
309
- tooShort: 'El usuario debe tener al menos 3 caracteres',
310
335
  },
311
336
  email: {
312
337
  title: 'Correo electrónico',
@@ -904,7 +929,6 @@ const dict = {
904
929
  firstPlaceholder: 'Tu nombre',
905
930
  lastLabel: 'Apellidos',
906
931
  lastPlaceholder: 'Tus apellidos',
907
- saveFailed: 'No se pudo guardar tu nombre',
908
932
  },
909
933
  avatar: {
910
934
  title: 'Hazlo tuyo',
@@ -1260,7 +1284,7 @@ const dict = {
1260
1284
  },
1261
1285
  rules: {
1262
1286
  title: 'Reglas del nombre de usuario',
1263
- content: 'Los nombres de usuario solo pueden contener letras minúsculas (a-z) y números (0-9). Deben tener al menos 4 caracteres. No se permiten caracteres especiales, espacios ni mayúsculas para mantener los nombres de usuario simples y fáciles de recordar.',
1287
+ content: 'Los nombres de usuario tienen entre 3 y 30 caracteres y usan letras, números, guiones (-) y guiones bajos (_). Empiezan y terminan con una letra o un número, nunca llevan dos - o _ seguidos y no son solo números. No se permiten espacios. Las mayúsculas se guardan tal como las escribes, pero Alicia y alicia son el mismo nombre de usuario.',
1264
1288
  },
1265
1289
  unique: {
1266
1290
  title: '¿Por qué debe ser único?',
@@ -1839,9 +1863,6 @@ const dict = {
1839
1863
  label: 'Nombre de usuario',
1840
1864
  available: 'El nombre de usuario está disponible',
1841
1865
  taken: 'El nombre de usuario ya está en uso',
1842
- tooShort: 'El nombre de usuario debe tener al menos 3 caracteres',
1843
- invalidChars: 'Solo letras, números, guiones y guiones bajos',
1844
- mustEndInBot: 'El nombre de usuario de una cuenta de bot debe terminar en “bot”',
1845
1866
  checkFailed: 'No se pudo comprobar la disponibilidad',
1846
1867
  },
1847
1868
  accountCategory: {
@@ -1876,16 +1897,16 @@ const dict = {
1876
1897
  transfer: 'Transferir propiedad',
1877
1898
  },
1878
1899
  errors: {
1879
- identifierRequired: 'Introduce un nombre de usuario o correo para invitar',
1900
+ emailRequired: 'Introduce un email para invitar.',
1901
+ emailInvalid: 'Introduce un email válido, como nombre@ejemplo.com.',
1880
1902
  missingAccount: 'Ninguna cuenta seleccionada.',
1881
1903
  noPermission: 'No tienes permiso para ver los miembros.',
1882
- userNotFound: 'Usuario no encontrado',
1883
1904
  },
1884
1905
  invite: {
1885
- title: 'Añadir un miembro',
1886
- identifierLabel: 'Nombre de usuario o correo',
1906
+ title: 'Invitar a un miembro',
1907
+ emailLabel: 'Email',
1887
1908
  roleLabel: 'Rol',
1888
- submit: 'Añadir miembro',
1909
+ submit: 'Enviar invitación',
1889
1910
  },
1890
1911
  removeConfirm: {
1891
1912
  title: 'Eliminar miembro',
@@ -1896,8 +1917,9 @@ const dict = {
1896
1917
  description: '¿Transferir la propiedad de esta cuenta a este miembro? Pasarás a ser administrador. Esto no se puede deshacer.',
1897
1918
  },
1898
1919
  toasts: {
1899
- added: 'Miembro añadido',
1900
- addFailed: 'No se pudo añadir el miembro',
1920
+ invited: 'Invitación enviada',
1921
+ inviteFailed: 'No se ha podido enviar la invitación. Inténtalo de nuevo.',
1922
+ invitationUpdateFailed: 'No se ha podido actualizar la invitación. Inténtalo de nuevo.',
1901
1923
  removed: 'Miembro eliminado',
1902
1924
  removeFailed: 'No se pudo eliminar el miembro',
1903
1925
  roleUpdated: 'Rol actualizado',
@@ -1959,6 +1981,7 @@ const dict = {
1959
1981
  title: 'Eliminar cuenta',
1960
1982
  warning: 'Esta acción no se puede deshacer. Tu cuenta y todos los datos asociados se eliminarán de forma permanente.',
1961
1983
  confirmLabel: 'Escribe "{{username}}" para confirmar',
1984
+ confirmMismatch: 'Ese no es tu nombre de usuario. Escribe "{{username}}" exactamente.',
1962
1985
  confirm: 'Eliminar para siempre',
1963
1986
  error: 'No se pudo eliminar la cuenta',
1964
1987
  handoff: {
@@ -2311,6 +2334,7 @@ const dict = {
2311
2334
  newLabel: 'Contraseña nueva',
2312
2335
  repeatLabel: 'Repite la contraseña',
2313
2336
  tooShort: 'Usa al menos {{min}} caracteres.',
2337
+ tooLong: 'Usa como máximo {{max}} caracteres.',
2314
2338
  mismatch: 'Las contraseñas no coinciden.',
2315
2339
  signOutOthers: 'Cerrar sesión en todos los demás sitios',
2316
2340
  save: 'Guardar contraseña',
@@ -2373,6 +2397,7 @@ const dict = {
2373
2397
  request: 'No se pudo empezar la verificación. Revisa el dominio e inténtalo otra vez.',
2374
2398
  notYet: 'Aún no está verificado. Los cambios de DNS pueden tardar en aparecer.',
2375
2399
  remove: 'No se pudo quitar el dominio',
2400
+ invalid: 'Escribe solo el dominio, como ejemplo.com, sin https://, barras ni espacios.',
2376
2401
  },
2377
2402
  remove: {
2378
2403
  title: 'Quitar dominio',
package/dist/cjs/index.js CHANGED
@@ -21,10 +21,10 @@
21
21
  * If a symbol does not appear here, it is NOT part of the public API.
22
22
  */
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.getBaseLanguage = exports.FALLBACK_LOCALE = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.commonsDeliveryPlatform = exports.pushTargetsFromDelivery = exports.selectCommonsDelivery = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.OxyAppDataIdentifierError = exports.AccountNotOnDeviceError = exports.REPUTATION_CACHE_PREFIX = exports.StoreInstallerError = exports.setStoreInstaller = exports.getStoreInstaller = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_CATEGORY_IDS = exports.buildUserDid = exports.DirectUploadInterruptedError = exports.MAX_UPLOAD_BYTES = exports.assetUrlCacheTTL = exports.ASSET_MULTIPART_UPLOAD_MAX_BYTES = exports.ServiceAssetMetadataError = exports.AssetUrlResolutionError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyApiError = exports.OXY_CLOUD_URL = exports.OXY_API_URL = exports.OxyServices = void 0;
25
- exports.isLocaleLoaded = exports.loadLocale = exports.translate = 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.coerceToSupportedLocale = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = void 0;
26
- 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.accountNameInvalidKey = exports.accountNameInvalidMessage = exports.accountNameMaxLength = exports.isValidAccountName = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.ACCOUNT_TITLE_INVALID_MESSAGE = 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.getLocalesVersion = exports.subscribeLocales = void 0;
27
- exports.packageInfo = 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.CENTRAL_IDP_APEX = exports.AUTH_WEB_ORIGIN = 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;
24
+ exports.FALLBACK_LOCALE = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.commonsDeliveryPlatform = exports.pushTargetsFromDelivery = exports.selectCommonsDelivery = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.OxyAppDataIdentifierError = exports.AccountNotOnDeviceError = exports.REPUTATION_CACHE_PREFIX = exports.StoreInstallerError = exports.setStoreInstaller = exports.getStoreInstaller = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_CATEGORY_IDS = exports.buildUserDid = exports.DirectUploadInterruptedError = exports.MAX_UPLOAD_BYTES = exports.assetUrlCacheTTL = exports.ASSET_MULTIPART_UPLOAD_MAX_BYTES = exports.sameHolderIdentity = exports.ServiceAssetMetadataError = exports.AssetUrlResolutionError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyApiError = exports.OXY_CLOUD_URL = exports.OXY_API_URL = exports.OxyServices = void 0;
25
+ exports.loadLocale = exports.translate = 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.coerceToSupportedLocale = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = exports.getBaseLanguage = void 0;
26
+ 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.accountNameInvalidKey = exports.accountNameInvalidMessage = exports.accountNameMaxLength = exports.isValidAccountName = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.ACCOUNT_TITLE_INVALID_MESSAGE = 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.getLocalesVersion = exports.subscribeLocales = exports.isLocaleLoaded = void 0;
27
+ exports.packageInfo = 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.CENTRAL_IDP_APEX = exports.AUTH_WEB_ORIGIN = 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 = exports.normalizeMultilineText = void 0;
28
28
  // The crypto shim MUST stay the first import. The root itself ships no crypto
29
29
  // library (`__tests__/rootEntryGraph.test.ts`), but `@oxy.so/protocol`'s root
30
30
  // entry (reached by the OAuth/PKCE and device helpers) does, and Metro does not
@@ -44,6 +44,8 @@ Object.defineProperty(exports, "OxyAuthenticationError", { enumerable: true, get
44
44
  Object.defineProperty(exports, "OxyAuthenticationTimeoutError", { enumerable: true, get: function () { return OxyServices_errors_1.OxyAuthenticationTimeoutError; } });
45
45
  Object.defineProperty(exports, "AssetUrlResolutionError", { enumerable: true, get: function () { return OxyServices_errors_1.AssetUrlResolutionError; } });
46
46
  Object.defineProperty(exports, "ServiceAssetMetadataError", { enumerable: true, get: function () { return OxyServices_errors_1.ServiceAssetMetadataError; } });
47
+ var holderEvidence_1 = require("./api/holderEvidence");
48
+ Object.defineProperty(exports, "sameHolderIdentity", { enumerable: true, get: function () { return holderEvidence_1.sameHolderIdentity; } });
47
49
  var assets_1 = require("./api/assets");
48
50
  Object.defineProperty(exports, "ASSET_MULTIPART_UPLOAD_MAX_BYTES", { enumerable: true, get: function () { return assets_1.ASSET_MULTIPART_UPLOAD_MAX_BYTES; } });
49
51
  Object.defineProperty(exports, "assetUrlCacheTTL", { enumerable: true, get: function () { return assets_1.assetUrlCacheTTL; } });
@@ -36,6 +36,9 @@ const REQUESTER_PROOF_HEADERS = new Set([
36
36
  'x-oxy-presented-requester-context',
37
37
  'x-oxy-presented-service-token',
38
38
  ]);
39
+ function newCredentialGeneration() {
40
+ return Object.freeze({});
41
+ }
39
42
  /** Comparison only fences local async work; it never validates token authority. */
40
43
  function sameTokenSession(left, right) {
41
44
  if (left === right)
@@ -246,14 +249,25 @@ const CACHE_SIZE_WARNING_THROTTLE_MS = 60000;
246
249
  class TokenStore {
247
250
  constructor() {
248
251
  this.accessToken = null;
252
+ this.generation = newCredentialGeneration();
249
253
  }
250
254
  setTokens(accessToken) {
255
+ this.generation = newCredentialGeneration();
251
256
  this.accessToken = accessToken;
252
257
  }
258
+ /** The identity of the bearer's current installation, never of its bytes. */
259
+ getGeneration() {
260
+ return this.generation;
261
+ }
262
+ /** Retire the current generation without touching the bearer (provider swaps). */
263
+ invalidateGeneration() {
264
+ this.generation = newCredentialGeneration();
265
+ }
253
266
  getAccessToken() {
254
267
  return this.accessToken;
255
268
  }
256
269
  clearTokens() {
270
+ this.generation = newCredentialGeneration();
257
271
  this.accessToken = null;
258
272
  }
259
273
  hasAccessToken() {
@@ -304,7 +318,7 @@ function linkAbort(parent, child) {
304
318
  * Uses native fetch instead of axios for smaller bundle size.
305
319
  */
306
320
  export class HttpService {
307
- constructor(config) {
321
+ constructor(config, credentialSource = null) {
308
322
  // Only active calls retain a lease. Invalidation detaches matching leases,
309
323
  // so late results cannot warm the cache or attract new deduplicated callers.
310
324
  this.readLeases = new Map();
@@ -401,6 +415,7 @@ export class HttpService {
401
415
  averageResponseTime: 0,
402
416
  };
403
417
  this.config = config;
418
+ this.credentialSource = credentialSource;
404
419
  this.baseURL = config.baseURL;
405
420
  this.tokenStore = new TokenStore();
406
421
  this.logger = new SimpleLogger(config.enableLogging || false, config.logLevel || 'error', 'HttpService');
@@ -517,6 +532,17 @@ export class HttpService {
517
532
  async request(config) {
518
533
  if (config.signal?.aborted)
519
534
  throw createCancelledError('Request cancelled before it was sent');
535
+ if (config.expectedCredentialGeneration) {
536
+ // The prototype is deliberately limited to writes; it does not
537
+ // yet define leases for cached/deduplicated reads or raw response paths.
538
+ if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(config.method) ||
539
+ config.skipAuth ||
540
+ Object.keys(config.headers ?? {}).some((key) => key.toLowerCase() === 'authorization')) {
541
+ throw new Error('A credential generation requires an implicitly authenticated write');
542
+ }
543
+ (this.credentialSource ?? this).assertCredentialGeneration(config.expectedCredentialGeneration);
544
+ config = { ...config, cache: false, deduplicate: false, retry: false };
545
+ }
520
546
  // The provider can change independently of the last request. Synchronize
521
547
  // before deriving the cache and deduplication identity, including hits.
522
548
  const explicitAuthorization = Object.keys(config.headers ?? {}).some((key) => key.toLowerCase() === 'authorization');
@@ -606,6 +632,12 @@ export class HttpService {
606
632
  const authHeader = usesSessionAuth
607
633
  ? await awaitRequestSignal(this.getAuthHeader(), signal)
608
634
  : null;
635
+ const assertExpectedCredential = () => {
636
+ if (config.expectedCredentialGeneration) {
637
+ (this.credentialSource ?? this).assertCredentialGeneration(config.expectedCredentialGeneration, authHeader);
638
+ }
639
+ };
640
+ assertExpectedCredential();
609
641
  const isOriginalSession = () => {
610
642
  if (this.sessionEpoch !== contextEpoch)
611
643
  return false;
@@ -655,6 +687,7 @@ export class HttpService {
655
687
  if (usesSessionAuth && !isOriginalSession()) {
656
688
  throw new OxyAuthenticationError('The request session changed', 'AUTH_SESSION_CHANGED');
657
689
  }
690
+ assertExpectedCredential();
658
691
  /**
659
692
  * Whether OUR timer fired, so a timeout can be told apart from a
660
693
  * cancellation. Both reach us from `fetch` as an indistinguishable
@@ -751,6 +784,9 @@ export class HttpService {
751
784
  Object.keys(headers).some((key) => REQUESTER_PROOF_HEADERS.has(key.toLowerCase()))) {
752
785
  throw new Error('Requester proofs require a transport that rejects redirects');
753
786
  }
787
+ // Header and source generation are checked together in this synchronous
788
+ // dispatch segment, after the preflight and any queue wait.
789
+ assertExpectedCredential();
754
790
  const response = useXhrForUpload
755
791
  ? await this.uploadViaXHR(fullUrl, method, headers, bodyValue, controller.signal, timeout, false)
756
792
  : await fetch(fullUrl, {
@@ -765,6 +801,13 @@ export class HttpService {
765
801
  // it, including on every throw between here and there.
766
802
  if (timeoutId)
767
803
  clearTimeout(timeoutId);
804
+ try {
805
+ assertExpectedCredential();
806
+ }
807
+ catch (error) {
808
+ void discardResponseBody(response);
809
+ throw error;
810
+ }
768
811
  if (usesSessionAuth && !isOriginalSession()) {
769
812
  void discardResponseBody(response);
770
813
  throw new OxyAuthenticationError('The request session changed', 'AUTH_SESSION_CHANGED');
@@ -775,6 +818,7 @@ export class HttpService {
775
818
  // once before giving up. HttpService deliberately does not know any
776
819
  // session routes; the refresh handler owns session rotation.
777
820
  if (response.status === 401 &&
821
+ !config.expectedCredentialGeneration &&
778
822
  !config._isAuthRetry &&
779
823
  usesSessionAuth &&
780
824
  isOriginalSession()) {
@@ -910,12 +954,14 @@ export class HttpService {
910
954
  if (usesSessionAuth && !isOriginalSession()) {
911
955
  throw new OxyAuthenticationError('The request session changed', 'AUTH_SESSION_CHANGED');
912
956
  }
957
+ assertExpectedCredential();
913
958
  const duration = Date.now() - startTime;
914
959
  this.updateMetrics(true, duration);
915
960
  this.config.onRequestEnd?.(url, method, duration, true);
916
961
  return responseData;
917
962
  }
918
963
  catch (error) {
964
+ assertExpectedCredential();
919
965
  const duration = Date.now() - startTime;
920
966
  this.updateMetrics(false, duration);
921
967
  // A body reader can reject or finish parsing an error after the viewer
@@ -1027,6 +1073,7 @@ export class HttpService {
1027
1073
  if (usesSessionAuth && !isOriginalSession()) {
1028
1074
  throw new OxyAuthenticationError('The request session changed', 'AUTH_SESSION_CHANGED');
1029
1075
  }
1076
+ assertExpectedCredential();
1030
1077
  // Cache the result if caching is enabled
1031
1078
  if (cache && cacheKey && lease.valid && result) {
1032
1079
  this.cache.set(cacheKey, result, cacheTTL);
@@ -1752,6 +1799,27 @@ export class HttpService {
1752
1799
  getSessionEpoch() {
1753
1800
  return this.sessionEpoch;
1754
1801
  }
1802
+ /** Capture only the local generation; the caller must separately bind authority. */
1803
+ captureCredentialGeneration() {
1804
+ // A pull-only provider can change A→B→A between reads without notifying us.
1805
+ if (this.accessTokenProvider)
1806
+ return null;
1807
+ return this.tokenStore.getAccessToken() ? this.tokenStore.getGeneration() : null;
1808
+ }
1809
+ isCredentialGenerationCurrent(generation) {
1810
+ return (!this.accessTokenProvider &&
1811
+ this.tokenStore.getAccessToken() !== null &&
1812
+ generation === this.tokenStore.getGeneration());
1813
+ }
1814
+ assertCredentialGeneration(generation, selectedHeader) {
1815
+ const token = this.tokenStore.getAccessToken();
1816
+ if (this.accessTokenProvider ||
1817
+ !token ||
1818
+ generation !== this.tokenStore.getGeneration() ||
1819
+ (selectedHeader !== undefined && selectedHeader !== `Bearer ${token}`)) {
1820
+ throw new OxyAuthenticationError('The request credential changed', 'AUTH_SESSION_CHANGED');
1821
+ }
1822
+ }
1755
1823
  /** Whether {@link endSession} ran and no token has been planted since. */
1756
1824
  hasSessionEnded() {
1757
1825
  return this.sessionEnded;
@@ -1760,6 +1828,7 @@ export class HttpService {
1760
1828
  this.authRefreshHandler = handler;
1761
1829
  }
1762
1830
  setAccessTokenProvider(provider) {
1831
+ this.tokenStore.invalidateGeneration();
1763
1832
  this.accessTokenProvider = provider;
1764
1833
  }
1765
1834
  /**
@@ -114,7 +114,7 @@ export class OxyServices {
114
114
  * Query). Pass `enableCache: true` to opt in.
115
115
  */
116
116
  createLinkedClient(config) {
117
- const client = new HttpService({ ...config, enableCache: config.enableCache ?? false });
117
+ const client = new HttpService({ ...config, enableCache: config.enableCache ?? false }, this.http);
118
118
  const syncToken = (accessToken) => {
119
119
  const current = client.getAccessToken();
120
120
  if (accessToken) {
@@ -81,7 +81,7 @@ export class ConnectedAppsApi {
81
81
  * `ConnectedApp.applicationId`, NOT a credential/client id).
82
82
  */
83
83
  async revoke(applicationId) {
84
- await this.ctx.request('DELETE', `/auth/grants/${applicationId}`, undefined, { cache: false });
84
+ await this.ctx.request('DELETE', `/auth/grants/${enc(applicationId)}`, undefined, { cache: false });
85
85
  this.ctx.oxy.cache.delete('GET:/auth/grants');
86
86
  }
87
87
  /** Resource-bound external MCP connections for the active Oxy account. */
@@ -932,7 +932,7 @@ export class AuthApi {
932
932
  */
933
933
  async checkUsername(username) {
934
934
  // Public availability lookup (pre-session) — skip the bearer preflight.
935
- return this.ctx.request('GET', `/auth/check-username/${username}`, undefined, {
935
+ return this.ctx.request('GET', `/auth/check-username/${encodeURIComponent(username)}`, undefined, {
936
936
  cache: false,
937
937
  skipAuth: true,
938
938
  });
@@ -10,6 +10,7 @@
10
10
  * arrive as ISO-8601 strings.
11
11
  */
12
12
  import { cancelProductSubscriptionCheck, parseContract, personalPlanCatalogueCheck, personalPlanCheckoutRequestCheck, personalPlanCheckoutResultCheck, productPlanCatalogueCheck, productPlanCheckoutRequestCheck, productSubscriptionCancellationResultCheck, productSubscriptionRenewalRequestCheck, productSubscriptionsResponseCheck, subjectProductAccessCheck, subjectProductAccessQueryCheck, subscriptionCreditGrantsResponseCheck, } from '@oxy.so/contracts/client';
13
+ const enc = encodeURIComponent;
13
14
  const SUBSCRIPTION_TTL = 2 * 60 * 1000;
14
15
  const WALLET_TTL = 60 * 1000;
15
16
  export class BillingApi {
@@ -73,7 +74,7 @@ export class BillingApi {
73
74
  /** A user's subscription (default: the signed-in user's). */
74
75
  async subscription(userId) {
75
76
  const id = this.resolveUserId(userId);
76
- return this.ctx.request('GET', `/subscription/${id}`, undefined, {
77
+ return this.ctx.request('GET', `/subscription/${enc(id)}`, undefined, {
77
78
  cache: true,
78
79
  cacheTTL: SUBSCRIPTION_TTL,
79
80
  });
@@ -81,7 +82,7 @@ export class BillingApi {
81
82
  /** A user's wallet (default: the signed-in user's). Cached briefly: the balance moves. */
82
83
  async wallet(userId) {
83
84
  const id = this.resolveUserId(userId);
84
- return this.ctx.request('GET', `/wallet/${id}`, undefined, {
85
+ return this.ctx.request('GET', `/wallet/${enc(id)}`, undefined, {
85
86
  cache: true,
86
87
  cacheTTL: WALLET_TTL,
87
88
  });
@@ -94,7 +95,7 @@ export class BillingApi {
94
95
  params.limit = options.limit;
95
96
  if (options.offset)
96
97
  params.offset = options.offset;
97
- return this.ctx.request('GET', `/wallet/transactions/${id}`, params, {
98
+ return this.ctx.request('GET', `/wallet/transactions/${enc(id)}`, params, {
98
99
  cache: false,
99
100
  });
100
101
  }
@@ -1,6 +1,7 @@
1
1
  import { checkContract, deviceBackgroundCredentialResponseCheck, deviceJoinCodeResponseCheck, deviceJoinResponseCheck, deviceRegisterResponseCheck, deviceTokenMintResponseCheck, } from '@oxy.so/contracts/client';
2
2
  import { logger } from '../logger/index.js';
3
3
  import { extractErrorStatus } from '../utils/errorUtils.js';
4
+ const enc = encodeURIComponent;
4
5
  /**
5
6
  * The server's `401 account_not_on_device` for a PINNED mint: the requested
6
7
  * `accountId` is not (or is no longer) a live account of this device session.
@@ -71,7 +72,7 @@ export class DevicesApi {
71
72
  * @param deviceId - The device ID to remove
72
73
  */
73
74
  async remove(deviceId) {
74
- await this.ctx.request('DELETE', `/devices/${deviceId}`, undefined, { cache: false });
75
+ await this.ctx.request('DELETE', `/devices/${enc(deviceId)}`, undefined, { cache: false });
75
76
  }
76
77
  /**
77
78
  * Get device sessions for a given session ID
@@ -81,7 +82,7 @@ export class DevicesApi {
81
82
  */
82
83
  async sessions(sessionId) {
83
84
  // Cache disabled by default to ensure fresh session data
84
- return this.ctx.request('GET', `/session/device/sessions/${sessionId}`, undefined, {
85
+ return this.ctx.request('GET', `/session/device/sessions/${enc(sessionId)}`, undefined, {
85
86
  cache: false, // Don't cache sessions - always get fresh data
86
87
  deduplicate: true, // Deduplicate concurrent requests for same sessionId
87
88
  });
@@ -99,7 +100,7 @@ export class DevicesApi {
99
100
  urlParams.deviceId = deviceId;
100
101
  if (excludeCurrent)
101
102
  urlParams.excludeCurrent = 'true';
102
- return this.ctx.request('POST', `/session/device/logout-all/${sessionId}`, urlParams, { cache: false });
103
+ return this.ctx.request('POST', `/session/device/logout-all/${enc(sessionId)}`, urlParams, { cache: false });
103
104
  }
104
105
  /**
105
106
  * Update device name
@@ -108,7 +109,7 @@ export class DevicesApi {
108
109
  * @returns Updated device object
109
110
  */
110
111
  async rename(sessionId, deviceName) {
111
- return this.ctx.request('PUT', `/session/device/name/${sessionId}`, { deviceName }, { cache: false });
112
+ return this.ctx.request('PUT', `/session/device/name/${enc(sessionId)}`, { deviceName }, { cache: false });
112
113
  }
113
114
  /**
114
115
  * Get security information