@authowl/react 0.21.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -933,6 +933,159 @@ var init_FormError = __esm({
933
933
  }
934
934
  });
935
935
 
936
+ // src/components/captcha-loader.ts
937
+ var captcha_loader_exports = {};
938
+ __export(captcha_loader_exports, {
939
+ loadCaptcha: () => loadCaptcha
940
+ });
941
+ function providerGlobal(adapter) {
942
+ const api = window[adapter.globalName];
943
+ return typeof api?.render === "function" ? api : void 0;
944
+ }
945
+ function loadCaptcha(adapter) {
946
+ const available = providerGlobal(adapter);
947
+ if (available) return Promise.resolve(available);
948
+ const cached = loaders.get(adapter.scriptUrl);
949
+ if (cached) return cached;
950
+ const loading = new Promise((resolve, reject) => {
951
+ const existing = document.querySelector(
952
+ `script[src="${adapter.scriptUrl}"]`
953
+ );
954
+ const script = existing ?? document.createElement("script");
955
+ let poll;
956
+ const cleanup = () => {
957
+ clearTimeout(timeout);
958
+ if (poll !== void 0) clearInterval(poll);
959
+ script.removeEventListener("load", loaded);
960
+ script.removeEventListener("error", failed);
961
+ };
962
+ const resolveWhenPublished = () => {
963
+ const api = providerGlobal(adapter);
964
+ if (!api) return false;
965
+ cleanup();
966
+ resolve(api);
967
+ return true;
968
+ };
969
+ const loaded = () => {
970
+ if (!resolveWhenPublished() && poll === void 0) {
971
+ poll = setInterval(resolveWhenPublished, GLOBAL_POLL_INTERVAL_MS);
972
+ }
973
+ };
974
+ const failed = () => {
975
+ cleanup();
976
+ if (!existing) script.remove();
977
+ reject(new Error(`${adapter.label} failed to load`));
978
+ };
979
+ const timeout = setTimeout(failed, SCRIPT_LOAD_TIMEOUT_MS);
980
+ script.addEventListener("load", loaded, { once: true });
981
+ script.addEventListener("error", failed, { once: true });
982
+ if (existing) {
983
+ loaded();
984
+ } else {
985
+ script.src = adapter.scriptUrl;
986
+ script.async = true;
987
+ script.defer = true;
988
+ const nonce = document.querySelector("script[nonce]")?.nonce;
989
+ if (nonce) script.nonce = nonce;
990
+ document.head.appendChild(script);
991
+ }
992
+ });
993
+ const retryable = loading.catch((error) => {
994
+ loaders.delete(adapter.scriptUrl);
995
+ throw error;
996
+ });
997
+ loaders.set(adapter.scriptUrl, retryable);
998
+ return retryable;
999
+ }
1000
+ var SCRIPT_LOAD_TIMEOUT_MS, GLOBAL_POLL_INTERVAL_MS, loaders;
1001
+ var init_captcha_loader = __esm({
1002
+ "src/components/captcha-loader.ts"() {
1003
+ "use strict";
1004
+ "use client";
1005
+ SCRIPT_LOAD_TIMEOUT_MS = 1e4;
1006
+ GLOBAL_POLL_INTERVAL_MS = 25;
1007
+ loaders = /* @__PURE__ */ new Map();
1008
+ }
1009
+ });
1010
+
1011
+ // src/components/captcha-providers.ts
1012
+ var captcha_providers_exports = {};
1013
+ __export(captcha_providers_exports, {
1014
+ CAPTCHA_ADAPTERS: () => CAPTCHA_ADAPTERS,
1015
+ captchaAdapterFor: () => captchaAdapterFor
1016
+ });
1017
+ function preferredTheme() {
1018
+ return typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1019
+ }
1020
+ function teardown(api, widgetId) {
1021
+ if (api.remove) {
1022
+ try {
1023
+ api.remove(widgetId);
1024
+ return;
1025
+ } catch {
1026
+ }
1027
+ }
1028
+ try {
1029
+ api.reset?.(widgetId);
1030
+ } catch {
1031
+ }
1032
+ }
1033
+ function captchaAdapterFor(provider) {
1034
+ return CAPTCHA_ADAPTERS[provider] ?? null;
1035
+ }
1036
+ var CAPTCHA_ADAPTERS;
1037
+ var init_captcha_providers = __esm({
1038
+ "src/components/captcha-providers.ts"() {
1039
+ "use strict";
1040
+ "use client";
1041
+ CAPTCHA_ADAPTERS = {
1042
+ turnstile: {
1043
+ scriptUrl: "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",
1044
+ globalName: "turnstile",
1045
+ label: "Cloudflare Turnstile",
1046
+ invisibleRenderOptions: ({ siteKey, theme, action, language }) => ({
1047
+ sitekey: siteKey,
1048
+ theme,
1049
+ action,
1050
+ language,
1051
+ execution: "execute",
1052
+ appearance: "interaction-only",
1053
+ size: "flexible"
1054
+ }),
1055
+ teardown
1056
+ },
1057
+ hcaptcha: {
1058
+ scriptUrl: "https://js.hcaptcha.com/1/api.js?render=explicit",
1059
+ globalName: "hcaptcha",
1060
+ label: "hCaptcha",
1061
+ invisibleRenderOptions: ({ siteKey, theme }) => ({
1062
+ sitekey: siteKey,
1063
+ // 'auto' is Turnstile vocabulary. hCaptcha and reCAPTCHA take light|dark
1064
+ // only and silently fall back to light on anything else, which reads badly
1065
+ // in a dark application.
1066
+ theme: theme === "auto" ? preferredTheme() : theme,
1067
+ size: "invisible"
1068
+ }),
1069
+ teardown
1070
+ },
1071
+ "recaptcha-v2": {
1072
+ scriptUrl: "https://www.google.com/recaptcha/api.js?render=explicit",
1073
+ globalName: "grecaptcha",
1074
+ label: "reCAPTCHA",
1075
+ invisibleRenderOptions: ({ siteKey, theme }) => ({
1076
+ sitekey: siteKey,
1077
+ // 'auto' is Turnstile vocabulary. hCaptcha and reCAPTCHA take light|dark
1078
+ // only and silently fall back to light on anything else, which reads badly
1079
+ // in a dark application.
1080
+ theme: theme === "auto" ? preferredTheme() : theme,
1081
+ size: "invisible"
1082
+ }),
1083
+ teardown
1084
+ }
1085
+ };
1086
+ }
1087
+ });
1088
+
936
1089
  // ../../node_modules/.pnpm/qrcode-generator@2.0.4/node_modules/qrcode-generator/dist/qrcode.mjs
