@adonis-agora/authkit-server 0.47.0 → 0.48.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 (188) hide show
  1. package/build/commands/clients_create.js +4 -4
  2. package/build/commands/configure.js +3 -2
  3. package/build/commands/doctor.js +8 -5
  4. package/build/commands/eject.js +1 -1
  5. package/build/commands/expire_scan.js +7 -5
  6. package/build/commands/import_users.js +1 -1
  7. package/build/commands/keys_rotate.js +7 -4
  8. package/build/host/views/account/apps.edge +5 -5
  9. package/build/host/views/account/login.edge +1 -1
  10. package/build/host/views/account/mfa.edge +7 -7
  11. package/build/host/views/account/orgs.edge +5 -5
  12. package/build/host/views/account/security.edge +10 -10
  13. package/build/host/views/account/tokens.edge +5 -5
  14. package/build/index.d.ts +7 -7
  15. package/build/index.js +5 -5
  16. package/build/providers/authkit_server_provider.d.ts +2 -2
  17. package/build/providers/authkit_server_provider.js +20 -11
  18. package/build/services/main.d.ts +1 -1
  19. package/build/services/main.js +2 -2
  20. package/build/src/accounts/lucid_account_store.d.ts +3 -3
  21. package/build/src/accounts/lucid_account_store.js +6 -8
  22. package/build/src/accounts/lucid_store/core.js +1 -1
  23. package/build/src/accounts/lucid_store/mfa.js +1 -1
  24. package/build/src/accounts/lucid_store/organizations.js +2 -4
  25. package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
  26. package/build/src/accounts/lucid_store/shared.d.ts +1 -1
  27. package/build/src/accounts/lucid_stores.d.ts +3 -3
  28. package/build/src/accounts/lucid_stores.js +2 -2
  29. package/build/src/adapters/database_adapter.js +3 -1
  30. package/build/src/adapters/factory.js +1 -1
  31. package/build/src/adapters/redis_adapter.js +2 -1
  32. package/build/src/commands/expire_scan_command.js +19 -5
  33. package/build/src/commands/settings_commands.js +22 -5
  34. package/build/src/define_config.d.ts +20 -20
  35. package/build/src/define_config.js +62 -67
  36. package/build/src/doctor/checks.js +45 -20
  37. package/build/src/events/dispatcher.d.ts +1 -1
  38. package/build/src/events/dispatcher.js +10 -10
  39. package/build/src/host/account_api/account_api_controller.js +95 -39
  40. package/build/src/host/account_deletion_ops.d.ts +4 -4
  41. package/build/src/host/account_deletion_ops.js +5 -5
  42. package/build/src/host/account_deletion_service.d.ts +2 -2
  43. package/build/src/host/account_deletion_service.js +2 -2
  44. package/build/src/host/account_export_service.d.ts +1 -1
  45. package/build/src/host/account_export_service.js +4 -6
  46. package/build/src/host/account_home.d.ts +0 -8
  47. package/build/src/host/account_home.js +6 -4
  48. package/build/src/host/account_login_url.d.ts +6 -3
  49. package/build/src/host/account_login_url.js +16 -8
  50. package/build/src/host/account_paths.d.ts +120 -0
  51. package/build/src/host/account_paths.js +161 -0
  52. package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
  53. package/build/src/host/admin_api/admin_orgs_service.js +3 -2
  54. package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
  55. package/build/src/host/admin_api/admin_users_service.js +20 -20
  56. package/build/src/host/admin_api/api_clients_controller.js +2 -2
  57. package/build/src/host/admin_api/api_misc_controller.js +2 -2
  58. package/build/src/host/admin_api/api_orgs_controller.js +3 -3
  59. package/build/src/host/admin_api/api_settings_controller.js +11 -5
  60. package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
  61. package/build/src/host/admin_api/api_users_controller.js +37 -38
  62. package/build/src/host/admin_api/dto.d.ts +4 -4
  63. package/build/src/host/admin_clients_service.js +5 -4
  64. package/build/src/host/admin_console/admin_shell_controller.js +4 -2
  65. package/build/src/host/admin_console/console_audit_controller.js +1 -1
  66. package/build/src/host/admin_console/console_clients_controller.js +5 -3
  67. package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
  68. package/build/src/host/admin_console/console_keys_controller.js +2 -4
  69. package/build/src/host/admin_console/console_orgs_controller.js +3 -3
  70. package/build/src/host/admin_console/console_roles_controller.js +30 -12
  71. package/build/src/host/admin_console/console_sessions_controller.js +3 -3
  72. package/build/src/host/admin_console/console_settings_controller.js +11 -5
  73. package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
  74. package/build/src/host/admin_console/console_users_controller.js +46 -47
  75. package/build/src/host/admin_prefix.js +2 -2
  76. package/build/src/host/admin_sessions_service.js +1 -3
  77. package/build/src/host/adonis_auth_sync.d.ts +1 -1
  78. package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
  79. package/build/src/host/app_key.js +1 -1
  80. package/build/src/host/avatar_storage.js +1 -3
  81. package/build/src/host/config_locks.js +1 -3
  82. package/build/src/host/console_session.js +1 -1
  83. package/build/src/host/controllers/account_apps_controller.js +3 -2
  84. package/build/src/host/controllers/account_confirm_controller.js +2 -6
  85. package/build/src/host/controllers/account_mfa_controller.js +63 -12
  86. package/build/src/host/controllers/account_orgs_controller.js +47 -27
  87. package/build/src/host/controllers/account_security_controller.d.ts +2 -2
  88. package/build/src/host/controllers/account_security_controller.js +114 -115
  89. package/build/src/host/controllers/account_session_controller.js +5 -5
  90. package/build/src/host/controllers/account_tokens_controller.js +3 -2
  91. package/build/src/host/controllers/interaction_controller.js +59 -22
  92. package/build/src/host/controllers/registration_controller.js +28 -13
  93. package/build/src/host/default_mailer.js +11 -7
  94. package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
  95. package/build/src/host/durable/account_deletion_workflow.js +14 -14
  96. package/build/src/host/durable/account_export_workflow.d.ts +2 -2
  97. package/build/src/host/durable/account_export_workflow.js +12 -12
  98. package/build/src/host/durable/index.d.ts +5 -5
  99. package/build/src/host/durable/index.js +6 -6
  100. package/build/src/host/i18n.d.ts +1376 -1376
  101. package/build/src/host/i18n.js +1380 -1380
  102. package/build/src/host/impersonation.js +1 -7
  103. package/build/src/host/key_rotation_actions.d.ts +1 -1
  104. package/build/src/host/login_attempt.js +35 -6
  105. package/build/src/host/login_notify.js +1 -1
  106. package/build/src/host/otp_lockout.js +1 -3
  107. package/build/src/host/rate_limit.js +1 -1
  108. package/build/src/host/register_auth_host.d.ts +30 -0
  109. package/build/src/host/register_auth_host.js +120 -74
  110. package/build/src/host/renderers/edge_renderer.js +15 -2
  111. package/build/src/host/renderers/inertia_renderer.d.ts +9 -1
  112. package/build/src/host/renderers/inertia_renderer.js +9 -1
  113. package/build/src/host/runtime_settings.js +1 -1
  114. package/build/src/host/runtime_toggles.d.ts +3 -3
  115. package/build/src/host/runtime_toggles.js +69 -24
  116. package/build/src/host/security_notice_service.d.ts +1 -1
  117. package/build/src/host/security_notice_service.js +1 -1
  118. package/build/src/host/session_context.js +1 -1
  119. package/build/src/host/sudo/index.d.ts +3 -3
  120. package/build/src/host/sudo/index.js +3 -3
  121. package/build/src/host/sudo/methods/magic_link.js +6 -5
  122. package/build/src/host/sudo/methods/passkey.js +7 -4
  123. package/build/src/host/sudo/methods/password.js +8 -3
  124. package/build/src/host/sudo/runtime.js +6 -13
  125. package/build/src/host/sudo_mode.js +3 -2
  126. package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
  127. package/build/src/host/ui-dist/index.html +1 -1
  128. package/build/src/keys/jwks_manager.js +1 -1
  129. package/build/src/keys/keystore.js +1 -1
  130. package/build/src/keys/keystore_manager.d.ts +2 -2
  131. package/build/src/keys/keystore_manager.js +2 -2
  132. package/build/src/keys/keystore_vault.js +8 -4
  133. package/build/src/mixins/with_audit_log.d.ts +1 -1
  134. package/build/src/mixins/with_auth_user.d.ts +1 -1
  135. package/build/src/mixins/with_auth_user.js +1 -1
  136. package/build/src/mixins/with_credentials.d.ts +1 -1
  137. package/build/src/mixins/with_mfa.d.ts +1 -1
  138. package/build/src/mixins/with_personal_access_token.d.ts +1 -1
  139. package/build/src/mixins/with_provider_identity.d.ts +1 -1
  140. package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
  141. package/build/src/observability/diagnostics_bridge.js +2 -2
  142. package/build/src/observability/metrics_service.js +1 -1
  143. package/build/src/observability/telescope/data_providers.d.ts +1 -1
  144. package/build/src/observability/telescope/data_providers.js +29 -43
  145. package/build/src/observability/telescope/extension.d.ts +1 -1
  146. package/build/src/observability/telescope/extension.js +58 -58
  147. package/build/src/observability/telescope/index.d.ts +2 -2
  148. package/build/src/observability/telescope/index.js +2 -2
  149. package/build/src/observability/wire_provider_events.d.ts +1 -1
  150. package/build/src/observability/wire_provider_events.js +11 -11
  151. package/build/src/password/common_passwords.js +1 -1
  152. package/build/src/password/password_manager.d.ts +1 -1
  153. package/build/src/password/password_manager.js +4 -4
  154. package/build/src/pat/lucid_pat_store.js +2 -6
  155. package/build/src/pat/pat_tokens.js +1 -1
  156. package/build/src/provider/interaction_actions.js +1 -1
  157. package/build/src/provider/key_rotation_scheduler.js +3 -1
  158. package/build/src/provider/logout_sources.js +2 -8
  159. package/build/src/provider/oidc_service.d.ts +2 -2
  160. package/build/src/provider/oidc_service.js +22 -14
  161. package/build/src/provider/single_flight_lock.js +1 -3
  162. package/build/src/provider/token_exchange.d.ts +1 -1
  163. package/build/src/schema/ensure.js +3 -1
  164. package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
  165. package/build/stubs/ui/react/pages/account/login.tsx +6 -7
  166. package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
  167. package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
  168. package/build/stubs/ui/react/pages/consent.tsx +12 -12
  169. package/build/stubs/ui/react/pages/forgot.tsx +7 -7
  170. package/build/stubs/ui/react/pages/login.tsx +22 -24
  171. package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  172. package/build/stubs/ui/react/pages/reset.tsx +10 -10
  173. package/build/stubs/ui/react/pages/signup.tsx +30 -16
  174. package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
  175. package/package.json +1 -1
  176. package/stubs/main.ts +2 -2
  177. package/stubs/ui/react/components/auth_shell.tsx +19 -19
  178. package/stubs/ui/react/pages/account/login.tsx +6 -7
  179. package/stubs/ui/react/pages/account/mfa.tsx +16 -15
  180. package/stubs/ui/react/pages/account/tokens.tsx +27 -17
  181. package/stubs/ui/react/pages/consent.tsx +12 -12
  182. package/stubs/ui/react/pages/forgot.tsx +7 -7
  183. package/stubs/ui/react/pages/login.tsx +22 -24
  184. package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
  185. package/stubs/ui/react/pages/reset.tsx +10 -10
  186. package/stubs/ui/react/pages/signup.tsx +30 -16
  187. package/stubs/ui/react/pages/verify-email.tsx +6 -6
  188. package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