937
1090
  var qrcode, QRMode, QRErrorCorrectionLevel, QRMaskPattern, QRUtil, QRMath, qrPolynomial, QRRSBlock, qrBitBuffer, qrNumber, qrAlphaNum, qr8BitByte, qrKanji, byteArrayOutputStream, base64EncodeOutputStream, base64DecodeInputStream, gifImage, createDataURL, qrcode_default, stringToBytes;
938
1091
  var init_qrcode = __esm({
@@ -3430,110 +3583,26 @@ function PasskeyButton({ redirectTo, onSignedIn }) {
3430
3583
  }
3431
3584
 
3432
3585
  // src/components/ForgotPassword.tsx
3433
- var React11 = __toESM(require("react"), 1);
3586
+ var React10 = __toESM(require("react"), 1);
3434
3587
  init_hooks();
3435
3588
  init_i18n();
3436
3589
  init_use_submit_action();
3437
3590
  init_Spinner();
3438
3591
 
3439
3592
  // src/components/AuthChallenge.tsx
3440
- var React10 = __toESM(require("react"), 1);
3593
+ var React9 = __toESM(require("react"), 1);
3441
3594
  init_hooks();
3442
3595
  init_i18n();
3596
+ init_FormError();
3443
3597
 
3444
- // src/components/Turnstile.tsx
3445
- var React9 = __toESM(require("react"), 1);
3446
- var import_jsx_runtime10 = require("react/jsx-runtime");
3447
- var SCRIPT_URL = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
3448
- var SCRIPT_LOAD_TIMEOUT_MS = 1e4;
3449
- var LOCAL_TURNSTILE_TEST_TOKEN = "XXXX.DUMMY.TOKEN.XXXX";
3450
- var loader = null;
3451
- function loadTurnstile() {
3452
- if (window.turnstile) return Promise.resolve(window.turnstile);
3453
- if (loader) return loader;
3454
- loader = new Promise((resolve, reject) => {
3455
- const existing = document.querySelector(`script[src="${SCRIPT_URL}"]`);
3456
- const script = existing ?? document.createElement("script");
3457
- const cleanup = () => {
3458
- clearTimeout(timeout);
3459
- script.removeEventListener("load", loaded);
3460
- script.removeEventListener("error", failed);
3461
- };
3462
- const loaded = () => {
3463
- cleanup();
3464
- if (window.turnstile) {
3465
- resolve(window.turnstile);
3466
- } else {
3467
- reject(new Error("Turnstile unavailable"));
3468
- }
3469
- };
3470
- const failed = () => {
3471
- cleanup();
3472
- if (!existing) script.remove();
3473
- reject(new Error("Turnstile failed to load"));
3474
- };
3475
- const timeout = setTimeout(failed, SCRIPT_LOAD_TIMEOUT_MS);
3476
- script.addEventListener("load", loaded, { once: true });
3477
- script.addEventListener("error", failed, { once: true });
3478
- if (!existing) {
3479
- script.src = SCRIPT_URL;
3480
- script.async = true;
3481
- script.defer = true;
3482
- const nonce = document.querySelector("script[nonce]")?.nonce;
3483
- if (nonce) script.nonce = nonce;
3484
- document.head.appendChild(script);
3485
- }
3486
- }).catch((error) => {
3487
- loader = null;
3488
- throw error;
3489
- });
3490
- return loader;
3491
- }
3492
- function Turnstile({
3493
- siteKey,
3494
- theme,
3495
- onToken,
3496
- onUnavailable
3497
- }) {
3498
- const container = React9.useRef(null);
3499
- const callbacks = React9.useRef({ onToken, onUnavailable });
3500
- callbacks.current = { onToken, onUnavailable };
3501
- React9.useEffect(() => {
3502
- callbacks.current.onToken(null);
3503
- if (!siteKey) {
3504
- callbacks.current.onToken(LOCAL_TURNSTILE_TEST_TOKEN);
3505
- return;
3506
- }
3507
- let active = true;
3508
- let widget = null;
3509
- void loadTurnstile().then((api) => {
3510
- if (!active || !container.current) return;
3511
- const id = api.render(container.current, {
3512
- sitekey: siteKey,
3513
- theme: theme === "system" ? "auto" : theme,
3514
- size: "flexible",
3515
- callback: (token) => callbacks.current.onToken(token),
3516
- "expired-callback": () => callbacks.current.onToken(null),
3517
- "error-callback": () => {
3518
- callbacks.current.onToken(null);
3519
- callbacks.current.onUnavailable();
3520
- }
3521
- });
3522
- widget = { api, id };
3523
- }).catch(() => {
3524
- if (active) callbacks.current.onUnavailable();
3525
- });
3526
- return () => {
3527
- active = false;
3528
- if (widget) widget.api.remove(widget.id);
3529
- };
3530
- }, [siteKey, theme]);
3531
- if (!siteKey) return null;
3532
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ba-turnstile", ref: container, "data-testid": "phoneotp-turnstile" });
3598
+ // src/components/captcha-provider-ids.ts
3599
+ var CAPTCHA_PROVIDER_IDS = ["turnstile", "hcaptcha", "recaptcha-v2"];
3600
+ function isSupportedCaptchaProvider(provider) {
3601
+ return CAPTCHA_PROVIDER_IDS.includes(provider);
3533
3602
  }
3534
3603
 
3535
3604
  // src/components/AuthChallenge.tsx
3536
- var import_jsx_runtime11 = require("react/jsx-runtime");
3605
+ var import_jsx_runtime10 = require("react/jsx-runtime");
3537
3606
  var AUTH_CHALLENGE_ACTIONS = {
3538
3607
  signUp: "auth_signup",
3539
3608
  signIn: "auth_signin",
@@ -3548,38 +3617,53 @@ var challengeError = {
3548
3617
  code: "BOT_CHALLENGE_FAILED",
3549
3618
  message: "Human verification failed."
3550
3619
  };
3551
- function turnstileTheme(root, fallback) {
3620
+ function captchaTheme(root, fallback) {
3552
3621
  const rendered = root?.dataset.authowlTheme;
3553
3622
  if (rendered === "light" || rendered === "dark") return rendered;
3554
3623
  if (fallback === "light" || fallback === "dark") return fallback;
3555
3624
  return "auto";
3556
3625
  }
3557
3626
  function useAuthChallenge() {
3558
- const { config, isLoading } = usePublicConfig();
3627
+ const { config, isLoading, retry } = usePublicConfig();
3559
3628
  const t = useT();
3560
- const container = React10.useRef(null);
3561
- const active = React10.useRef(null);
3562
- const [status, setStatus] = React10.useState("idle");
3563
- const siteKey = config?.authTurnstileSiteKey ?? null;
3564
- const removeActive = React10.useCallback((reason) => {
3629
+ const container = React9.useRef(null);
3630
+ const active = React9.useRef(null);
3631
+ const [status, setStatus] = React9.useState("idle");
3632
+ const captcha = config?.captcha ?? null;
3633
+ const providerSupported = captcha ? isSupportedCaptchaProvider(captcha.provider) : false;
3634
+ const unavailableProvider = captcha && !providerSupported ? captcha.provider : null;
3635
+ const refetched = React9.useRef(null);
3636
+ const refetchIfConfigMayBeStale = React9.useCallback(() => {
3637
+ if (!config || refetched.current === config) return;
3638
+ refetched.current = config;
3639
+ retry();
3640
+ }, [config, retry]);
3641
+ const removeActive = React9.useCallback((reason) => {
3565
3642
  const current = active.current;
3566
3643
  active.current = null;
3567
3644
  if (!current) return;
3568
- current.api.remove(current.id);
3645
+ current.adapter.teardown(current.api, current.id);
3569
3646
  if (reason) current.reject(reason);
3570
3647
  }, []);
3571
- React10.useEffect(
3572
- () => () => removeActive(new Error("Turnstile challenge was cancelled")),
3648
+ React9.useEffect(
3649
+ () => () => removeActive(new Error("Captcha challenge was cancelled")),
3573
3650
  [removeActive]
3574
3651
  );
3575
- const tokenFor = React10.useCallback(
3652
+ const tokenFor = React9.useCallback(
3576
3653
  async (action) => {
3577
- if (!siteKey) return null;
3654
+ if (!captcha) return null;
3655
+ if (!providerSupported) throw new Error(`Unsupported captcha provider: ${captcha.provider}`);
3578
3656
  setStatus("checking");
3579
- removeActive(new Error("Turnstile challenge was replaced"));
3657
+ removeActive(new Error("Captcha challenge was replaced"));
3580
3658
  try {
3581
- const api = await loadTurnstile();
3582
- if (!container.current) throw new Error("Turnstile container unavailable");
3659
+ const [{ loadCaptcha: loadCaptcha2 }, { captchaAdapterFor: captchaAdapterFor2 }] = await Promise.all([
3660
+ Promise.resolve().then(() => (init_captcha_loader(), captcha_loader_exports)),
3661
+ Promise.resolve().then(() => (init_captcha_providers(), captcha_providers_exports))
3662
+ ]);
3663
+ const adapter = captchaAdapterFor2(captcha.provider);
3664
+ if (!adapter) throw new Error(`Unsupported captcha provider: ${captcha.provider}`);
3665
+ const api = await loadCaptcha2(adapter);
3666
+ if (!container.current) throw new Error("Captcha container unavailable");
3583
3667
  return await new Promise((resolve, reject) => {
3584
3668
  let settled = false;
3585
3669
  const finish = (outcome) => {
@@ -3587,27 +3671,29 @@ function useAuthChallenge() {
3587
3671
  settled = true;
3588
3672
  const current = active.current;
3589
3673
  active.current = null;
3590
- if (current) current.api.remove(current.id);
3674
+ if (current) current.adapter.teardown(current.api, current.id);
3591
3675
  if ("token" in outcome) resolve(outcome.token);
3592
3676
  else reject(outcome.error);
3593
3677
  };
3594
- const fail = () => finish({ error: new Error("Turnstile challenge failed") });
3678
+ const fail = () => finish({ error: new Error("Captcha challenge failed") });
3595
3679
  const root = container.current.closest(".authowl-root");
3680
+ const optionalFailureCallbacks = captcha.provider === "turnstile" ? {
3681
+ "timeout-callback": fail,
3682
+ "unsupported-callback": fail
3683
+ } : {};
3596
3684
  const id = api.render(container.current, {
3597
- sitekey: siteKey,
3598
- theme: turnstileTheme(root, config?.branding?.theme),
3599
- action,
3600
- execution: "execute",
3601
- appearance: "interaction-only",
3602
- size: "flexible",
3603
- language: root?.dataset.authowlLocale ?? config?.locale,
3685
+ ...adapter.invisibleRenderOptions({
3686
+ siteKey: captcha.siteKey,
3687
+ theme: captchaTheme(root, config?.branding?.theme),
3688
+ action,
3689
+ language: root?.dataset.authowlLocale ?? config?.locale
3690
+ }),
3604
3691
  callback: (token) => finish({ token }),
3605
3692
  "expired-callback": fail,
3606
3693
  "error-callback": fail,
3607
- "timeout-callback": fail,
3608
- "unsupported-callback": fail
3694
+ ...optionalFailureCallbacks
3609
3695
  });
3610
- active.current = { api, id, reject };
3696
+ active.current = { adapter, api, id, reject };
3611
3697
  try {
3612
3698
  api.execute(id);
3613
3699
  } catch {
@@ -3618,9 +3704,9 @@ function useAuthChallenge() {
3618
3704
  setStatus("idle");
3619
3705
  }
3620
3706
  },
3621
- [config?.branding?.theme, config?.locale, removeActive, siteKey]
3707
+ [captcha, config?.branding?.theme, config?.locale, providerSupported, removeActive]
3622
3708
  );
3623
- const run = React10.useCallback(
3709
+ const run = React9.useCallback(
3624
3710
  async (action, request) => {
3625
3711
  if (isLoading) {
3626
3712
  return {
@@ -3630,41 +3716,44 @@ function useAuthChallenge() {
3630
3716
  }
3631
3717
  try {
3632
3718
  const token = await tokenFor(action);
3633
- return await request(token ? { authChallengeToken: token } : void 0);
3719
+ const result = await request(token ? { authChallengeToken: token } : void 0);
3720
+ if (result?.error?.code === challengeError.code) refetchIfConfigMayBeStale();
3721
+ return result;
3634
3722
  } catch {
3635
3723
  setStatus("failed");
3724
+ refetchIfConfigMayBeStale();
3636
3725
  return {
3637
3726
  data: null,
3638
3727
  error: challengeError
3639
3728
  };
3640
3729
  }
3641
3730
  },
3642
- [isLoading, tokenFor]
3731
+ [isLoading, refetchIfConfigMayBeStale, tokenFor]
3643
3732
  );
3644
- const control = siteKey ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ba-auth-challenge", "data-testid": "auth-challenge", children: [
3645
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ba-turnstile", ref: container }),
3646
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ba-sr-only", role: "status", "aria-live": "polite", children: status === "checking" ? t("authChallenge.checking") : status === "failed" ? t("authChallenge.error.failed") : "" })
3733
+ const control = captcha ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ba-auth-challenge", "data-testid": "auth-challenge", children: [
3734
+ providerSupported ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ba-turnstile", ref: container }) : null,
3735
+ unavailableProvider ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FormError, { "data-testid": "auth-challenge-unsupported", children: t("authChallenge.error.unsupportedProvider", { provider: unavailableProvider }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ba-sr-only", role: "status", "aria-live": "polite", children: status === "checking" ? t("authChallenge.checking") : status === "failed" ? t("authChallenge.error.failed") : "" })
3647
3736
  ] }) : null;
3648
3737
  return { run, control, configPending: isLoading };
3649
3738
  }
3650
3739
 
3651
3740
  // src/components/ForgotPassword.tsx
3652
3741
  init_FormError();
3653
- var import_jsx_runtime12 = require("react/jsx-runtime");
3742
+ var import_jsx_runtime11 = require("react/jsx-runtime");
3654
3743
  function ForgotPassword({ resetPasswordUrl, onBack }) {
3655
3744
  const t = useT();
3656
3745
  const { requestPasswordReset } = usePasswordReset();
3657
3746
  const { pending, error, run } = useSubmitAction();
3658
3747
  const authChallenge = useAuthChallenge();
3659
- const [email, setEmail] = React11.useState("");
3660
- const [sent, setSent] = React11.useState(false);
3748
+ const [email, setEmail] = React10.useState("");
3749
+ const [sent, setSent] = React10.useState(false);
3661
3750
  if (sent) {
3662
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "ba-fields", "data-testid": "forgot-sent", children: [
3663
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "ba-muted", children: richMessage(t("forgotPassword.sent"), { email: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Bidi, { children: email }) }) }),
3664
- onBack && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
3751
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ba-fields", "data-testid": "forgot-sent", children: [
3752
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ba-muted", children: richMessage(t("forgotPassword.sent"), { email: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Bidi, { children: email }) }) }),
3753
+ onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
3665
3754
  ] });
3666
3755
  }
3667
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3756
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3668
3757
  "form",
3669
3758
  {
3670
3759
  method: "post",
@@ -3678,9 +3767,9 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
3678
3767
  });
3679
3768
  },
3680
3769
  children: [
3681
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "ba-label", children: [
3770
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: "ba-label", children: [
3682
3771
  t("common.emailLabel"),
3683
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3772
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3684
3773
  "input",
3685
3774
  {
3686
3775
  className: "ba-input",
@@ -3693,18 +3782,18 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
3693
3782
  )
3694
3783
  ] }),
3695
3784
  authChallenge.control,
3696
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FormError, { children: error }),
3697
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3785
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(FormError, { children: error }),
3786
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3698
3787
  "button",
3699
3788
  {
3700
3789
  className: "ba-button",
3701
3790
  type: "submit",
3702
3791
  disabled: pending || authChallenge.configPending,
3703
3792
  "aria-busy": pending || void 0,
3704
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Busy, { busy: pending, label: t("common.sending"), children: t("forgotPassword.submit") })
3793
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Busy, { busy: pending, label: t("common.sending"), children: t("forgotPassword.submit") })
3705
3794
  }
3706
3795
  ),
3707
- onBack && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
3796
+ onBack && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
3708
3797
  ]
3709
3798
  }
3710
3799
  );
@@ -3714,7 +3803,7 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
3714
3803
  init_i18n();
3715
3804
  init_Spinner();
3716
3805
  init_FormError();
3717
- var import_jsx_runtime13 = require("react/jsx-runtime");
3806
+ var import_jsx_runtime12 = require("react/jsx-runtime");
3718
3807
  function OtpCodeForm({
3719
3808
  email,
3720
3809
  code,
@@ -3725,7 +3814,7 @@ function OtpCodeForm({
3725
3814
  error
3726
3815
  }) {
3727
3816
  const t = useT();
3728
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3817
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3729
3818
  "form",
3730
3819
  {
3731
3820
  method: "post",
@@ -3736,9 +3825,9 @@ function OtpCodeForm({
3736
3825
  onSubmit();
3737
3826
  },
3738
3827
  children: [
3739
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ba-label", children: [
3740
- richMessage(t("emailOtp.codeLabel"), { email: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Bidi, { children: email }) }),
3741
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3828
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "ba-label", children: [
3829
+ richMessage(t("emailOtp.codeLabel"), { email: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Bidi, { children: email }) }),
3830
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3742
3831
  "input",
3743
3832
  {
3744
3833
  className: "ba-input",
@@ -3751,29 +3840,29 @@ function OtpCodeForm({
3751
3840
  }
3752
3841
  )
3753
3842
  ] }),
3754
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FormError, { children: error }),
3755
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "ba-button", type: "submit", disabled: pending, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Busy, { busy: pending, label: t("common.verifying"), children: t("emailOtp.verifySubmit") }) }),
3756
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ba-link-button", onClick: onChangeEmail, children: t("emailOtp.changeEmail") })
3843
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FormError, { children: error }),
3844
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "ba-button", type: "submit", disabled: pending, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Busy, { busy: pending, label: t("common.verifying"), children: t("emailOtp.verifySubmit") }) }),
3845
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "ba-link-button", onClick: onChangeEmail, children: t("emailOtp.changeEmail") })
3757
3846
  ]
3758
3847
  }
3759
3848
  );
3760
3849
  }
3761
3850
 
3762
3851
  // src/components/MFAChallenge.tsx
3763
- var React12 = __toESM(require("react"), 1);
3852
+ var React11 = __toESM(require("react"), 1);
3764
3853
  init_hooks();
3765
3854
  init_i18n();
3766
3855
  init_use_submit_action();
3767
3856
  init_Spinner();
3768
3857
  init_FormError();
3769
- var import_jsx_runtime14 = require("react/jsx-runtime");
3858
+ var import_jsx_runtime13 = require("react/jsx-runtime");
3770
3859
  function MFAChallenge({ onVerified, allowTrustDevice = true }) {
3771
3860
  const t = useT();
3772
3861
  const { verifyTotp, verifyBackupCode, sendOtp, verifyOtp } = useMFA();
3773
3862
  const { pending, error, setError, run } = useSubmitAction();
3774
- const [mode, setMode] = React12.useState("totp");
3775
- const [code, setCode] = React12.useState("");
3776
- const [trustDevice, setTrustDevice] = React12.useState(false);
3863
+ const [mode, setMode] = React11.useState("totp");
3864
+ const [code, setCode] = React11.useState("");
3865
+ const [trustDevice, setTrustDevice] = React11.useState(false);
3777
3866
  const switchMode = (next) => {
3778
3867
  setMode(next);
3779
3868
  setCode("");
@@ -3796,12 +3885,12 @@ function MFAChallenge({ onVerified, allowTrustDevice = true }) {
3796
3885
  };
3797
3886
  const hint = mode === "totp" ? t("mfa.challenge.totpHint") : mode === "backup" ? t("mfa.challenge.backupHint") : t("mfa.challenge.otpHint");
3798
3887
  const label2 = mode === "totp" ? t("mfa.challenge.totpLabel") : mode === "backup" ? t("mfa.challenge.backupLabel") : t("mfa.challenge.otpLabel");
3799
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("form", { method: "post", className: "ba-fields", "data-testid": "mfa-challenge", onSubmit: submit, children: [
3800
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "ba-title", children: t("mfa.challenge.title") }),
3801
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "ba-muted", children: hint }),
3802
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "ba-label", children: [
3888
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { method: "post", className: "ba-fields", "data-testid": "mfa-challenge", onSubmit: submit, children: [
3889
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "ba-title", children: t("mfa.challenge.title") }),
3890
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "ba-muted", children: hint }),
3891
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ba-label", children: [
3803
3892
  label2,