@@ -28,21 +28,22 @@
28
28
  * GET /account/api/orgs/invitations → convites pendentes
29
29
  */
30
30
  import '../augmentations.js';
31
- import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
32
- import { supportsAccountSecurity, supportsProfile, supportsPasskeys, supportsOrganizations, } from '../../accounts/account_store.js';
33
- import { changePasswordValidator, changeEmailValidator, updateProfileValidator, } from '../validators.js';
34
- import { AdminSessionsService } from '../admin_sessions_service.js';
35
- import { enrichSessionsWithContext } from '../session_context.js';
31
+ import { supportsAccountSecurity, supportsOrganizations, supportsPasskeys, supportsProfile, } from '../../accounts/account_store.js';
36
32
  import { PasswordPolicyError } from '../../password/password_manager.js';
37
- import { resolveRuntimeSettings } from '../runtime_settings.js';
33
+ import { accountPath } from '../account_paths.js';
34
+ import { ACTIVE_ORG_COOKIE } from '../active_org_cookie.js';
35
+ import { AdminSessionsService } from '../admin_sessions_service.js';
38
36
  import { syncAdonisAuthLogout } from '../adonis_auth_sync.js';
37
+ import { AvatarUploadError, isAvatarUploadSupported, storeAvatar } from '../avatar_storage.js';
38
+ import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail } from '../default_mailer.js';
39
+ import { translate } from '../i18n.js';
40
+ import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
41
+ import { resolveRuntimeSettings } from '../runtime_settings.js';
39
42
  import { resolveEffectiveEmailChange, resolveEffectivePasswordHistory, } from '../runtime_toggles.js';