3804
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3893
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3805
3894
  "input",
3806
3895
  {
3807
3896
  className: "ba-input",
@@ -3817,28 +3906,28 @@ function MFAChallenge({ onVerified, allowTrustDevice = true }) {
3817
3906
  }
3818
3907
  )
3819
3908
  ] }),
3820
- mode !== "backup" && allowTrustDevice && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "ba-consent ba-consent-centered", children: [
3821
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "ba-checkbox-control", children: [
3822
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { className: "ba-checkbox", type: "checkbox", checked: trustDevice, onChange: (e) => setTrustDevice(e.target.checked) }),
3823
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ba-checkbox-check" }) })
3909
+ mode !== "backup" && allowTrustDevice && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { className: "ba-consent ba-consent-centered", children: [
3910
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "ba-checkbox-control", children: [
3911
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("input", { className: "ba-checkbox", type: "checkbox", checked: trustDevice, onChange: (e) => setTrustDevice(e.target.checked) }),
3912
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ba-checkbox-check" }) })
3824
3913
  ] }),
3825
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: t("mfa.challenge.trustDevice", { days: 30 }) })
3914
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t("mfa.challenge.trustDevice", { days: 30 }) })
3826
3915
  ] }),
3827
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FormError, { children: error }),
3828
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.challenge.submit") }) }),
3829
- mode !== "totp" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("totp"), children: t("mfa.challenge.useTotp") }),
3830
- mode !== "backup" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("backup"), children: t("mfa.challenge.useBackup") }),
3831
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ba-link-button", disabled: pending, onClick: requestOtp, children: t(mode === "otp" ? "mfa.challenge.resendOtp" : "mfa.challenge.useEmailOtp") })
3916
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FormError, { children: error }),
3917
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.challenge.submit") }) }),
3918
+ mode !== "totp" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("totp"), children: t("mfa.challenge.useTotp") }),
3919
+ mode !== "backup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("backup"), children: t("mfa.challenge.useBackup") }),
3920
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ba-link-button", disabled: pending, onClick: requestOtp, children: t(mode === "otp" ? "mfa.challenge.resendOtp" : "mfa.challenge.useEmailOtp") })
3832
3921
  ] });
3833
3922
  }
3834
3923
 
3835
3924
  // src/components/mfa-enrollment-step.tsx
3836
- var React15 = __toESM(require("react"), 1);
3925
+ var React14 = __toESM(require("react"), 1);
3837
3926
  init_hooks();
3838
3927
  init_i18n();
3839
3928
 
3840
3929
  // src/components/MFAEnrollment.tsx
3841
- var React14 = __toESM(require("react"), 1);
3930
+ var React13 = __toESM(require("react"), 1);
3842
3931
  init_hooks();
3843
3932
  init_i18n();
3844
3933
  init_use_submit_action();
@@ -3851,13 +3940,13 @@ function shouldDiscardSetup(args) {
3851
3940
  }
3852
3941
 
3853
3942
  // src/components/QrCode.tsx
3854
- var React13 = __toESM(require("react"), 1);
3943
+ var React12 = __toESM(require("react"), 1);
3855
3944
  init_i18n();
3856
- var import_jsx_runtime15 = require("react/jsx-runtime");
3945
+ var import_jsx_runtime14 = require("react/jsx-runtime");
3857
3946
  function QrCode({ value, size = 200 }) {
3858
3947
  const t = useT();
3859
- const [path, setPath] = React13.useState(null);
3860
- React13.useEffect(() => {
3948
+ const [path, setPath] = React12.useState(null);
3949
+ React12.useEffect(() => {
3861
3950
  let active = true;
3862
3951
  setPath(null);
3863
3952
  void Promise.resolve().then(() => (init_qr(), qr_exports)).then(
@@ -3875,7 +3964,7 @@ function QrCode({ value, size = 200 }) {
3875
3964
  if (!path) return null;
3876
3965
  const margin = 4;
3877
3966
  const dim = path.count + margin * 2;
3878
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3967
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3879
3968
  "svg",
3880
3969
  {
3881
3970
  width: size,
@@ -3886,8 +3975,8 @@ function QrCode({ value, size = 200 }) {
3886
3975
  shapeRendering: "crispEdges",
3887
3976
  className: "ba-qr",
3888
3977
  children: [
3889
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { width: dim, height: dim, fill: "#ffffff" }),
3890
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("g", { transform: `translate(${margin} ${margin})`, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: path.d, fill: "#000000" }) })
3978
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("rect", { width: dim, height: dim, fill: "#ffffff" }),
3979
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("g", { transform: `translate(${margin} ${margin})`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: path.d, fill: "#000000" }) })
3891
3980
  ]
3892
3981
  }
3893
3982
  );
@@ -3895,7 +3984,7 @@ function QrCode({ value, size = 200 }) {
3895
3984
 
3896
3985
  // src/components/MFAEnrollment.tsx
3897
3986
  init_FormError();
3898
- var import_jsx_runtime16 = require("react/jsx-runtime");
3987
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3899
3988
  function secretFromUri(uri) {
3900
3989
  try {
3901
3990
  return new URL(uri).searchParams.get("secret");
@@ -3908,11 +3997,11 @@ function MFAEnrollment({ onEnrolled, title }) {
3908
3997
  const { enable, verifyTotp } = useMFA();
3909
3998
  const { user, isLoaded } = useUser();
3910
3999
  const { pending, error, run } = useSubmitAction();
3911
- const [password, setPassword] = React14.useState("");
3912
- const [setup, setSetup] = React14.useState(null);
3913
- const [code, setCode] = React14.useState("");
3914
- const [done, setDone] = React14.useState(false);
3915
- const setupOwnerId = React14.useRef(null);
4000
+ const [password, setPassword] = React13.useState("");
4001
+ const [setup, setSetup] = React13.useState(null);
4002
+ const [code, setCode] = React13.useState("");
4003
+ const [done, setDone] = React13.useState(false);
4004
+ const setupOwnerId = React13.useRef(null);
3916
4005
  const userId = user?.id ?? null;
3917
4006
  const stale = shouldDiscardSetup({
3918
4007
  isLoaded,
@@ -3921,7 +4010,7 @@ function MFAEnrollment({ onEnrolled, title }) {
3921
4010
  currentUserId: userId
3922
4011
  });
3923
4012
  const activeSetup = stale ? null : setup;
3924
- React14.useEffect(() => {
4013
+ React13.useEffect(() => {
3925
4014
  if (stale) {
3926
4015
  setSetup(null);
3927
4016
  setPassword("");
@@ -3929,22 +4018,22 @@ function MFAEnrollment({ onEnrolled, title }) {
3929
4018
  }
3930
4019
  }, [stale]);
3931
4020
  if (done) {
3932
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ba-fields", "data-testid": "mfa-enroll-done", children: [
3933
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "ba-title", children: t("mfa.enroll.doneTitle") }),
3934
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.doneBody") })
4021
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ba-fields", "data-testid": "mfa-enroll-done", children: [
4022
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "ba-title", children: t("mfa.enroll.doneTitle") }),
4023
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.doneBody") })
3935
4024
  ] });
3936
4025
  }
3937
4026
  if (!activeSetup) {
3938
4027
  if (!isLoaded) {
3939
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ba-fields", "data-testid": "mfa-enroll-loading", "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ba-skeleton" }) });
4028
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ba-fields", "data-testid": "mfa-enroll-loading", "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ba-skeleton" }) });
3940
4029
  }
3941
4030
  if (user?.twoFactorEnabled) {
3942
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ba-fields", "data-testid": "mfa-enroll-active", children: [
3943
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "ba-title", children: t("mfa.enroll.activeTitle") }),
3944
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.activeBody") })
4031
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ba-fields", "data-testid": "mfa-enroll-active", children: [
4032
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "ba-title", children: t("mfa.enroll.activeTitle") }),
4033
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.activeBody") })
3945
4034
  ] });