40
43
  import { dispatchSecurityNotice } from '../security_notice_service.js';
41
- import { requireSudo, isSudoActive, SUDO_MODE_DEFAULTS, resolveEffectiveSudoMode } from '../sudo_mode.js';
42
- import { translate } from '../i18n.js';
43
- import { storeAvatar, isAvatarUploadSupported, AvatarUploadError } from '../avatar_storage.js';
44
- import { sendEmailChangeConfirmationEmail, sendEmailChangeNoticeEmail, } from '../default_mailer.js';
45
- import { ACTIVE_ORG_COOKIE } from '../active_org_cookie.js';
44
+ import { enrichSessionsWithContext } from '../session_context.js';
45
+ import { SUDO_MODE_DEFAULTS, isSudoActive, requireSudo, resolveEffectiveSudoMode, } from '../sudo_mode.js';
46
+ import { changeEmailValidator, changePasswordValidator, updateProfileValidator, } from '../validators.js';
46
47
  // ---------------------------------------------------------------------------
47
48
  // Helpers (shared with existing controllers — kept local to avoid coupling)
48
49
  // ---------------------------------------------------------------------------
@@ -91,7 +92,9 @@ export default class AccountApiController {
91
92
  let sudoActive = false;
92
93
  try {
93
94
  const sudoSettings = await resolveRuntimeSettings(ctx);
94
- const resolved = sudoSettings ? await resolveEffectiveSudoMode(sudoSettings) : SUDO_MODE_DEFAULTS;
95
+ const resolved = sudoSettings
96
+ ? await resolveEffectiveSudoMode(sudoSettings)
97
+ : SUDO_MODE_DEFAULTS;
95
98
  sudoActive = resolved.enabled ? isSudoActive(ctx, resolved.graceMinutes) : true;
96
99
  }
97
100
  catch {
@@ -212,7 +215,9 @@ export default class AccountApiController {
212
215
  const store = cfg.accountStore;
213
216
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
214
217
  if (!supportsProfile(store)) {
215
- return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.profile.not_supported')));
218
+ return ctx.response
219
+ .status(422)
220
+ .send(apiErr('capability_unsupported', translate(cfg.messages, 'account.profile.not_supported')));
216
221
  }
217
222
  const { name, avatarUrl } = await ctx.request.validateUsing(updateProfileValidator);
218
223
  let resolvedAvatarUrl = avatarUrl ?? null;
@@ -263,30 +268,39 @@ export default class AccountApiController {
263
268
  const store = cfg.accountStore;
264
269
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
265
270
  if (!supportsAccountSecurity(store)) {
266
- return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
271
+ return ctx.response
272
+ .status(422)
273
+ .send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
267
274
  }
268
275
  // Sudo gate.
269
276
  const sudoSettings = await resolveRuntimeSettings(ctx);
270
277
  const sudoResult = await requireSudo(ctx, sudoSettings);
271
278
  if (sudoResult !== true) {
272
279
  // Para a API JSON, devolvemos 403 em vez de redirecionar.
273
- return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
280
+ return ctx.response
281
+ .status(403)
282
+ .send(apiErr('sudo_required', 'Identity confirmation required.'));
274
283
  }
275
284
  const { currentPassword, newPassword } = await ctx.request.validateUsing(changePasswordValidator);
276
285
  const account = await store.findById(userId);
277
286
  const verified = account ? await store.verifyCredentials(account.email, currentPassword) : null;
278
287
  if (!verified) {
279
- return ctx.response.status(422).send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
288
+ return ctx.response
289
+ .status(422)
290
+ .send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
280
291
  }
281
292
  // Histórico de senhas.
282
293
  const { supportsPasswordHistory } = await import('../../accounts/account_store.js');
283
294
  if (supportsPasswordHistory(store)) {
284
295
  const histSettings = await resolvePasswordHistorySettings(ctx);
285
296
  if (histSettings.enabled) {
286
- const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ?? newPassword;
297
+ const pepperedNew = cfg.accountStore.__passwordManager?.applyCurrentPepper?.(newPassword) ??
298
+ newPassword;
287
299
  const reused = await store.isPasswordReused(userId, pepperedNew, histSettings.count);
288
300
  if (reused) {
289
- return ctx.response.status(422).send(apiErr('password_reused', translate(cfg.messages, 'password.reused', { count: histSettings.count })));
301
+ return ctx.response
302
+ .status(422)
303
+ .send(apiErr('password_reused', translate(cfg.messages, 'password.reused', { count: histSettings.count })));
290
304
  }
291
305
  const rawRow = await cfg.accountStore.__getRawRow?.(userId);
292
306
  if (rawRow?.password) {
@@ -300,7 +314,9 @@ export default class AccountApiController {
300
314
  }
301
315
  catch (error) {
302
316
  if (error instanceof PasswordPolicyError) {
303
- return ctx.response.status(422).send(apiErr('password_policy', translate(cfg.messages, error.key, error.params)));
317
+ return ctx.response
318
+ .status(422)
319
+ .send(apiErr('password_policy', translate(cfg.messages, error.key, error.params)));
304
320
  }
305
321
  throw error;
306
322
  }
@@ -327,31 +343,45 @@ export default class AccountApiController {
327
343
  const store = cfg.accountStore;
328
344
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
329
345
  if (!supportsAccountSecurity(store)) {
330
- return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
346
+ return ctx.response
347
+ .status(422)
348
+ .send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
331
349
  }
332
350
  const sudoSettings = await resolveRuntimeSettings(ctx);
333
351
  const sudoResult = await requireSudo(ctx, sudoSettings);
334
352
  if (sudoResult !== true) {
335
- return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
353
+ return ctx.response
354
+ .status(403)
355
+ .send(apiErr('sudo_required', 'Identity confirmation required.'));
336
356
  }
337
357
  const emailChangeSettings = await resolveEmailChangeSettings(ctx);
338
358
  if (!emailChangeSettings.enabled) {
339
- return ctx.response.status(422).send(apiErr('disabled', translate(cfg.messages, 'account.security.email_change_disabled')));
359
+ return ctx.response
360
+ .status(422)
361
+ .send(apiErr('disabled', translate(cfg.messages, 'account.security.email_change_disabled')));
340
362
  }
341
363
  const { currentPassword, newEmail } = await ctx.request.validateUsing(changeEmailValidator);
342
364
  const account = await store.findById(userId);
343
365
  if (emailChangeSettings.requirePassword) {
344
366
  if (!currentPassword) {
345
- return ctx.response.status(422).send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
367
+ return ctx.response
368
+ .status(422)
369
+ .send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
346
370
  }
347
- const verified = account ? await store.verifyCredentials(account.email, currentPassword) : null;
371
+ const verified = account
372
+ ? await store.verifyCredentials(account.email, currentPassword)
373
+ : null;
348
374
  if (!verified) {
349
- return ctx.response.status(422).send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
375
+ return ctx.response
376
+ .status(422)
377
+ .send(apiErr('invalid_credentials', translate(cfg.messages, 'errors.invalid_credentials')));
350
378
  }
351
379
  }
352
380
  const issued = await store.requestEmailChange(userId, newEmail);
353
381
  if (!issued) {
354
- return ctx.response.status(409).send(apiErr('email_taken', translate(cfg.messages, 'errors.email_taken')));
382
+ return ctx.response
383
+ .status(409)
384
+ .send(apiErr('email_taken', translate(cfg.messages, 'errors.email_taken')));
355
385
  }
356
386
  await cfg.audit?.record({
357
387
  type: 'email_change.requested',
@@ -360,7 +390,7 @@ export default class AccountApiController {
360
390
  ip: ctx.request.ip?.() ?? null,
361
391
  });
362
392
  const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
363
- const confirmUrl = `${origin}/account/email/confirm?token=${encodeURIComponent(issued.token)}`;
393
+ const confirmUrl = `${origin}${accountPath('emailConfirm')}?token=${encodeURIComponent(issued.token)}`;
364
394
  if (cfg.mail?.onEmailChangeConfirm) {
365
395
  await cfg.mail.onEmailChangeConfirm({
366
396
  email: newEmail,
@@ -370,7 +400,11 @@ export default class AccountApiController {
370
400
  });
371
401
  }
372
402
  else if (cfg.mail?.onEmailVerification) {
373
- await cfg.mail.onEmailVerification({ email: newEmail, verifyUrl: confirmUrl, token: issued.token });
403
+ await cfg.mail.onEmailVerification({
404
+ email: newEmail,
405
+ verifyUrl: confirmUrl,
406
+ token: issued.token,
407
+ });
374
408
  }
375
409
  else {
376
410
  await sendEmailChangeConfirmationEmail(ctx, { email: newEmail, confirmUrl });
@@ -393,7 +427,9 @@ export default class AccountApiController {
393
427
  const store = cfg.accountStore;
394
428
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
395
429
  if (!supportsAccountSecurity(store)) {
396
- return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
430
+ return ctx.response
431
+ .status(422)
432
+ .send(apiErr('capability_unsupported', translate(cfg.messages, 'account.security.not_supported')));
397
433
  }
398
434
  const cancelFn = store.cancelEmailChange;
399
435
  if (typeof cancelFn === 'function') {
@@ -447,7 +483,9 @@ export default class AccountApiController {
447
483
  const sessionId = ctx.request.param('id');
448
484
  const adminSessions = new AdminSessionsService(service);
449
485
  if (!adminSessions.canList) {
450
- return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
486
+ return ctx.response
487
+ .status(422)
488
+ .send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
451
489
  }
452
490
  // Verifica que a sessão pertence à conta logada.
453
491
  const sessions = await adminSessions.listSessions(userId);
@@ -475,7 +513,9 @@ export default class AccountApiController {
475
513
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
476
514
  const adminSessions = new AdminSessionsService(service);
477
515
  if (!adminSessions.canList) {
478
- return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
516
+ return ctx.response
517
+ .status(422)
518
+ .send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
479
519
  }
480
520
  // Sem sessão OIDC corrente identificável aqui (o console de conta usa sessão
481
521
  // Adonis, não sessão OIDC). Revogamos TODAS as sessões OIDC da conta.
@@ -503,7 +543,9 @@ export default class AccountApiController {
503
543
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
504
544
  const adminSessions = new AdminSessionsService(service);
505
545
  if (!adminSessions.canList) {
506
- return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
546
+ return ctx.response
547
+ .status(422)
548
+ .send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
507
549
  }
508
550
  // Revogar todas as sessões OIDC + grants da conta.
509
551
  const result = await adminSessions.revokeAll(userId);
@@ -550,7 +592,9 @@ export default class AccountApiController {
550
592
  const clientId = ctx.request.param('clientId');
551
593
  const adminSessions = new AdminSessionsService(service);
552
594
  if (!adminSessions.canList) {
553
- return ctx.response.status(422).send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
595
+ return ctx.response
596
+ .status(422)
597
+ .send(apiErr('capability_unsupported', 'Session enumeration not supported by this adapter.'));
554
598
  }
555
599
  const result = await adminSessions.revokeClientGrants(userId, clientId);
556
600
  await cfg.audit?.record({
@@ -622,12 +666,16 @@ export default class AccountApiController {
622
666
  const cfg = service.config;
623
667
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
624
668
  if (!supportsPasskeys(cfg.accountStore)) {
625
- return ctx.response.status(422).send(apiErr('capability_unsupported', translate(cfg.messages, 'errors.passkeys_unavailable')));
669
+ return ctx.response
670
+ .status(422)
671
+ .send(apiErr('capability_unsupported', translate(cfg.messages, 'errors.passkeys_unavailable')));
626
672
  }
627
673
  const sudoSettings = await resolveRuntimeSettings(ctx);
628
674
  const sudoResult = await requireSudo(ctx, sudoSettings);
629
675
  if (sudoResult !== true) {
630
- return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
676
+ return ctx.response
677
+ .status(403)
678
+ .send(apiErr('sudo_required', 'Identity confirmation required.'));
631
679
  }
632
680
  const credentialId = ctx.request.param('id');
633
681
  await cfg.accountStore.removePasskey?.(userId, credentialId);
@@ -676,13 +724,17 @@ export default class AccountApiController {
676
724
  const service = await ctx.containerResolver.make('authkit.server');
677
725
  const cfg = service.config;
678
726
  if (!cfg.patStore) {
679
- return ctx.response.status(422).send(apiErr('capability_unsupported', 'PAT store not configured.'));
727
+ return ctx.response
728
+ .status(422)
729
+ .send(apiErr('capability_unsupported', 'PAT store not configured.'));
680
730
  }
681
731
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
682
732
  const sudoSettings = await resolveRuntimeSettings(ctx);
683
733
  const sudoResult = await requireSudo(ctx, sudoSettings);
684
734
  if (sudoResult !== true) {
685
- return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
735
+ return ctx.response
736
+ .status(403)
737
+ .send(apiErr('sudo_required', 'Identity confirmation required.'));
686
738
  }
687
739
  const name = ctx.request.input('name', 'Token').trim() || 'Token';
688
740
  const { token, pat } = await cfg.patStore.issue({ accountId: userId, name });
@@ -709,13 +761,17 @@ export default class AccountApiController {
709
761
  const service = await ctx.containerResolver.make('authkit.server');
710
762
  const cfg = service.config;
711
763
  if (!cfg.patStore) {
712
- return ctx.response.status(422).send(apiErr('capability_unsupported', 'PAT store not configured.'));
764
+ return ctx.response
765
+ .status(422)
766
+ .send(apiErr('capability_unsupported', 'PAT store not configured.'));
713
767
  }
714
768
  const userId = ctx.session.get(ACCOUNT_SESSION_KEY);
715
769
  const sudoSettings = await resolveRuntimeSettings(ctx);
716
770
  const sudoResult = await requireSudo(ctx, sudoSettings);
717
771
  if (sudoResult !== true) {
718
- return ctx.response.status(403).send(apiErr('sudo_required', 'Identity confirmation required.'));
772
+ return ctx.response
773
+ .status(403)
774
+ .send(apiErr('sudo_required', 'Identity confirmation required.'));
719
775
  }
720
776
  const patId = ctx.request.param('id');
721
777
  const revoked = await cfg.patStore.revoke(userId, patId);
@@ -1,7 +1,7 @@
1
- import type { OidcService } from "../provider/oidc_service.js";
2
- import type { ResolvedServerConfig } from "../define_config.js";
3
- import type { AuthAccount } from "../accounts/account_store.js";
4
- import type { DeletionActor } from "./account_deletion_service.js";
1
+ import type { AuthAccount } from '../accounts/account_store.js';
2
+ import type { ResolvedServerConfig } from '../define_config.js';
3
+ import type { OidcService } from '../provider/oidc_service.js';
4
+ import type { DeletionActor } from './account_deletion_service.js';
5
5
  /**
6
6
  * As 9 etapas do cascade de deleção de conta (LGPD/GDPR) extraídas em operações
7
7
  * DISCRETAS e IDEMPOTENTES. Cada uma é chamável individualmente — re-executar uma
@@ -1,6 +1,6 @@
1
- import { supportsAccountDeletion, supportsMfa, supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from "../accounts/account_store.js";
2
- import { AdminSessionsService } from "./admin_sessions_service.js";
3
- import { deleteAvatar } from "./avatar_storage.js";
1
+ import { supportsAccountDeletion, supportsMfa, supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from '../accounts/account_store.js';
2
+ import { AdminSessionsService } from './admin_sessions_service.js';
3
+ import { deleteAvatar } from './avatar_storage.js';
4
4
  /**
5
5
  * Carrega o snapshot da conta. Retorna null se a conta não existe (ou já foi
6
6
  * deletada) — idempotente: re-executar após a deleção da linha devolve null.
@@ -22,7 +22,7 @@ export async function snapshotAccount(cfg, accountId) {
22
22
  */
23
23
  export async function auditDeleted(cfg, snapshot, actor) {
24
24
  await cfg.audit?.record({
25
- type: "account.deleted",
25
+ type: 'account.deleted',
26
26
  accountId: snapshot.id,
27
27
  email: snapshot.email,
28
28
  actorId: actor.actorId,
@@ -99,7 +99,7 @@ export async function deleteAccountAvatar(cfg, accountId, avatarUrl) {
99
99
  }
100
100
  /** 8) Anonimiza o histórico de audit da conta (quando o sink suporta). */
101
101
  export async function anonymizeAudit(cfg, accountId) {
102
- if (cfg.audit && typeof cfg.audit.anonymizeAccount === "function") {
102
+ if (cfg.audit && typeof cfg.audit.anonymizeAccount === 'function') {
103
103
  const auditAnonymized = await cfg.audit.anonymizeAccount(accountId);
104
104
  return { auditAnonymized };
105
105
  }
@@ -1,4 +1,4 @@
1
- import type { OidcService } from "../provider/oidc_service.js";
1
+ import type { OidcService } from '../provider/oidc_service.js';
2
2
  /** Quem disparou a deleção (auditoria). 'self' = o próprio usuário; senão admin. */
3
3
  export interface DeletionActor {
4
4
  /** Id de quem agiu: o próprio user (self-service) ou o admin. null para admin-api. */
@@ -11,7 +11,7 @@ export interface DeletionActor {
11
11
  * - 'admin' → um admin pelo console HTML;
12
12
  * - 'admin-api' → via Admin REST API / SDK.
13
13
  */
14
- source: "self" | "admin" | "admin-api";
14
+ source: 'self' | 'admin' | 'admin-api';
15
15
  }
16
16
  /** Contagens do que foi removido no cascade (para auditoria/diagnóstico). */
17
17
  export interface DeletionResult {
@@ -1,5 +1,5 @@
1
- import { supportsAccountDeletion } from "../accounts/account_store.js";
2
- import { anonymizeAudit, auditDeleted, deleteAccountAvatar, deleteAccountRow, disableMfa, removeFromOrgs, removePasskeys, revokePats, revokeSessions, snapshotAccount, unlinkProviders, } from "./account_deletion_ops.js";
1
+ import { supportsAccountDeletion } from '../accounts/account_store.js';
2
+ import { anonymizeAudit, auditDeleted, deleteAccountAvatar, deleteAccountRow, disableMfa, removeFromOrgs, removePasskeys, revokePats, revokeSessions, snapshotAccount, unlinkProviders, } from './account_deletion_ops.js';
3
3
  /**
4
4
  * Orquestra a deleção COMPLETA de uma conta (LGPD/GDPR — "direito ao
5
5
  * esquecimento"), compartilhada entre o self-service (console de conta) e o admin
@@ -1,4 +1,4 @@
1
- import type { OidcService } from "../provider/oidc_service.js";
1
+ import type { OidcService } from '../provider/oidc_service.js';
2
2
  /** Payload de export de dados de uma conta (portabilidade — LGPD/GDPR). */
3
3
  export interface AccountExport {
4
4
  /** Versão do formato do export (para evolução futura). */
@@ -1,5 +1,5 @@
1
- import { supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from "../accounts/account_store.js";
2
- import { AdminSessionsService } from "./admin_sessions_service.js";
1
+ import { supportsOrganizations, supportsPasskeys, supportsProviderIdentity, } from '../accounts/account_store.js';
2
+ import { AdminSessionsService } from './admin_sessions_service.js';
3
3
  /** Limite de eventos de audit incluídos no export (evita payloads gigantes). */
4
4
  const AUDIT_EXPORT_LIMIT = 1000;
5
5
  /**
@@ -98,7 +98,7 @@ export class AccountExportService {
98
98
  }
99
99
  // Audit do próprio usuário (quando o sink suporta consulta).
100
100
  let auditLog = [];
101
- if (cfg.audit && typeof cfg.audit.list === "function") {
101
+ if (cfg.audit && typeof cfg.audit.list === 'function') {
102
102
  try {
103
103
  const page = await cfg.audit.list({
104
104
  subject: accountId,
@@ -110,9 +110,7 @@ export class AccountExportService {
110
110
  clientId: e.clientId ?? null,
111
111
  ip: e.ip ?? null,
112
112
  metadata: e.metadata ?? null,
113
- createdAt: typeof e.createdAt === "string"
114
- ? e.createdAt
115
- : (e.createdAt?.toISOString?.() ?? null),
113
+ createdAt: typeof e.createdAt === 'string' ? e.createdAt : (e.createdAt?.toISOString?.() ?? null),
116
114
  }));
117
115
  }
118
116
  catch {
@@ -1,11 +1,3 @@
1
- /**
2
- * Destino default da área da conta (pós-login do console sem `return_to`,
3
- * confirmações de e-mail, fallback de redirects).
4
- *
5
- * Configurável via `accountHome` no defineConfig; default '/account/security'
6
- * (a "minha conta" natural — perfil, senha, sessões). Nunca '/account/tokens':
7
- * aterrissar usuário comum numa tela de Personal Access Tokens é hostil.
8
- */
9
1
  export declare function accountHome(cfg: {
10
2
  accountHome?: string;
11
3
  }): string;
@@ -2,10 +2,12 @@
2
2
  * Destino default da área da conta (pós-login do console sem `return_to`,
3
3
  * confirmações de e-mail, fallback de redirects).
4
4
  *
5
- * Configurável via `accountHome` no defineConfig; default '/account/security'
6
- * (a "minha conta" natural — perfil, senha, sessões). Nunca '/account/tokens':
7
- * aterrissar usuário comum numa tela de Personal Access Tokens é hostil.
5
+ * Configurável via `accountHome` no defineConfig; default `accountPath('security')`
6
+ * (a "minha conta" natural — perfil, senha, sessões; `/account/security`, ou
7
+ * `/conta/seguranca` com overrides). Nunca a tela de tokens: aterrissar usuário
8
+ * comum numa tela de Personal Access Tokens é hostil.
8
9
  */
10
+ import { accountPath } from './account_paths.js';
9
11
  export function accountHome(cfg) {
10
- return cfg.accountHome ?? '/account/security';
12
+ return cfg.accountHome ?? accountPath('security');
11
13
  }
@@ -27,14 +27,17 @@
27
27
  * Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
28
28
  *
29
29
  * Não normaliza o path: aceita qualquer caminho interno que o host queira
30
- * (ex.: `'/login'`, `'/auth/entrar'`). Valor vazio/whitespace cai no default.
30
+ * (ex.: `'/login'`, `'/auth/entrar'`). Valor vazio/whitespace volta a derivar
31
+ * de `accountPath('login')`.
31
32
  *
32
33
  * @param url Caminho de destino do redirect de não-autenticado.
33
34
  */
34
35
  export declare function setAccountLoginUrl(url: string): void;
35
36
  /**
36
- * Retorna a URL de login do console de conta (default `'/account/login'`).
37
- * Usada por todo redirect/link de "faça login" da lib.
37
+ * Retorna a URL de login do console de conta. Sem override explícito, deriva de
38
+ * `accountPath('login')` (default `'/account/login'`; `'/conta/login'` com
39
+ * `accountRoutes: { prefix: '/conta' }`). Usada por todo redirect/link de
40
+ * "faça login" da lib.
38
41
  */
39
42
  export declare function getAccountLoginUrl(): string;
40
43
  /** Restaura o default — uso em testes (isola o singleton entre casos). */
@@ -22,29 +22,37 @@
22
22
  * ANTES da primeira request, quando o container ainda não existe. Um módulo ESM
23
23
  * é inicializado uma vez por processo — ideal para configuração imutável de boot.
24
24
  */
25
- const DEFAULT_ACCOUNT_LOGIN_URL = '/account/login';
26
- let _loginUrl = DEFAULT_ACCOUNT_LOGIN_URL;
25
+ import { accountPath } from './account_paths.js';
26
+ /**
27
+ * `undefined` = não configurado explicitamente → deriva de `accountPath('login')`
28
+ * (que respeita o prefixo/segmento configurados via `accountRoutes`). Default
29
+ * efetivo `'/account/login'` — back-compat total.
30
+ */
31
+ let _loginUrl;
27
32
  /**
28
33
  * Define a URL de login do console de conta para este processo.
29
34
  * Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
30
35
  *
31
36
  * Não normaliza o path: aceita qualquer caminho interno que o host queira
32
- * (ex.: `'/login'`, `'/auth/entrar'`). Valor vazio/whitespace cai no default.
37
+ * (ex.: `'/login'`, `'/auth/entrar'`). Valor vazio/whitespace volta a derivar
38
+ * de `accountPath('login')`.
33
39
  *
34
40
  * @param url Caminho de destino do redirect de não-autenticado.
35
41
  */
36
42
  export function setAccountLoginUrl(url) {
37
43
  const trimmed = (url ?? '').trim();
38
- _loginUrl = trimmed || DEFAULT_ACCOUNT_LOGIN_URL;
44
+ _loginUrl = trimmed || undefined;
39
45
  }
40
46
  /**
41
- * Retorna a URL de login do console de conta (default `'/account/login'`).
42
- * Usada por todo redirect/link de "faça login" da lib.
47
+ * Retorna a URL de login do console de conta. Sem override explícito, deriva de
48
+ * `accountPath('login')` (default `'/account/login'`; `'/conta/login'` com
49
+ * `accountRoutes: { prefix: '/conta' }`). Usada por todo redirect/link de
50
+ * "faça login" da lib.
43
51
  */
44
52
  export function getAccountLoginUrl() {
45
- return _loginUrl;
53
+ return _loginUrl ?? accountPath('login');
46
54
  }
47
55
  /** Restaura o default — uso em testes (isola o singleton entre casos). */
48
56
  export function resetAccountLoginUrl() {
49
- _loginUrl = DEFAULT_ACCOUNT_LOGIN_URL;
57
+ _loginUrl = undefined;
50
58
  }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Prefixo e segmentos de tela do console de conta (`/account/*`) — singleton de
3
+ * processo, configurável/localizável.
4
+ *
5
+ * Definidos em tempo de registro das rotas (`registerAuthHost`, via a opção
6
+ * `accountRoutes`) e lidos em runtime por TODOS os pontos que constroem uma URL
7
+ * do console de conta: o registro de rotas, os redirects dos controllers, as
8
+ * URLs absolutas dos e-mails transacionais, os guards de sudo e as views Edge
9
+ * (injetados como prop global `accountPaths` pelo renderer).
10
+ *
11
+ * Por que módulo singleton e não binding do container? Mesma razão do
12
+ * `admin_prefix` e do `account_login_url`: os guards e o registro de rotas são
13
+ * closures construídas em tempo de registro, ANTES da primeira request, quando
14
+ * o container do AdonisJS ainda não existe. Um módulo ESM é inicializado uma vez
15
+ * por processo — ideal para configuração imutável de boot.
16
+ *
17
+ * ── O que é configurável e o que NÃO é ──────────────────────────────────────
18
+ * Configuráveis: o PREFIXO (`/account` → `/conta`) e o SEGMENTO de cada TELA
19
+ * navegável (`security` → `seguranca`, `confirm` → `confirmar`, ...). São paths
20
+ * que o usuário vê na barra de endereço e que um host pode querer no idioma dele.
21
+ *
22
+ * NÃO configuráveis (de propósito): os action-subpaths dos POSTs de dentro de
23
+ * cada tela — `/password`, `/email`, `/enroll`, `/confirm`, `/disable`,
24
+ * `/passkeys/options`, `/passkeys/verify`, `/delete`, `/export`, o `/magic-link`
25
+ * e o `/passkey` das rotas de sudo, etc. São endpoints de formulário invisíveis
26
+ * ao usuário (ele nunca digita `/account/security/password`), servem apenas de
27
+ * alvo de `<form action>`/`fetch`, e mantê-los fixos evita multiplicar a
28
+ * superfície de configuração (e a de bugs) sem nenhum ganho de UX. Renomear a
29
+ * tela já cobre o caso real "quero /conta/seguranca". Idem para o segmento fixo
30
+ * `api` da JSON API (`{prefix}/api/*`): ele segue o prefixo mas nunca é
31
+ * traduzido — é contrato de máquina (authkit-react), não de humano.
32
+ */
33
+ /**
34
+ * Segmentos default de cada TELA navegável do console de conta. A chave é o
35
+ * identificador estável usado no código (`accountPath('security')`); o valor é
36
+ * o segmento de URL, sobrescrevível por `setAccountPaths`.
37
+ */
38
+ declare const DEFAULT_ACCOUNT_SEGMENTS: {
39
+ readonly login: "login";
40
+ readonly logout: "logout";
41
+ readonly security: "security";
42
+ readonly mfa: "mfa";
43
+ readonly confirm: "confirm";
44
+ readonly tokens: "tokens";
45
+ readonly apps: "apps";
46
+ readonly orgs: "orgs";
47
+ readonly emailConfirm: "email/confirm";
48
+ };
49
+ /** Chave de tela do console de conta (`login`, `security`, `confirm`, ...). */
50
+ export type AccountPathKey = keyof typeof DEFAULT_ACCOUNT_SEGMENTS;
51
+ /** Overrides aceitos por `setAccountPaths` / a opção `accountRoutes`. */
52
+ export interface AccountPathsOptions {
53
+ /** Prefixo base do console (ex.: `'/conta'`). Normalizado automaticamente. */
54
+ prefix?: string;
55
+ /** Segmentos de tela a sobrescrever (ex.: `{ security: 'seguranca' }`). */
56
+ paths?: Partial<Record<AccountPathKey, string>>;
57
+ }
58
+ /**
59
+ * Normaliza o prefixo: começa com '/', sem trailing slash. Mesma semântica de
60
+ * `normalizeAdminPrefix`. `'/'` → `'/'` (raiz) é improvável mas seguro.
61
+ */
62
+ export declare function normalizeAccountPrefix(raw: string): string;
63
+ /**
64
+ * Define o prefixo e/ou os segmentos de tela do console de conta para este
65
+ * processo. Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
66
+ *
67
+ * Só sobrescreve o que foi passado — chamada sem `prefix` mantém o prefixo
68
+ * atual, e segmentos ausentes de `paths` mantêm o default. Valores
69
+ * vazios/whitespace são ignorados (caem no default), nunca produzem um path
70
+ * quebrado.
71
+ *
72
+ * @example
73
+ * setAccountPaths({ prefix: '/conta', paths: { security: 'seguranca', confirm: 'confirmar' } })
74
+ * accountPath('security') // → '/conta/seguranca'
75
+ * accountPath('confirm') // → '/conta/confirmar'
76
+ */
77
+ export declare function setAccountPaths(opts: AccountPathsOptions | undefined): void;
78
+ /**
79
+ * Junta o prefixo do console de conta com um subpath arbitrário, tratando o
80
+ * prefixo raiz (`'/'`) como `''` na composição: sem isso o resultado seria
81
+ * `'//<sub>'`, que num `<form action>`/`fetch()` é URL protocol-relative (o
82
+ * browser lê `<sub>` como HOST, não como path).
83
+ *
84
+ * Base canônica de `accountPath()` e de QUALQUER outra composição
85
+ * prefixo+subpath fora das TELAS fixas (ex.: o `{prefix}/api` da JSON API em
86
+ * `register_auth_host`). Novas composições devem passar por aqui em vez de
87
+ * reimplementar `${accountPrefix()}/algo` — é exatamente essa duplicação que
88
+ * reproduz o bug do `//` quando o prefixo normaliza pra raiz.
89
+ *
90
+ * @example
91
+ * joinAccountPath('api') // default → '/account/api'
92
+ */
93
+ export declare function joinAccountPath(sub: string): string;
94
+ /**
95
+ * Retorna o path completo de uma TELA do console de conta (prefixo + segmento).
96
+ * Para os action-subpaths (POSTs de dentro da tela) concatene o subpath fixo:
97
+ * `accountPath('security') + '/password'`.
98
+ *
99
+ * @example
100
+ * accountPath('security') // default → '/account/security'
101
+ * accountPath('confirm') // default → '/account/confirm'
102
+ */
103
+ export declare function accountPath(key: AccountPathKey): string;
104
+ /**
105
+ * Retorna o prefixo BRUTO do console de conta (default `'/account'`; pode ser
106
+ * `'/'` na raiz). Para compor `{prefix}/algo` (ex.: a JSON API, cujo segmento
107
+ * `api` é fixo — contrato de máquina), use `joinAccountPath('algo')`: ela
108
+ * colapsa a raiz e evita o `//`. Este getter existe para os casos (ex.: os
109
+ * asserts de teste) que precisam do valor cru do prefixo.
110
+ */
111
+ export declare function accountPrefix(): string;
112
+ /**
113
+ * Mapa `{ chave → path completo }` de TODAS as telas. Injetado como prop global
114
+ * `accountPaths` nas views Edge, para que os `<form action>`/`fetch()` respeitem
115
+ * os overrides sem cada view conhecer o prefixo.
116
+ */
117
+ export declare function accountPathsMap(): Record<AccountPathKey, string>;
118
+ /** Restaura os defaults — uso em testes (isola o singleton entre casos). */
119
+ export declare function resetAccountPaths(): void;
120
+ export {};