3946
4035
  }
3947
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4036
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3948
4037
  "form",
3949
4038
  {
3950
4039
  method: "post",
@@ -3961,11 +4050,11 @@ function MFAEnrollment({ onEnrolled, title }) {
3961
4050
  });
3962
4051
  },
3963
4052
  children: [
3964
- title !== null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "ba-title", children: title ?? t("mfa.enroll.title") }),
3965
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.passwordHint") }),
3966
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "ba-label", children: [
4053
+ title !== null && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "ba-title", children: title ?? t("mfa.enroll.title") }),
4054
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.passwordHint") }),
4055
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "ba-label", children: [
3967
4056
  t("common.passwordLabel"),
3968
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4057
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3969
4058
  "input",
3970
4059
  {
3971
4060
  className: "ba-input",
@@ -3977,29 +4066,29 @@ function MFAEnrollment({ onEnrolled, title }) {
3977
4066
  }
3978
4067
  )
3979
4068
  ] }),
3980
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(FormError, { children: error }),
3981
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { className: "ba-button", type: "submit", disabled: pending || !password, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Busy, { busy: pending, label: t("mfa.enroll.startPending"), children: t("common.continue") }) })
4069
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FormError, { children: error }),
4070
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { className: "ba-button", type: "submit", disabled: pending || !password, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Busy, { busy: pending, label: t("mfa.enroll.startPending"), children: t("common.continue") }) })
3982
4071
  ]
3983
4072
  }
3984
4073
  );
3985
4074
  }
3986
4075
  const secret = secretFromUri(activeSetup.totpURI);
3987
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ba-fields", "data-testid": "mfa-enroll-verify", children: [
3988
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "ba-title", children: t("mfa.enroll.scanQrTitle") }),
3989
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.scanQrHint") }),
3990
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(QrCode, { value: activeSetup.totpURI }),
3991
- secret && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "ba-muted", children: richMessage(t("mfa.enroll.manualKey"), {
3992
- secret: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("code", { className: "ba-code", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Bidi, { children: secret }) })
4076
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ba-fields", "data-testid": "mfa-enroll-verify", children: [
4077
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "ba-title", children: t("mfa.enroll.scanQrTitle") }),
4078
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ba-muted", children: t("mfa.enroll.scanQrHint") }),
4079
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(QrCode, { value: activeSetup.totpURI }),
4080
+ secret && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ba-muted", children: richMessage(t("mfa.enroll.manualKey"), {
4081
+ secret: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "ba-code", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Bidi, { children: secret }) })
3993
4082
  }) }),
3994
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
3995
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "ba-muted", children: [
3996
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: t("mfa.enroll.backupCodesWarningStrong") }),
4083
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
4084
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "ba-muted", children: [
4085
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: t("mfa.enroll.backupCodesWarningStrong") }),
3997
4086
  " ",
3998
4087
  t("mfa.enroll.backupCodesWarningRest")
3999
4088
  ] }),
4000
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "ba-backup-codes", "data-testid": "mfa-backup-codes", children: activeSetup.backupCodes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("code", { className: "ba-code", children: c }) }, c)) })
4089
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ul", { className: "ba-backup-codes", "data-testid": "mfa-backup-codes", children: activeSetup.backupCodes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "ba-code", children: c }) }, c)) })
4001
4090
  ] }),
4002
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4091
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4003
4092
  "form",
4004
4093
  {
4005
4094
  method: "post",
@@ -4017,9 +4106,9 @@ function MFAEnrollment({ onEnrolled, title }) {
4017
4106
  });
4018
4107
  },
4019
4108
  children: [
4020
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "ba-label", children: [
4109
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "ba-label", children: [
4021
4110
  t("mfa.enroll.codeLabel"),
4022
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4111
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4023
4112
  "input",
4024
4113
  {
4025
4114
  className: "ba-input",
@@ -4031,8 +4120,8 @@ function MFAEnrollment({ onEnrolled, title }) {
4031
4120
  }
4032
4121
  )
4033
4122
  ] }),
4034
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(FormError, { children: error }),
4035
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.enroll.activateSubmit") }) })
4123
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FormError, { children: error }),
4124
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.enroll.activateSubmit") }) })
4036
4125
  ]
4037
4126
  }
4038
4127
  )
@@ -4040,13 +4129,13 @@ function MFAEnrollment({ onEnrolled, title }) {
4040
4129
  }
4041
4130
 
4042
4131
  // src/components/mfa-enrollment-step.tsx
4043
- var import_jsx_runtime17 = require("react/jsx-runtime");
4132
+ var import_jsx_runtime16 = require("react/jsx-runtime");
4044
4133
  function useConfirmedMfaPending(onUnknown = "pending") {
4045
4134
  const client = useAuthClient();
4046
4135
  const { needsMfaEnrollment } = useUser();
4047
4136
  const { refetch } = useSession();
4048
- const [confirmed, setConfirmed] = React15.useState(null);
4049
- React15.useEffect(() => {
4137
+ const [confirmed, setConfirmed] = React14.useState(null);
4138
+ React14.useEffect(() => {
4050
4139
  if (!needsMfaEnrollment) {
4051
4140
  setConfirmed(null);
4052
4141
  return;
@@ -4070,32 +4159,32 @@ function useConfirmedMfaPending(onUnknown = "pending") {
4070
4159
  function MfaEnrollmentStep({ title }) {
4071
4160
  const t = useT();
4072
4161
  const { refetch } = useSession();
4073
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4074
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h2", { className: "ba-title", children: title ?? t("mfaGate.title") }),
4075
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "ba-muted", children: t("mfaGate.body") }),
4076
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(MFAEnrollment, { onEnrolled: () => refetch({ query: { disableCookieCache: true } }) })
4162
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4163
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "ba-title", children: title ?? t("mfaGate.title") }),
4164
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "ba-muted", children: t("mfaGate.body") }),
4165
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MFAEnrollment, { onEnrolled: () => refetch({ query: { disableCookieCache: true } }) })
4077
4166
  ] });
4078
4167
  }
4079
4168
 
4080
4169
  // src/components/AuthOwlBadge.tsx
4081
4170
  init_hooks();
4082
4171
  init_i18n();
4083
- var import_jsx_runtime18 = require("react/jsx-runtime");
4172
+ var import_jsx_runtime17 = require("react/jsx-runtime");
4084
4173
  function OwlMark() {
4085
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("svg", { className: "ba-badge-mark", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: [
4086
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "12", cy: "12", r: "11", fill: "#F5B84C" }),
4087
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "8.4", cy: "10", r: "3", fill: "#fff" }),
4088
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "15.6", cy: "10", r: "3", fill: "#fff" }),
4089
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "8.4", cy: "10", r: "1.4", fill: "#1f1300" }),
4090
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: "15.6", cy: "10", r: "1.4", fill: "#1f1300" }),
4091
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("path", { d: "M12 13.1l1.5 2.1h-3z", fill: "#1f1300" })
4174
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { className: "ba-badge-mark", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: [
4175
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "12", cy: "12", r: "11", fill: "#F5B84C" }),
4176
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "8.4", cy: "10", r: "3", fill: "#fff" }),
4177
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "15.6", cy: "10", r: "3", fill: "#fff" }),
4178
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "8.4", cy: "10", r: "1.4", fill: "#1f1300" }),
4179
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("circle", { cx: "15.6", cy: "10", r: "1.4", fill: "#1f1300" }),
4180
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M12 13.1l1.5 2.1h-3z", fill: "#1f1300" })
4092
4181
  ] });
4093
4182
  }
4094
4183
  function AuthOwlBadge({ href = "https://authowl.dev", force } = {}) {
4095
4184
  const t = useT();
4096
4185
  const { config } = usePublicConfig();
4097
4186
  if (!force && !config?.badge) return null;
4098
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4187
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
4099
4188
  "a",
4100
4189
  {
4101
4190
  className: "ba-badge",
@@ -4104,9 +4193,9 @@ function AuthOwlBadge({ href = "https://authowl.dev", force } = {}) {
4104
4193
  rel: "noopener noreferrer",
4105
4194
  "data-testid": "authowl-badge",
4106
4195
  children: [
4107
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OwlMark, {}),
4108
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: richMessage(t("badge.securedBy"), {
4109
- brand: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ba-badge-brand", children: "AuthOwl" })
4196
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(OwlMark, {}),
4197
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: richMessage(t("badge.securedBy"), {
4198
+ brand: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ba-badge-brand", children: "AuthOwl" })
4110
4199
  }) })
4111
4200
  ]
4112
4201
  }
@@ -4127,19 +4216,19 @@ init_i18n();
4127
4216
 
4128
4217
  // src/components/ConsentDocLinks.tsx
4129
4218
  init_i18n();
4130
- var import_jsx_runtime19 = require("react/jsx-runtime");
4219
+ var import_jsx_runtime18 = require("react/jsx-runtime");
4131
4220
  function ConsentDocLinks({ termsUrl, privacyUrl }) {
4132
4221
  const t = useT();
4133
4222
  if (!termsUrl && !privacyUrl) return null;
4134
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
4135
- termsUrl && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href: termsUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.termsOfService") }),
4223
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4224
+ termsUrl && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { href: termsUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.termsOfService") }),
4136
4225
  termsUrl && privacyUrl && t("consent.docJoiner"),
4137
- privacyUrl && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href: privacyUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.privacyPolicy") })
4226
+ privacyUrl && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("a", { href: privacyUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.privacyPolicy") })
4138
4227
  ] });
4139
4228
  }
4140
4229
 
4141
4230
  // src/components/LegalConsentCheckbox.tsx
4142
- var import_jsx_runtime20 = require("react/jsx-runtime");
4231
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4143
4232
  function LegalConsentCheckbox({
4144
4233
  legal,
4145
4234
  accepted,
@@ -4148,9 +4237,9 @@ function LegalConsentCheckbox({
4148
4237
  }) {
4149
4238
  const t = useT();
4150
4239
  if (!legal.required) return null;
4151
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { className: "ba-consent", "data-testid": testId, children: [
4152
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "ba-checkbox-control", children: [
4153
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4240
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "ba-consent", "data-testid": testId, children: [
4241
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "ba-checkbox-control", children: [
4242
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4154
4243
  "input",
4155
4244
  {
4156
4245
  className: "ba-checkbox",
@@ -4159,14 +4248,69 @@ function LegalConsentCheckbox({
4159
4248
  onChange: (event) => onAcceptedChange(event.target.checked)
4160
4249
  }
4161
4250
  ),
4162
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ba-checkbox-check" }) })
4251
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ba-checkbox-check" }) })
4163
4252
  ] }),
4164
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: richMessage(t("signUp.consentLabel"), {
4165
- links: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ConsentDocLinks, { termsUrl: legal.termsUrl, privacyUrl: legal.privacyUrl })
4253
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: richMessage(t("signUp.consentLabel"), {
4254
+ links: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConsentDocLinks, { termsUrl: legal.termsUrl, privacyUrl: legal.privacyUrl })
4166
4255
  }) })
4167
4256
  ] });
4168
4257
  }
4169
4258
 
4259
+ // src/components/Turnstile.tsx
4260
+ var React15 = __toESM(require("react"), 1);
4261
+ var import_jsx_runtime20 = require("react/jsx-runtime");
4262
+ var LOCAL_TURNSTILE_TEST_TOKEN = "XXXX.DUMMY.TOKEN.XXXX";
4263
+ async function loadTurnstile() {
4264
+ const [{ loadCaptcha: loadCaptcha2 }, { CAPTCHA_ADAPTERS: CAPTCHA_ADAPTERS2 }] = await Promise.all([
4265
+ Promise.resolve().then(() => (init_captcha_loader(), captcha_loader_exports)),
4266
+ Promise.resolve().then(() => (init_captcha_providers(), captcha_providers_exports))
4267
+ ]);
4268
+ const adapter = CAPTCHA_ADAPTERS2.turnstile;
4269
+ return { api: await loadCaptcha2(adapter), adapter };
4270
+ }
4271
+ function Turnstile({
4272
+ siteKey,
4273
+ theme,
4274
+ onToken,
4275
+ onUnavailable
4276
+ }) {
4277
+ const container = React15.useRef(null);
4278
+ const callbacks = React15.useRef({ onToken, onUnavailable });
4279
+ callbacks.current = { onToken, onUnavailable };
4280
+ React15.useEffect(() => {
4281
+ callbacks.current.onToken(null);
4282
+ if (!siteKey) {
4283
+ callbacks.current.onToken(LOCAL_TURNSTILE_TEST_TOKEN);
4284
+ return;
4285
+ }
4286
+ let active = true;
4287
+ let widget = null;
4288
+ void loadTurnstile().then(({ api, adapter }) => {
4289
+ if (!active || !container.current) return;
4290
+ const id = api.render(container.current, {
4291
+ sitekey: siteKey,
4292
+ theme: theme === "system" ? "auto" : theme,
4293
+ size: "flexible",
4294
+ callback: (token) => callbacks.current.onToken(token),
4295
+ "expired-callback": () => callbacks.current.onToken(null),
4296
+ "error-callback": () => {
4297
+ callbacks.current.onToken(null);
4298
+ callbacks.current.onUnavailable();
4299
+ }
4300
+ });
4301
+ widget = { api, adapter, id };
4302
+ }).catch(() => {
4303
+ if (active) callbacks.current.onUnavailable();
4304
+ });
4305
+ return () => {
4306
+ active = false;
4307
+ if (widget) widget.adapter.teardown(widget.api, widget.id);
4308
+ };
4309
+ }, [siteKey, theme]);
4310
+ if (!siteKey) return null;
4311
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "ba-turnstile", ref: container, "data-testid": "phoneotp-turnstile" });
4312
+ }
4313
+
4170
4314
  // src/components/PhoneOTP.tsx
4171
4315
  init_use_submit_action();
4172
4316
  init_Spinner();
@@ -6671,9 +6815,9 @@ function formatSessionTime(date, locale) {
6671
6815
  // src/components/user-profile/use-account-resource.ts
6672
6816
  var React38 = __toESM(require("react"), 1);
6673
6817
  init_i18n();
6674
- function useAccountResource(loader2, failure) {
6675
- const loaderRef = React38.useRef(loader2);
6676
- loaderRef.current = loader2;
6818
+ function useAccountResource(loader, failure) {
6819
+ const loaderRef = React38.useRef(loader);
6820
+ loaderRef.current = loader;
6677
6821
  const failureRef = React38.useRef(failure);
6678
6822
  failureRef.current = failure;
6679
6823
  const toServerError = useServerError();