@lalternative/auth 0.6.0 → 0.7.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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/use-session.ts","../src/components/auth-field.tsx","../src/components/auth-submit.tsx","../src/components/login-form.tsx","../src/components/social-buttons.tsx","../src/components/register-form.tsx","../src/components/verify-email-form.tsx","../src/components/forgot-password-form.tsx","../src/components/reset-password-form.tsx","../src/components/auth-layout.tsx","../src/components/invitation-notice.tsx"],"sourcesContent":["import type { PlatformAuthClient } from \"../client\"\n\n/**\n * Returns a useSession hook bound to the given auth client.\n * Usage: const { data: session, isPending } = useSession(authClient)\n */\nexport function useSession(authClient: PlatformAuthClient) {\n return authClient.useSession()\n}\n\n/**\n * Returns a logout function bound to the given auth client.\n * Usage: const logout = useLogout(authClient)\n */\nexport function useLogout(authClient: PlatformAuthClient) {\n const signOut = async () => {\n await authClient.signOut()\n }\n return signOut\n}\n","import { useId, useState, type InputHTMLAttributes, type ReactNode } from \"react\"\n\ntype NativeProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"id\" | \"className\">\n\nexport interface AuthFieldProps extends NativeProps {\n label: string\n /** Rendered on the right of the label row — typically a \"forgot password\" link */\n hint?: ReactNode\n /** Persistent helper text under the field, tied to it for screen readers */\n description?: string\n invalid?: boolean\n}\n\n/**\n * A single credential field.\n *\n * The label is a real <label>, always visible, never a placeholder standing in\n * for one: a placeholder disappears the moment someone types, which is exactly\n * when a person double-checking a long password needs to know what the box is.\n *\n * Touch targets are 52px tall on mobile and 44px from sm up. Below ~48px,\n * thumbs miss; on a pointer device the same height reads as oversized, so the\n * two are not one compromise value.\n *\n * The focus ring is a ring rather than an outline so it follows the rounded\n * corners exactly, and the border darkens at the same time — the border is what\n * survives forced-colors mode, where the ring is dropped.\n */\nexport function AuthField({\n label,\n hint,\n description,\n invalid = false,\n ...props\n}: AuthFieldProps) {\n const id = useId()\n const descriptionId = description ? `${id}-description` : undefined\n const [revealed, setRevealed] = useState(false)\n\n const isPassword = props.type === \"password\"\n const type = isPassword && revealed ? \"text\" : props.type\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <label\n htmlFor={id}\n className=\"text-sm font-medium leading-none text-foreground\"\n >\n {label}\n </label>\n {hint}\n </div>\n\n <div className=\"relative\">\n <input\n {...props}\n id={id}\n type={type}\n aria-invalid={invalid || undefined}\n aria-describedby={descriptionId}\n className={[\n \"h-[52px] w-full rounded-xl border bg-background px-4 text-base\",\n \"sm:h-11 sm:text-sm\",\n isPassword ? \"pr-12\" : \"\",\n \"text-foreground placeholder:text-muted-foreground/70\",\n \"transition-[border-color,box-shadow] duration-150 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-[3px]\",\n invalid\n ? \"border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20\"\n : \"border-input focus-visible:border-ring focus-visible:ring-ring/15\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ]\n .filter(Boolean)\n .join(\" \")}\n />\n\n {isPassword && (\n <button\n type=\"button\"\n onClick={() => setRevealed((v) => !v)}\n disabled={props.disabled}\n aria-pressed={revealed}\n aria-label={\n revealed ? \"Masquer le mot de passe\" : \"Afficher le mot de passe\"\n }\n className=\"absolute inset-y-0 right-0 flex w-12 items-center justify-center rounded-r-xl text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-60\"\n >\n <EyeIcon open={revealed} />\n </button>\n )}\n </div>\n\n {description && (\n <p id={descriptionId} className=\"text-xs text-muted-foreground\">\n {description}\n </p>\n )}\n </div>\n )\n}\n\n// Inline rather than from lucide-react: the package would gain a dependency\n// consumers already have at a different version, for two paths.\nfunction EyeIcon({ open }: { open: boolean }) {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0Z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n {!open && <path d=\"m3 3 18 18\" />}\n </svg>\n )\n}\n","import type { ReactNode } from \"react\"\n\ninterface AuthSubmitProps {\n pending?: boolean\n disabled?: boolean\n pendingLabel: string\n children: ReactNode\n}\n\n/**\n * The primary action of an auth screen.\n *\n * The label swaps to its pending form in place, with the spinner absolutely\n * positioned: a spinner inserted into the flow would widen the row and shift\n * the text sideways at the exact moment the person is watching it.\n *\n * The press feedback is a 1px translate rather than a scale — scaling a\n * full-width button visibly blurs its text mid-transform.\n */\nexport function AuthSubmit({\n pending = false,\n disabled = false,\n pendingLabel,\n children,\n}: AuthSubmitProps) {\n return (\n <button\n type=\"submit\"\n disabled={disabled || pending}\n aria-busy={pending || undefined}\n className={[\n \"relative flex h-[52px] w-full items-center justify-center rounded-xl sm:h-11\",\n \"bg-primary text-base font-medium text-primary-foreground sm:text-sm\",\n \"shadow-sm transition-[background-color,transform,box-shadow] duration-150 ease-out\",\n \"hover:bg-primary/90 active:translate-y-px\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:pointer-events-none disabled:opacity-55\",\n ].join(\" \")}\n >\n {pending && (\n <span\n aria-hidden=\"true\"\n className=\"absolute left-4 size-4 animate-spin rounded-full border-2 border-current border-t-transparent opacity-70 motion-reduce:animate-none\"\n />\n )}\n {pending ? pendingLabel : children}\n </button>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { LoginFormLabels, LoginFormProps } from \"../types\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthSubmit } from \"./auth-submit\"\nimport { SocialButtons } from \"./social-buttons\"\n\nconst DEFAULTS: Required<LoginFormLabels> = {\n title: \"Connexion\",\n subtitle: \"Content de te revoir. Entre tes identifiants pour continuer.\",\n emailPlaceholder: \"Adresse e-mail\",\n passwordPlaceholder: \"Mot de passe\",\n forgotPassword: \"Mot de passe oublié ?\",\n submit: \"Se connecter\",\n submitPending: \"Connexion…\",\n noAccount: \"Pas encore de compte ?\",\n register: \"Créer un compte\",\n emailRequired: \"Renseigne ton adresse e-mail\",\n passwordRequired: \"Renseigne ton mot de passe\",\n invalidCredentials: \"Adresse e-mail ou mot de passe incorrect\",\n // accountLinking is disabled in createPlatformAuth, so a social sign-in on an\n // address already registered with a password is refused with this code.\n // Without a message the button reads as broken rather than as a rejection.\n accountNotLinked:\n \"Cette adresse est déjà associée à un mot de passe. Connecte-toi avec ton mot de passe.\",\n socialCancelled: \"Connexion annulée.\",\n socialFailed: \"La connexion a échoué. Réessaie.\",\n}\n\nexport function LoginForm({\n onSuccess,\n registerUrl = \"/register\",\n forgotPasswordUrl = \"/forgot-password\",\n socialCallbackUrl = \"/\",\n socialProviders = [],\n coreTokenUrl = \"/api/auth/core-token\",\n labels,\n authClient,\n}: LoginFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [email, setEmail] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [error, setError] = useState<string | undefined>()\n const [isPending, setIsPending] = useState(false)\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!email.trim()) {\n setError(t.emailRequired)\n return\n }\n if (!password) {\n setError(t.passwordRequired)\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.signIn.email({\n email: email.trim(),\n password,\n })\n if (res?.error) {\n setError(res.error.message ?? t.invalidCredentials)\n return\n }\n // The better-auth session cookie alone does not authenticate a Go core:\n // it verifies the EdDSA JWT minted here against the issuer's JWKS.\n // Skipped when the app sets the token itself, or has no core at all.\n if (coreTokenUrl) {\n await fetch(coreTokenUrl, { credentials: \"include\" })\n }\n onSuccess?.()\n } catch (err) {\n setError(err instanceof Error ? err.message : t.invalidCredentials)\n } finally {\n setIsPending(false)\n }\n }\n\n const handleSocial = async (provider: \"google\" | \"github\") => {\n setError(undefined)\n try {\n await authClient.signIn.social({\n provider,\n callbackURL: socialCallbackUrl,\n })\n } catch (err) {\n const code = err instanceof Error ? err.message : \"\"\n setError(\n code === \"account_not_linked\"\n ? t.accountNotLinked\n : code === \"access_denied\"\n ? t.socialCancelled\n : t.socialFailed,\n )\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <header className=\"space-y-1.5\">\n <h1 className=\"text-[26px] font-semibold leading-tight tracking-[-0.02em] text-foreground sm:text-2xl\">\n {t.title}\n </h1>\n <p className=\"text-balance text-sm leading-relaxed text-muted-foreground\">\n {t.subtitle}\n </p>\n </header>\n\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-xl border border-destructive/25 bg-destructive/10 px-3.5 py-3 text-sm leading-snug text-destructive\"\n >\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-5\" noValidate>\n <AuthField\n label={t.emailPlaceholder}\n type=\"email\"\n inputMode=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"email\"\n autoCapitalize=\"none\"\n spellCheck={false}\n invalid={!!error}\n />\n\n <AuthField\n label={t.passwordPlaceholder}\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"current-password\"\n invalid={!!error}\n hint={\n <a\n href={forgotPasswordUrl}\n className=\"rounded text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.forgotPassword}\n </a>\n }\n />\n\n <AuthSubmit\n pending={isPending}\n disabled={!email.trim() || !password}\n pendingLabel={t.submitPending}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <SocialButtons\n providers={socialProviders}\n onSelect={handleSocial}\n disabled={isPending}\n />\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.noAccount}{\" \"}\n <a\n href={registerUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.register}\n </a>\n </p>\n </div>\n )\n}\n","const LABELS: Record<string, string> = {\n google: \"Continuer avec Google\",\n github: \"Continuer avec GitHub\",\n}\n\ninterface SocialButtonsProps {\n providers: Array<\"google\" | \"github\">\n onSelect: (provider: \"google\" | \"github\") => void | Promise<void>\n disabled?: boolean\n /** Divider text between the password form and the providers */\n separator?: string\n /** Per-provider button copy, merged over the French defaults */\n labels?: Partial<Record<\"google\" | \"github\", string>>\n}\n\nexport function SocialButtons({\n providers,\n onSelect,\n disabled = false,\n separator = \"ou\",\n labels,\n}: SocialButtonsProps) {\n if (providers.length === 0) return null\n\n const copy = { ...LABELS, ...labels }\n\n return (\n <div className=\"space-y-4\">\n {/* The rule is two flex segments rather than a line behind an opaque\n label: an opaque background only hides the rule when it matches the\n surface behind it, which breaks the moment this sits on a card. */}\n <div aria-hidden=\"true\" className=\"flex items-center gap-3\">\n <span className=\"h-px flex-1 bg-border\" />\n <span className=\"text-[11px] uppercase tracking-[0.14em] text-muted-foreground\">\n {separator}\n </span>\n <span className=\"h-px flex-1 bg-border\" />\n </div>\n\n <div className=\"space-y-2.5\">\n {providers.map((provider) => (\n <button\n key={provider}\n type=\"button\"\n onClick={() => onSelect(provider)}\n disabled={disabled}\n className={[\n \"flex h-[52px] w-full items-center justify-center gap-2.5 rounded-xl sm:h-11\",\n \"border border-input bg-background text-base font-medium text-foreground sm:text-sm\",\n \"transition-[background-color,border-color,transform] duration-150 ease-out\",\n \"hover:border-foreground/20 hover:bg-accent active:translate-y-px\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:pointer-events-none disabled:opacity-55\",\n ].join(\" \")}\n >\n <ProviderMark provider={provider} />\n {copy[provider] ?? provider}\n </button>\n ))}\n </div>\n </div>\n )\n}\n\n// Brand marks are inlined: they must keep their own colors (Google's mark is\n// unusable in monochrome) and adding an icon dependency to this package would\n// duplicate one every consumer already ships.\nfunction ProviderMark({ provider }: { provider: \"google\" | \"github\" }) {\n if (provider === \"google\") {\n return (\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 18 18\" aria-hidden=\"true\">\n <path\n fill=\"#4285F4\"\n d=\"M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.72v2.26h2.92c1.7-1.57 2.68-3.88 2.68-6.62Z\"\n />\n <path\n fill=\"#34A853\"\n d=\"M9 18c2.43 0 4.47-.8 5.96-2.18l-2.92-2.26c-.8.54-1.84.86-3.04.86-2.34 0-4.32-1.58-5.02-3.7H.96v2.33A9 9 0 0 0 9 18Z\"\n />\n <path\n fill=\"#FBBC05\"\n d=\"M3.98 10.72a5.4 5.4 0 0 1 0-3.44V4.95H.96a9 9 0 0 0 0 8.1l3.02-2.33Z\"\n />\n <path\n fill=\"#EA4335\"\n d=\"M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.58C13.46.9 11.43 0 9 0A9 9 0 0 0 .96 4.95l3.02 2.33C4.68 5.16 6.66 3.58 9 3.58Z\"\n />\n </svg>\n )\n }\n return (\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M8 0a8 8 0 0 0-2.53 15.59c.4.07.55-.17.55-.38l-.01-1.49c-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.4 7.4 0 0 1 4 0c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48l-.01 2.2c0 .21.15.46.55.38A8 8 0 0 0 8 0Z\" />\n </svg>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { RegisterFormLabels, RegisterFormProps } from \"../types\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthSubmit } from \"./auth-submit\"\nimport { SocialButtons } from \"./social-buttons\"\n\nconst MIN_PASSWORD_LENGTH = 8\n\nconst DEFAULTS: Required<RegisterFormLabels> = {\n title: \"Créer un compte\",\n subtitle: \"Nous t'enverrons un code pour confirmer ton adresse e-mail.\",\n namePlaceholder: \"Nom complet\",\n emailPlaceholder: \"Adresse e-mail\",\n passwordPlaceholder: \"Mot de passe\",\n passwordHint: `Au moins ${MIN_PASSWORD_LENGTH} caractères.`,\n submit: \"Créer mon compte\",\n submitPending: \"Création…\",\n haveAccount: \"Tu as déjà un compte ?\",\n login: \"Se connecter\",\n nameRequired: \"Renseigne ton nom\",\n emailRequired: \"Renseigne ton adresse e-mail\",\n passwordTooShort: `Le mot de passe doit faire au moins ${MIN_PASSWORD_LENGTH} caractères`,\n signUpFailed: \"La création du compte a échoué\",\n}\n\nexport function RegisterForm({\n onSuccess,\n loginUrl = \"/login\",\n legal,\n socialCallbackUrl = \"/\",\n socialProviders = [],\n labels,\n authClient,\n}: RegisterFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [name, setName] = useState(\"\")\n const [email, setEmail] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [error, setError] = useState<string | undefined>()\n const [isPending, setIsPending] = useState(false)\n\n const tooShort = password.length > 0 && password.length < MIN_PASSWORD_LENGTH\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!name.trim()) {\n setError(t.nameRequired)\n return\n }\n if (!email.trim()) {\n setError(t.emailRequired)\n return\n }\n if (password.length < MIN_PASSWORD_LENGTH) {\n setError(t.passwordTooShort)\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.signUp.email({\n name: name.trim(),\n email: email.trim(),\n password,\n })\n if (res?.error) {\n setError(res.error.message ?? t.signUpFailed)\n return\n }\n // createPlatformAuth sets requireEmailVerification, so sign-up leaves the\n // account unverified and without a session: the caller routes to the OTP\n // step rather than into the app.\n onSuccess?.(email.trim())\n } catch (err) {\n setError(err instanceof Error ? err.message : t.signUpFailed)\n } finally {\n setIsPending(false)\n }\n }\n\n const handleSocial = async (provider: \"google\" | \"github\") => {\n setError(undefined)\n try {\n await authClient.signIn.social({\n provider,\n callbackURL: socialCallbackUrl,\n })\n } catch (err) {\n setError(err instanceof Error ? err.message : t.signUpFailed)\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <header className=\"space-y-1.5\">\n <h1 className=\"text-[26px] font-semibold leading-tight tracking-[-0.02em] text-foreground sm:text-2xl\">\n {t.title}\n </h1>\n <p className=\"text-balance text-sm leading-relaxed text-muted-foreground\">\n {t.subtitle}\n </p>\n </header>\n\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-xl border border-destructive/25 bg-destructive/10 px-3.5 py-3 text-sm leading-snug text-destructive\"\n >\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-5\" noValidate>\n <AuthField\n label={t.namePlaceholder}\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"name\"\n />\n\n <AuthField\n label={t.emailPlaceholder}\n type=\"email\"\n inputMode=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"email\"\n autoCapitalize=\"none\"\n spellCheck={false}\n />\n\n <AuthField\n label={t.passwordPlaceholder}\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"new-password\"\n description={t.passwordHint}\n // Only once they have typed something: flagging an untouched field\n // red would scold someone for not having started yet.\n invalid={tooShort}\n />\n\n <AuthSubmit pending={isPending} pendingLabel={t.submitPending}>\n {t.submit}\n </AuthSubmit>\n\n {legal && (\n <p className=\"text-center text-xs leading-relaxed text-muted-foreground\">\n {legal}\n </p>\n )}\n </form>\n\n <SocialButtons\n providers={socialProviders}\n onSelect={handleSocial}\n disabled={isPending}\n />\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.haveAccount}{\" \"}\n <a\n href={loginUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.login}\n </a>\n </p>\n </div>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { VerifyEmailFormProps } from \"../types\"\n\nexport function VerifyEmailForm({\n email,\n onSuccess,\n authClient,\n}: VerifyEmailFormProps) {\n const [otp, setOtp] = useState(\"\")\n const [isVerifying, setIsVerifying] = useState(false)\n const [isResending, setIsResending] = useState(false)\n const [resendMessage, setResendMessage] = useState<string | undefined>()\n const [error, setError] = useState<string | undefined>()\n\n const handleVerify = async (e: FormEvent) => {\n e.preventDefault()\n if (!otp.trim() || otp.length < 6) {\n setError(\"Please enter the 6-digit code\")\n return\n }\n setError(undefined)\n setIsVerifying(true)\n try {\n const res = await authClient.emailOtp.verifyEmail({ email, otp })\n console.log(\"[verify-email] response:\", JSON.stringify(res?.data), \"error:\", JSON.stringify(res?.error))\n if (res?.error) {\n setError(res.error.message ?? \"Invalid code. Please try again.\")\n return\n }\n onSuccess?.()\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Invalid code. Please try again.\")\n } finally {\n setIsVerifying(false)\n }\n }\n\n const handleResend = async () => {\n if (!email) {\n setError(\"Email address is not available. Please register again.\")\n return\n }\n setError(undefined)\n setResendMessage(undefined)\n setIsResending(true)\n try {\n await authClient.emailOtp.sendVerificationOtp({\n email,\n type: \"email-verification\",\n })\n setResendMessage(\"A new code has been sent to your inbox.\")\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to resend code.\")\n } finally {\n setIsResending(false)\n }\n }\n\n return (\n <div className=\"space-y-8\">\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight\">\n Verify your email\n </h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {email ? (\n <>\n Enter the 6-digit code sent to{\" \"}\n <span className=\"font-medium text-foreground\">{email}</span>\n </>\n ) : (\n \"Enter the 6-digit code sent to your email\"\n )}\n </p>\n </div>\n\n {error && (\n <div className=\"rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive\">\n {error}\n </div>\n )}\n\n {resendMessage && (\n <div className=\"rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-xs text-green-700\">\n {resendMessage}\n </div>\n )}\n\n <form onSubmit={handleVerify} className=\"space-y-4\" noValidate>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n maxLength={6}\n value={otp}\n onChange={(e) => setOtp(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"000000\"\n required\n disabled={isVerifying}\n autoComplete=\"one-time-code\"\n className=\"flex h-12 w-full rounded-md border border-input bg-background px-3 py-2 text-center text-lg tracking-[0.4em] font-mono ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n\n <button\n type=\"submit\"\n disabled={isVerifying || otp.length < 6}\n className=\"inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {isVerifying ? \"Verifying...\" : \"Verify email\"}\n </button>\n\n <button\n type=\"button\"\n onClick={handleResend}\n disabled={isResending}\n className=\"inline-flex h-11 w-full items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n {isResending ? \"Sending...\" : \"Resend code\"}\n </button>\n </form>\n\n <p className=\"text-center text-sm text-muted-foreground\">\n Already verified?{\" \"}\n <a\n href=\"/login\"\n className=\"font-medium text-foreground underline underline-offset-4 hover:text-foreground/80\"\n >\n Sign in\n </a>\n </p>\n </div>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { ForgotPasswordFormProps } from \"../types\"\n\nexport function ForgotPasswordForm({\n onSuccess,\n loginUrl = \"/login\",\n authClient,\n}: ForgotPasswordFormProps) {\n const [email, setEmail] = useState(\"\")\n const [error, setError] = useState<string | undefined>()\n const [isPending, setIsPending] = useState(false)\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!email.trim()) {\n setError(\"Please enter your email address\")\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.emailOtp.sendVerificationOtp({\n email,\n type: \"forget-password\",\n })\n if (res?.error) {\n setError(res.error.message ?? \"Failed to send reset code\")\n return\n }\n onSuccess?.(email)\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to send reset code\")\n } finally {\n setIsPending(false)\n }\n }\n\n return (\n <div className=\"space-y-8\">\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight\">\n Forgot your password?\n </h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n Enter your email address and we'll send you a code to reset your\n password.\n </p>\n </div>\n\n {error && (\n <div className=\"rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive\">\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-4\" noValidate>\n <input\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"Email address\"\n required\n disabled={isPending}\n autoComplete=\"email\"\n className=\"flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n\n <button\n type=\"submit\"\n disabled={isPending || !email.trim()}\n className=\"inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {isPending ? \"Sending...\" : \"Send reset code\"}\n </button>\n </form>\n\n <p className=\"text-center text-sm text-muted-foreground\">\n Remember your password?{\" \"}\n <a\n href={loginUrl}\n className=\"font-medium text-foreground underline underline-offset-4 hover:text-foreground/80\"\n >\n Sign in\n </a>\n </p>\n </div>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { ResetPasswordFormProps } from \"../types\"\n\nexport function ResetPasswordForm({\n email,\n onSuccess,\n loginUrl = \"/login\",\n authClient,\n}: ResetPasswordFormProps) {\n const [otp, setOtp] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [confirmPassword, setConfirmPassword] = useState(\"\")\n const [error, setError] = useState<string | undefined>()\n const [isResetting, setIsResetting] = useState(false)\n const [isResending, setIsResending] = useState(false)\n const [resendMessage, setResendMessage] = useState<string | undefined>()\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!otp.trim() || otp.length < 6) {\n setError(\"Please enter the 6-digit code\")\n return\n }\n if (password.length < 8) {\n setError(\"Password must be at least 8 characters\")\n return\n }\n if (password !== confirmPassword) {\n setError(\"Passwords do not match\")\n return\n }\n setError(undefined)\n setIsResetting(true)\n try {\n const res = await fetch(\"/api/auth/email-otp/reset-password\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, otp, password }),\n })\n if (!res.ok) {\n const body = await res.json().catch(() => null)\n setError(body?.message ?? \"Failed to reset password\")\n return\n }\n onSuccess?.()\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to reset password\",\n )\n } finally {\n setIsResetting(false)\n }\n }\n\n const handleResend = async () => {\n setError(undefined)\n setResendMessage(undefined)\n setIsResending(true)\n try {\n await authClient.emailOtp.sendVerificationOtp({\n email,\n type: \"forget-password\",\n })\n setResendMessage(\"A new code has been sent to your inbox.\")\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to resend code.\")\n } finally {\n setIsResending(false)\n }\n }\n\n return (\n <div className=\"space-y-8\">\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight\">\n Reset your password\n </h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n Enter the 6-digit code sent to{\" \"}\n <span className=\"font-medium text-foreground\">{email}</span> and your\n new password.\n </p>\n </div>\n\n {error && (\n <div className=\"rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive\">\n {error}\n </div>\n )}\n\n {resendMessage && (\n <div className=\"rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-xs text-green-700\">\n {resendMessage}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-4\" noValidate>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n maxLength={6}\n value={otp}\n onChange={(e) => setOtp(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"000000\"\n required\n disabled={isResetting}\n autoComplete=\"one-time-code\"\n className=\"flex h-12 w-full rounded-md border border-input bg-background px-3 py-2 text-center text-lg tracking-[0.4em] font-mono ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n\n <input\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"New password\"\n required\n disabled={isResetting}\n autoComplete=\"new-password\"\n className=\"flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n\n <input\n type=\"password\"\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n placeholder=\"Confirm new password\"\n required\n disabled={isResetting}\n autoComplete=\"new-password\"\n className=\"flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n\n <button\n type=\"submit\"\n disabled={isResetting || otp.length < 6 || !password}\n className=\"inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {isResetting ? \"Resetting...\" : \"Reset password\"}\n </button>\n\n <button\n type=\"button\"\n onClick={handleResend}\n disabled={isResending}\n className=\"inline-flex h-11 w-full items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n {isResending ? \"Sending...\" : \"Resend code\"}\n </button>\n </form>\n\n <p className=\"text-center text-sm text-muted-foreground\">\n Remember your password?{\" \"}\n <a\n href={loginUrl}\n className=\"font-medium text-foreground underline underline-offset-4 hover:text-foreground/80\"\n >\n Sign in\n </a>\n </p>\n </div>\n )\n}\n","import type { AuthLayoutProps } from \"../types\"\n\n/**\n * The frame every auth screen sits in.\n *\n * Mobile and desktop are two layouts, not one scaled down. On a phone the form\n * IS the page: no card, no border, edge-to-edge padding, top-aligned so the\n * fields stay above the keyboard instead of being pushed under it by vertical\n * centering. From sm up it becomes a bounded card on a tinted ground — a\n * full-width form on a 1440px display is unreadable. It stays top-aligned\n * there too: vertical centering drops a short form to the middle of a tall\n * window, well below where the eye lands.\n *\n * min-h-dvh rather than min-h-screen: on mobile browsers 100vh includes the\n * retracting URL bar, so a screen-height container overflows by its height.\n */\nexport function AuthLayout({\n logo,\n title,\n subtitle,\n children,\n footer,\n}: AuthLayoutProps) {\n return (\n <div className=\"flex min-h-dvh flex-col bg-background px-5 pb-10 pt-12 sm:items-center sm:bg-muted/40 sm:px-6 sm:py-16\">\n <div className=\"mx-auto w-full max-w-[420px]\">\n <div className=\"mb-7 text-center sm:mb-6\">\n {logo && <div className=\"mb-5 flex justify-center\">{logo}</div>}\n <p className=\"text-sm font-medium tracking-[0.02em] text-foreground\">\n {title}\n </p>\n {subtitle && (\n <p className=\"mt-1 text-balance text-sm text-muted-foreground\">\n {subtitle}\n </p>\n )}\n </div>\n\n <div className=\"sm:rounded-2xl sm:border sm:border-border/70 sm:bg-card sm:p-8 sm:shadow-lg sm:shadow-black/5\">\n {children}\n </div>\n\n {footer && (\n <div className=\"mt-6 text-center text-xs leading-relaxed text-muted-foreground\">\n {footer}\n </div>\n )}\n </div>\n </div>\n )\n}\n","import type { InvitationNoticeProps } from \"../types\"\n\n/**\n * Every app is reachable at contact@ its own apex domain, so the address is\n * derived rather than configured. Sub-domains are stripped because the app is\n * routinely served from app./admin. while the mailbox lives on the apex;\n * multi-part public suffixes (.co.uk) would need a real suffix list and no app\n * using this is on one.\n */\nfunction defaultSupportEmail(): string | undefined {\n if (typeof window === \"undefined\") return undefined\n const host = window.location.hostname\n if (!host || host === \"localhost\" || /^[\\d.]+$/.test(host)) return undefined\n const apex = host.split(\".\").slice(-2).join(\".\")\n return `contact@${apex}`\n}\n\nconst REASON_MESSAGE: Record<string, string> = {\n expired: \"Cette invitation a expiré.\",\n claimed: \"Cette invitation a déjà été utilisée.\",\n unknown: \"Ce lien d'invitation n'est pas valide.\",\n}\n\n/**\n * What an invitee sees when their link does not work.\n *\n * It names the reason instead of merging the cases into one message: \"expired\"\n * tells someone their link was real and that asking for another one is worth\n * it, which \"invalid\" does not. Short TTLs make that distinction routine.\n *\n * The only way forward offered is a mailto, deliberately. A self-service\n * \"request a new invitation\" form would mean a public endpoint accepting dead\n * tokens, a queue to moderate, and a way to probe which tokens once existed —\n * for a flow where the operator already knows the person by name.\n */\nexport function InvitationNotice({\n reason = \"unknown\",\n supportEmail,\n title = \"Invitation indisponible\",\n action,\n}: InvitationNoticeProps) {\n const contact = supportEmail ?? defaultSupportEmail()\n\n return (\n <div className=\"space-y-8\">\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {REASON_MESSAGE[reason] ?? REASON_MESSAGE.unknown}\n </p>\n </div>\n\n {contact ? (\n <p className=\"text-sm text-muted-foreground\">\n Écrivez-nous à{\" \"}\n <a\n href={`mailto:${contact}`}\n className=\"font-medium text-foreground underline underline-offset-4 hover:text-foreground/80\"\n >\n {contact}\n </a>{\" \"}\n pour en recevoir une nouvelle.\n </p>\n ) : null}\n\n {action}\n </div>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs","useState","jsx","jsxs","jsx","jsxs","useState","useState","jsx","jsxs","DEFAULTS","useState","useState","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","jsx","jsxs","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/hooks/use-session.ts","../src/components/auth-field.tsx","../src/components/auth-submit.tsx","../src/components/login-form.tsx","../src/components/auth-alert.tsx","../src/components/auth-heading.tsx","../src/components/social-buttons.tsx","../src/components/register-form.tsx","../src/components/verify-email-form.tsx","../src/components/auth-otp-field.tsx","../src/components/forgot-password-form.tsx","../src/components/reset-password-form.tsx","../src/components/auth-layout.tsx","../src/components/invitation-notice.tsx"],"sourcesContent":["import type { PlatformAuthClient } from \"../client\"\n\n/**\n * Returns a useSession hook bound to the given auth client.\n * Usage: const { data: session, isPending } = useSession(authClient)\n */\nexport function useSession(authClient: PlatformAuthClient) {\n return authClient.useSession()\n}\n\n/**\n * Returns a logout function bound to the given auth client.\n * Usage: const logout = useLogout(authClient)\n */\nexport function useLogout(authClient: PlatformAuthClient) {\n const signOut = async () => {\n await authClient.signOut()\n }\n return signOut\n}\n","import { useId, useState, type InputHTMLAttributes, type ReactNode } from \"react\"\n\ntype NativeProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"id\" | \"className\">\n\nexport interface AuthFieldProps extends NativeProps {\n label: string\n /** Rendered on the right of the label row — typically a \"forgot password\" link */\n hint?: ReactNode\n /** Persistent helper text under the field, tied to it for screen readers */\n description?: string\n invalid?: boolean\n}\n\n/**\n * A single credential field.\n *\n * The label is a real <label>, always visible, never a placeholder standing in\n * for one: a placeholder disappears the moment someone types, which is exactly\n * when a person double-checking a long password needs to know what the box is.\n *\n * Touch targets are 52px tall on mobile and 44px from sm up. Below ~48px,\n * thumbs miss; on a pointer device the same height reads as oversized, so the\n * two are not one compromise value.\n *\n * The focus ring is a ring rather than an outline so it follows the rounded\n * corners exactly, and the border darkens at the same time — the border is what\n * survives forced-colors mode, where the ring is dropped.\n */\nexport function AuthField({\n label,\n hint,\n description,\n invalid = false,\n ...props\n}: AuthFieldProps) {\n const id = useId()\n const descriptionId = description ? `${id}-description` : undefined\n const [revealed, setRevealed] = useState(false)\n\n const isPassword = props.type === \"password\"\n const type = isPassword && revealed ? \"text\" : props.type\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <label\n htmlFor={id}\n className=\"text-sm font-medium leading-none text-foreground\"\n >\n {label}\n </label>\n {hint}\n </div>\n\n <div className=\"relative\">\n <input\n {...props}\n id={id}\n type={type}\n aria-invalid={invalid || undefined}\n aria-describedby={descriptionId}\n className={[\n \"h-[52px] w-full rounded-xl border bg-background px-4 text-base\",\n \"sm:h-11 sm:text-sm\",\n isPassword ? \"pr-12\" : \"\",\n \"text-foreground placeholder:text-muted-foreground/70\",\n \"transition-[border-color,box-shadow] duration-150 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-[3px]\",\n invalid\n ? \"border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20\"\n : \"border-input focus-visible:border-ring focus-visible:ring-ring/15\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ]\n .filter(Boolean)\n .join(\" \")}\n />\n\n {isPassword && (\n <button\n type=\"button\"\n onClick={() => setRevealed((v) => !v)}\n disabled={props.disabled}\n aria-pressed={revealed}\n aria-label={\n revealed ? \"Masquer le mot de passe\" : \"Afficher le mot de passe\"\n }\n className=\"absolute inset-y-0 right-0 flex w-12 items-center justify-center rounded-r-xl text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-60\"\n >\n <EyeIcon open={revealed} />\n </button>\n )}\n </div>\n\n {description && (\n <p id={descriptionId} className=\"text-xs text-muted-foreground\">\n {description}\n </p>\n )}\n </div>\n )\n}\n\n// Inline rather than from lucide-react: the package would gain a dependency\n// consumers already have at a different version, for two paths.\nfunction EyeIcon({ open }: { open: boolean }) {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0Z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n {!open && <path d=\"m3 3 18 18\" />}\n </svg>\n )\n}\n","import type { ReactNode } from \"react\"\n\ninterface AuthSubmitProps {\n pending?: boolean\n disabled?: boolean\n pendingLabel: string\n children: ReactNode\n}\n\n/**\n * The primary action of an auth screen.\n *\n * The label swaps to its pending form in place, with the spinner absolutely\n * positioned: a spinner inserted into the flow would widen the row and shift\n * the text sideways at the exact moment the person is watching it.\n *\n * The press feedback is a 1px translate rather than a scale — scaling a\n * full-width button visibly blurs its text mid-transform.\n */\nexport function AuthSubmit({\n pending = false,\n disabled = false,\n pendingLabel,\n children,\n}: AuthSubmitProps) {\n return (\n <button\n type=\"submit\"\n disabled={disabled || pending}\n aria-busy={pending || undefined}\n className={[\n \"relative flex h-[52px] w-full items-center justify-center rounded-xl sm:h-11\",\n \"bg-primary text-base font-medium text-primary-foreground sm:text-sm\",\n \"shadow-sm transition-[background-color,transform,box-shadow] duration-150 ease-out\",\n \"hover:bg-primary/90 active:translate-y-px\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:pointer-events-none disabled:opacity-55\",\n ].join(\" \")}\n >\n {pending && (\n <span\n aria-hidden=\"true\"\n className=\"absolute left-4 size-4 animate-spin rounded-full border-2 border-current border-t-transparent opacity-70 motion-reduce:animate-none\"\n />\n )}\n {pending ? pendingLabel : children}\n </button>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { LoginFormLabels, LoginFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthHeading } from \"./auth-heading\"\nimport { AuthSubmit } from \"./auth-submit\"\nimport { SocialButtons } from \"./social-buttons\"\n\nconst DEFAULTS: Required<LoginFormLabels> = {\n title: \"Connexion\",\n subtitle: \"Content de te revoir. Entre tes identifiants pour continuer.\",\n emailPlaceholder: \"Adresse e-mail\",\n passwordPlaceholder: \"Mot de passe\",\n forgotPassword: \"Mot de passe oublié ?\",\n submit: \"Se connecter\",\n submitPending: \"Connexion…\",\n noAccount: \"Pas encore de compte ?\",\n register: \"Créer un compte\",\n emailRequired: \"Renseigne ton adresse e-mail\",\n passwordRequired: \"Renseigne ton mot de passe\",\n invalidCredentials: \"Adresse e-mail ou mot de passe incorrect\",\n // accountLinking is disabled in createPlatformAuth, so a social sign-in on an\n // address already registered with a password is refused with this code.\n // Without a message the button reads as broken rather than as a rejection.\n accountNotLinked:\n \"Cette adresse est déjà associée à un mot de passe. Connecte-toi avec ton mot de passe.\",\n socialCancelled: \"Connexion annulée.\",\n socialFailed: \"La connexion a échoué. Réessaie.\",\n}\n\nexport function LoginForm({\n onSuccess,\n registerUrl = \"/register\",\n forgotPasswordUrl = \"/forgot-password\",\n socialCallbackUrl = \"/\",\n socialProviders = [],\n coreTokenUrl = \"/api/auth/core-token\",\n labels,\n titleClassName,\n error: externalError,\n authClient,\n}: LoginFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [email, setEmail] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [ownError, setOwnError] = useState<string | undefined>()\n const [isPending, setIsPending] = useState(false)\n\n // What the person just did outranks what happened before they arrived.\n const error = ownError ?? externalError\n const setError = setOwnError\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!email.trim()) {\n setError(t.emailRequired)\n return\n }\n if (!password) {\n setError(t.passwordRequired)\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.signIn.email({\n email: email.trim(),\n password,\n })\n if (res?.error) {\n setError(res.error.message ?? t.invalidCredentials)\n return\n }\n // The better-auth session cookie alone does not authenticate a Go core:\n // it verifies the EdDSA JWT minted here against the issuer's JWKS.\n // Skipped when the app sets the token itself, or has no core at all.\n if (coreTokenUrl) {\n await fetch(coreTokenUrl, { credentials: \"include\" })\n }\n onSuccess?.()\n } catch (err) {\n setError(err instanceof Error ? err.message : t.invalidCredentials)\n } finally {\n setIsPending(false)\n }\n }\n\n const handleSocial = async (provider: \"google\" | \"github\") => {\n setError(undefined)\n try {\n await authClient.signIn.social({\n provider,\n callbackURL: socialCallbackUrl,\n })\n } catch (err) {\n const code = err instanceof Error ? err.message : \"\"\n setError(\n code === \"account_not_linked\"\n ? t.accountNotLinked\n : code === \"access_denied\"\n ? t.socialCancelled\n : t.socialFailed,\n )\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <AuthHeading\n title={t.title}\n subtitle={t.subtitle}\n titleClassName={titleClassName}\n />\n\n <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-5\" noValidate>\n <AuthField\n label={t.emailPlaceholder}\n type=\"email\"\n inputMode=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"email\"\n autoCapitalize=\"none\"\n spellCheck={false}\n invalid={!!error}\n />\n\n <AuthField\n label={t.passwordPlaceholder}\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"current-password\"\n invalid={!!error}\n hint={\n <a\n href={forgotPasswordUrl}\n className=\"rounded text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.forgotPassword}\n </a>\n }\n />\n\n <AuthSubmit\n pending={isPending}\n disabled={!email.trim() || !password}\n pendingLabel={t.submitPending}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <SocialButtons\n providers={socialProviders}\n onSelect={handleSocial}\n disabled={isPending}\n />\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.noAccount}{\" \"}\n <a\n href={registerUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.register}\n </a>\n </p>\n </div>\n )\n}\n","interface AuthAlertProps {\n children?: string\n tone?: \"error\" | \"success\"\n}\n\n/**\n * Inline feedback for an auth screen.\n *\n * `role=\"alert\"` on the wrapper is not enough on its own: the node has to be\n * in the tree before the text lands in it for the live region to fire, which\n * is why the element renders empty rather than being mounted with its message.\n *\n * The success tone uses the theme's own tokens rather than a fixed green,\n * which would sit on a dark surface as an unreadable pale block.\n */\nexport function AuthAlert({ children, tone = \"error\" }: AuthAlertProps) {\n return (\n <div\n role={tone === \"error\" ? \"alert\" : \"status\"}\n aria-live={tone === \"error\" ? \"assertive\" : \"polite\"}\n className={\n children\n ? [\n \"rounded-xl border px-3.5 py-3 text-sm leading-snug\",\n tone === \"error\"\n ? \"border-destructive/25 bg-destructive/10 text-destructive\"\n : \"border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400\",\n ].join(\" \")\n : undefined\n }\n >\n {children}\n </div>\n )\n}\n","interface AuthHeadingProps {\n title: string\n subtitle?: string\n /** Replaces the default size and weight — pass the whole look */\n titleClassName?: string\n}\n\n/**\n * The heading of an auth screen, centred above left-aligned fields. That\n * contrast of alignment is what gives the card its hierarchy without a rule.\n *\n * It lives in the form rather than in the layout so a screen carries exactly\n * one <h1>, and so the copy travels with the component that owns the step.\n */\nexport function AuthHeading({\n title,\n subtitle,\n titleClassName = \"text-2xl font-semibold tracking-tight\",\n}: AuthHeadingProps) {\n return (\n <header className=\"space-y-1.5 text-center\">\n <h1 className={`leading-tight text-foreground ${titleClassName}`}>\n {title}\n </h1>\n {subtitle && (\n <p className=\"text-balance text-sm leading-relaxed text-muted-foreground\">\n {subtitle}\n </p>\n )}\n </header>\n )\n}\n","const LABELS: Record<string, string> = {\n google: \"Continuer avec Google\",\n github: \"Continuer avec GitHub\",\n}\n\ninterface SocialButtonsProps {\n providers: Array<\"google\" | \"github\">\n onSelect: (provider: \"google\" | \"github\") => void | Promise<void>\n disabled?: boolean\n /** Divider text between the password form and the providers */\n separator?: string\n /** Per-provider button copy, merged over the French defaults */\n labels?: Partial<Record<\"google\" | \"github\", string>>\n}\n\nexport function SocialButtons({\n providers,\n onSelect,\n disabled = false,\n separator = \"ou\",\n labels,\n}: SocialButtonsProps) {\n if (providers.length === 0) return null\n\n const copy = { ...LABELS, ...labels }\n\n return (\n <div className=\"space-y-4\">\n {/* The rule is two flex segments rather than a line behind an opaque\n label: an opaque background only hides the rule when it matches the\n surface behind it, which breaks the moment this sits on a card. */}\n <div aria-hidden=\"true\" className=\"flex items-center gap-3\">\n <span className=\"h-px flex-1 bg-border\" />\n <span className=\"text-[11px] uppercase tracking-[0.14em] text-muted-foreground\">\n {separator}\n </span>\n <span className=\"h-px flex-1 bg-border\" />\n </div>\n\n {/* One per row while there is space for the wording, side by side once\n there are several: in a half-card the full label no longer fits, and\n a truncated \"Continuer avec…\" says less than the mark alone. The\n wording stays as the accessible name either way. */}\n <div\n className={\n providers.length > 1\n ? \"grid gap-2.5 sm:grid-cols-2\"\n : \"grid gap-2.5\"\n }\n >\n {providers.map((provider) => (\n <button\n key={provider}\n type=\"button\"\n onClick={() => onSelect(provider)}\n disabled={disabled}\n aria-label={copy[provider] ?? provider}\n className={[\n \"flex h-[52px] w-full items-center justify-center gap-2.5 rounded-xl sm:h-11\",\n \"border border-input bg-background text-base font-medium text-foreground sm:text-sm\",\n \"transition-[background-color,border-color,transform] duration-150 ease-out\",\n \"hover:border-foreground/20 hover:bg-accent active:translate-y-px\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:pointer-events-none disabled:opacity-55\",\n ].join(\" \")}\n >\n <ProviderMark provider={provider} />\n <span className={providers.length > 1 ? \"sm:hidden\" : \"\"}>\n {copy[provider] ?? provider}\n </span>\n </button>\n ))}\n </div>\n </div>\n )\n}\n\n// Brand marks are inlined: they must keep their own colors (Google's mark is\n// unusable in monochrome) and adding an icon dependency to this package would\n// duplicate one every consumer already ships.\nfunction ProviderMark({ provider }: { provider: \"google\" | \"github\" }) {\n if (provider === \"google\") {\n return (\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 18 18\" aria-hidden=\"true\">\n <path\n fill=\"#4285F4\"\n d=\"M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.72v2.26h2.92c1.7-1.57 2.68-3.88 2.68-6.62Z\"\n />\n <path\n fill=\"#34A853\"\n d=\"M9 18c2.43 0 4.47-.8 5.96-2.18l-2.92-2.26c-.8.54-1.84.86-3.04.86-2.34 0-4.32-1.58-5.02-3.7H.96v2.33A9 9 0 0 0 9 18Z\"\n />\n <path\n fill=\"#FBBC05\"\n d=\"M3.98 10.72a5.4 5.4 0 0 1 0-3.44V4.95H.96a9 9 0 0 0 0 8.1l3.02-2.33Z\"\n />\n <path\n fill=\"#EA4335\"\n d=\"M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.58C13.46.9 11.43 0 9 0A9 9 0 0 0 .96 4.95l3.02 2.33C4.68 5.16 6.66 3.58 9 3.58Z\"\n />\n </svg>\n )\n }\n return (\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M8 0a8 8 0 0 0-2.53 15.59c.4.07.55-.17.55-.38l-.01-1.49c-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.4 7.4 0 0 1 4 0c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48l-.01 2.2c0 .21.15.46.55.38A8 8 0 0 0 8 0Z\" />\n </svg>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { RegisterFormLabels, RegisterFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthHeading } from \"./auth-heading\"\nimport { AuthSubmit } from \"./auth-submit\"\nimport { SocialButtons } from \"./social-buttons\"\n\nconst MIN_PASSWORD_LENGTH = 8\n\nconst DEFAULTS: Required<RegisterFormLabels> = {\n title: \"Créer un compte\",\n subtitle: \"Nous t'enverrons un code pour confirmer ton adresse e-mail.\",\n namePlaceholder: \"Nom complet\",\n emailPlaceholder: \"Adresse e-mail\",\n passwordPlaceholder: \"Mot de passe\",\n passwordHint: `Au moins ${MIN_PASSWORD_LENGTH} caractères.`,\n submit: \"Créer mon compte\",\n submitPending: \"Création…\",\n haveAccount: \"Tu as déjà un compte ?\",\n login: \"Se connecter\",\n nameRequired: \"Renseigne ton nom\",\n emailRequired: \"Renseigne ton adresse e-mail\",\n passwordTooShort: `Le mot de passe doit faire au moins ${MIN_PASSWORD_LENGTH} caractères`,\n signUpFailed: \"La création du compte a échoué\",\n}\n\nexport function RegisterForm({\n onSuccess,\n loginUrl = \"/login\",\n legal,\n socialCallbackUrl = \"/\",\n socialProviders = [],\n labels,\n titleClassName,\n error: externalError,\n authClient,\n}: RegisterFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [name, setName] = useState(\"\")\n const [email, setEmail] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [ownError, setOwnError] = useState<string | undefined>()\n const [isPending, setIsPending] = useState(false)\n\n // What the person just did outranks what happened before they arrived.\n const error = ownError ?? externalError\n const setError = setOwnError\n\n const tooShort = password.length > 0 && password.length < MIN_PASSWORD_LENGTH\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!name.trim()) {\n setError(t.nameRequired)\n return\n }\n if (!email.trim()) {\n setError(t.emailRequired)\n return\n }\n if (password.length < MIN_PASSWORD_LENGTH) {\n setError(t.passwordTooShort)\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.signUp.email({\n name: name.trim(),\n email: email.trim(),\n password,\n })\n if (res?.error) {\n setError(res.error.message ?? t.signUpFailed)\n return\n }\n // createPlatformAuth sets requireEmailVerification, so sign-up leaves the\n // account unverified and without a session: the caller routes to the OTP\n // step rather than into the app.\n onSuccess?.(email.trim())\n } catch (err) {\n setError(err instanceof Error ? err.message : t.signUpFailed)\n } finally {\n setIsPending(false)\n }\n }\n\n const handleSocial = async (provider: \"google\" | \"github\") => {\n setError(undefined)\n try {\n await authClient.signIn.social({\n provider,\n callbackURL: socialCallbackUrl,\n })\n } catch (err) {\n setError(err instanceof Error ? err.message : t.signUpFailed)\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <AuthHeading\n title={t.title}\n subtitle={t.subtitle}\n titleClassName={titleClassName}\n />\n\n <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-5\" noValidate>\n <AuthField\n label={t.namePlaceholder}\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"name\"\n />\n\n <AuthField\n label={t.emailPlaceholder}\n type=\"email\"\n inputMode=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"email\"\n autoCapitalize=\"none\"\n spellCheck={false}\n />\n\n <AuthField\n label={t.passwordPlaceholder}\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"new-password\"\n description={t.passwordHint}\n // Only once they have typed something: flagging an untouched field\n // red would scold someone for not having started yet.\n invalid={tooShort}\n />\n\n <AuthSubmit pending={isPending} pendingLabel={t.submitPending}>\n {t.submit}\n </AuthSubmit>\n\n {legal && (\n <p className=\"text-center text-xs leading-relaxed text-muted-foreground\">\n {legal}\n </p>\n )}\n </form>\n\n <SocialButtons\n providers={socialProviders}\n onSelect={handleSocial}\n disabled={isPending}\n />\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.haveAccount}{\" \"}\n <a\n href={loginUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.login}\n </a>\n </p>\n </div>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { VerifyEmailFormLabels, VerifyEmailFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthHeading } from \"./auth-heading\"\nimport { AuthOtpField, OTP_LENGTH } from \"./auth-otp-field\"\nimport { AuthSubmit } from \"./auth-submit\"\n\nconst DEFAULTS: Required<VerifyEmailFormLabels> = {\n title: \"Vérifie ton adresse\",\n subtitle: \"Entre le code à 6 chiffres envoyé à\",\n subtitleNoEmail: \"Entre le code à 6 chiffres reçu par e-mail.\",\n codePlaceholder: \"Code de vérification\",\n submit: \"Vérifier\",\n submitPending: \"Vérification…\",\n resend: \"Renvoyer le code\",\n resendPending: \"Envoi…\",\n resent: \"Un nouveau code vient de t'être envoyé.\",\n alreadyVerified: \"Adresse déjà vérifiée ?\",\n login: \"Se connecter\",\n codeRequired: \"Entre le code à 6 chiffres\",\n invalidCode: \"Code invalide. Réessaie.\",\n resendFailed: \"L'envoi du code a échoué\",\n missingEmail: \"Adresse e-mail introuvable. Recommence l'inscription.\",\n}\n\nexport function VerifyEmailForm({\n email,\n onSuccess,\n loginUrl = \"/login\",\n labels,\n titleClassName,\n authClient,\n}: VerifyEmailFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [otp, setOtp] = useState(\"\")\n const [isVerifying, setIsVerifying] = useState(false)\n const [isResending, setIsResending] = useState(false)\n const [resendMessage, setResendMessage] = useState<string | undefined>()\n const [error, setError] = useState<string | undefined>()\n\n const handleVerify = async (e: FormEvent) => {\n e.preventDefault()\n if (otp.length < OTP_LENGTH) {\n setError(t.codeRequired)\n return\n }\n setError(undefined)\n setResendMessage(undefined)\n setIsVerifying(true)\n try {\n const res = await authClient.emailOtp.verifyEmail({ email, otp })\n if (res?.error) {\n setError(res.error.message ?? t.invalidCode)\n return\n }\n onSuccess?.()\n } catch (err) {\n setError(err instanceof Error ? err.message : t.invalidCode)\n } finally {\n setIsVerifying(false)\n }\n }\n\n const handleResend = async () => {\n if (!email) {\n setError(t.missingEmail)\n return\n }\n setError(undefined)\n setResendMessage(undefined)\n setIsResending(true)\n try {\n await authClient.emailOtp.sendVerificationOtp({\n email,\n type: \"email-verification\",\n })\n setResendMessage(t.resent)\n } catch (err) {\n setError(err instanceof Error ? err.message : t.resendFailed)\n } finally {\n setIsResending(false)\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <AuthHeading\n title={t.title}\n subtitle={email ? `${t.subtitle} ${email}.` : t.subtitleNoEmail}\n titleClassName={titleClassName}\n />\n\n <AuthAlert>{error}</AuthAlert>\n <AuthAlert tone=\"success\">{resendMessage}</AuthAlert>\n\n <form onSubmit={handleVerify} className=\"space-y-5\" noValidate>\n <AuthOtpField\n id=\"verify-email-otp\"\n label={t.codePlaceholder}\n value={otp}\n onChange={setOtp}\n disabled={isVerifying}\n />\n\n <AuthSubmit\n pending={isVerifying}\n disabled={otp.length < OTP_LENGTH}\n pendingLabel={t.submitPending}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <div className=\"space-y-4 text-center text-sm text-muted-foreground\">\n <button\n type=\"button\"\n onClick={handleResend}\n disabled={isResending}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-55\"\n >\n {isResending ? t.resendPending : t.resend}\n </button>\n\n <p>\n {t.alreadyVerified}{\" \"}\n <a\n href={loginUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.login}\n </a>\n </p>\n </div>\n </div>\n )\n}\n","interface AuthOtpFieldProps {\n label: string\n value: string\n onChange: (value: string) => void\n disabled?: boolean\n invalid?: boolean\n id: string\n}\n\nexport const OTP_LENGTH = 6\n\n/**\n * The 6-digit code field.\n *\n * Not an AuthField: the value is a code being read off another screen, so it\n * is spaced and monospaced to be checked digit by digit, and non-digits are\n * dropped on the way in — pasting a code from a mail client routinely carries\n * a trailing space.\n */\nexport function AuthOtpField({\n label,\n value,\n onChange,\n disabled = false,\n invalid = false,\n id,\n}: AuthOtpFieldProps) {\n return (\n <div className=\"space-y-2\">\n <label\n htmlFor={id}\n className=\"block text-sm font-medium leading-none text-foreground\"\n >\n {label}\n </label>\n <input\n id={id}\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n maxLength={OTP_LENGTH}\n value={value}\n onChange={(e) => onChange(e.target.value.replace(/\\D/g, \"\"))}\n required\n disabled={disabled}\n autoComplete=\"one-time-code\"\n aria-invalid={invalid || undefined}\n className={[\n \"h-[52px] w-full rounded-xl border bg-background px-4 sm:h-11\",\n \"text-center font-mono text-lg tracking-[0.4em]\",\n \"text-foreground\",\n \"transition-[border-color,box-shadow] duration-150 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-[3px]\",\n invalid\n ? \"border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20\"\n : \"border-input focus-visible:border-ring focus-visible:ring-ring/15\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ].join(\" \")}\n />\n </div>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type {\n ForgotPasswordFormLabels,\n ForgotPasswordFormProps,\n} from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthHeading } from \"./auth-heading\"\nimport { AuthSubmit } from \"./auth-submit\"\n\nconst DEFAULTS: Required<ForgotPasswordFormLabels> = {\n title: \"Mot de passe oublié ?\",\n subtitle:\n \"Entre ton adresse e-mail et nous t'enverrons un code pour le réinitialiser.\",\n emailPlaceholder: \"Adresse e-mail\",\n submit: \"Envoyer le code\",\n submitPending: \"Envoi…\",\n rememberPassword: \"Tu t'en souviens finalement ?\",\n login: \"Se connecter\",\n emailRequired: \"Renseigne ton adresse e-mail\",\n sendFailed: \"L'envoi du code a échoué\",\n}\n\nexport function ForgotPasswordForm({\n onSuccess,\n loginUrl = \"/login\",\n labels,\n titleClassName,\n authClient,\n}: ForgotPasswordFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [email, setEmail] = useState(\"\")\n const [error, setError] = useState<string | undefined>()\n const [isPending, setIsPending] = useState(false)\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (!email.trim()) {\n setError(t.emailRequired)\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.emailOtp.sendVerificationOtp({\n email: email.trim(),\n type: \"forget-password\",\n })\n if (res?.error) {\n setError(res.error.message ?? t.sendFailed)\n return\n }\n onSuccess?.(email.trim())\n } catch (err) {\n setError(err instanceof Error ? err.message : t.sendFailed)\n } finally {\n setIsPending(false)\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <AuthHeading\n title={t.title}\n subtitle={t.subtitle}\n titleClassName={titleClassName}\n />\n\n <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-5\" noValidate>\n <AuthField\n label={t.emailPlaceholder}\n type=\"email\"\n inputMode=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n disabled={isPending}\n autoComplete=\"email\"\n autoCapitalize=\"none\"\n spellCheck={false}\n invalid={!!error}\n />\n\n <AuthSubmit\n pending={isPending}\n disabled={!email.trim()}\n pendingLabel={t.submitPending}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.rememberPassword}{\" \"}\n <a\n href={loginUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.login}\n </a>\n </p>\n </div>\n )\n}\n","import { useState, type FormEvent } from \"react\"\nimport type { ResetPasswordFormLabels, ResetPasswordFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthHeading } from \"./auth-heading\"\nimport { AuthOtpField, OTP_LENGTH } from \"./auth-otp-field\"\nimport { AuthSubmit } from \"./auth-submit\"\n\nconst MIN_PASSWORD_LENGTH = 8\n\nconst DEFAULTS: Required<ResetPasswordFormLabels> = {\n title: \"Nouveau mot de passe\",\n subtitle: \"Entre le code à 6 chiffres reçu par e-mail et ton nouveau mot de passe.\",\n codePlaceholder: \"Code de vérification\",\n passwordPlaceholder: \"Nouveau mot de passe\",\n passwordHint: `Au moins ${MIN_PASSWORD_LENGTH} caractères.`,\n confirmPlaceholder: \"Confirme le mot de passe\",\n submit: \"Réinitialiser\",\n submitPending: \"Réinitialisation…\",\n resend: \"Renvoyer le code\",\n resendPending: \"Envoi…\",\n resent: \"Un nouveau code vient de t'être envoyé.\",\n rememberPassword: \"Tu t'en souviens finalement ?\",\n login: \"Se connecter\",\n codeRequired: \"Entre le code à 6 chiffres\",\n passwordTooShort: `Le mot de passe doit faire au moins ${MIN_PASSWORD_LENGTH} caractères`,\n passwordMismatch: \"Les deux mots de passe ne correspondent pas\",\n resetFailed: \"La réinitialisation a échoué\",\n resendFailed: \"L'envoi du code a échoué\",\n}\n\nexport function ResetPasswordForm({\n email,\n onSuccess,\n loginUrl = \"/login\",\n labels,\n titleClassName,\n authClient,\n}: ResetPasswordFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [otp, setOtp] = useState(\"\")\n const [password, setPassword] = useState(\"\")\n const [confirmPassword, setConfirmPassword] = useState(\"\")\n const [error, setError] = useState<string | undefined>()\n const [isResetting, setIsResetting] = useState(false)\n const [isResending, setIsResending] = useState(false)\n const [resendMessage, setResendMessage] = useState<string | undefined>()\n\n const tooShort = password.length > 0 && password.length < MIN_PASSWORD_LENGTH\n const mismatch = confirmPassword.length > 0 && confirmPassword !== password\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\n if (otp.length < OTP_LENGTH) {\n setError(t.codeRequired)\n return\n }\n if (password.length < MIN_PASSWORD_LENGTH) {\n setError(t.passwordTooShort)\n return\n }\n if (password !== confirmPassword) {\n setError(t.passwordMismatch)\n return\n }\n setError(undefined)\n setResendMessage(undefined)\n setIsResetting(true)\n try {\n const res = await fetch(\"/api/auth/email-otp/reset-password\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, otp, password }),\n })\n if (!res.ok) {\n const body = await res.json().catch(() => null)\n setError(body?.message ?? t.resetFailed)\n return\n }\n onSuccess?.()\n } catch (err) {\n setError(err instanceof Error ? err.message : t.resetFailed)\n } finally {\n setIsResetting(false)\n }\n }\n\n const handleResend = async () => {\n setError(undefined)\n setResendMessage(undefined)\n setIsResending(true)\n try {\n await authClient.emailOtp.sendVerificationOtp({\n email,\n type: \"forget-password\",\n })\n setResendMessage(t.resent)\n } catch (err) {\n setError(err instanceof Error ? err.message : t.resendFailed)\n } finally {\n setIsResending(false)\n }\n }\n\n return (\n <div className=\"space-y-7\">\n <AuthHeading\n title={t.title}\n subtitle={t.subtitle}\n titleClassName={titleClassName}\n />\n\n <AuthAlert>{error}</AuthAlert>\n <AuthAlert tone=\"success\">{resendMessage}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-5\" noValidate>\n <AuthOtpField\n id=\"reset-password-otp\"\n label={t.codePlaceholder}\n value={otp}\n onChange={setOtp}\n disabled={isResetting}\n />\n\n <AuthField\n label={t.passwordPlaceholder}\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n disabled={isResetting}\n autoComplete=\"new-password\"\n description={t.passwordHint}\n invalid={tooShort}\n />\n\n <AuthField\n label={t.confirmPlaceholder}\n type=\"password\"\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n required\n disabled={isResetting}\n autoComplete=\"new-password\"\n invalid={mismatch}\n />\n\n <AuthSubmit\n pending={isResetting}\n disabled={otp.length < OTP_LENGTH || !password || !confirmPassword}\n pendingLabel={t.submitPending}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <div className=\"space-y-4 text-center text-sm text-muted-foreground\">\n <button\n type=\"button\"\n onClick={handleResend}\n disabled={isResending}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-55\"\n >\n {isResending ? t.resendPending : t.resend}\n </button>\n\n <p>\n {t.rememberPassword}{\" \"}\n <a\n href={loginUrl}\n className=\"rounded font-medium text-foreground underline underline-offset-4 decoration-foreground/25 transition-colors hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t.login}\n </a>\n </p>\n </div>\n </div>\n )\n}\n","import type { AuthLayoutProps } from \"../types\"\n\n/**\n * The frame every auth screen sits in. It owns the ground, the card and the\n * app's own marks (logo, illustration, legal footer); the form it wraps owns\n * its title and its fields.\n *\n * Mobile and desktop are two layouts, not one scaled down. On a phone the form\n * IS the page: no card, no border, edge-to-edge padding, top-aligned so the\n * fields stay above the keyboard instead of being pushed under it by vertical\n * centering. From sm up it becomes a bounded card on a tinted ground — a\n * full-width form on a 1440px display is unreadable.\n *\n * With a `panel` the card splits in two from md up, the form on the left and\n * the illustration on the right. The panel is dropped below that width rather\n * than stacked: a decorative half-screen above a form costs a full swipe\n * before the first field. Without a `panel` the card stays the single 420px\n * column it has always been.\n *\n * min-h-dvh rather than min-h-screen: on mobile browsers 100vh includes the\n * retracting URL bar, so a screen-height container overflows by its height.\n */\nexport function AuthLayout({\n logo,\n panel,\n children,\n footer,\n}: AuthLayoutProps) {\n return (\n <div className=\"flex min-h-dvh flex-col bg-background px-5 pb-10 pt-12 sm:items-center sm:bg-muted/40 sm:px-6 sm:py-16\">\n <div\n className={`mx-auto w-full ${panel ? \"max-w-4xl\" : \"max-w-[420px]\"}`}\n >\n <div\n className={[\n \"sm:overflow-hidden sm:rounded-2xl sm:border sm:border-border/70 sm:bg-card sm:shadow-lg sm:shadow-black/5\",\n panel ? \"md:grid md:grid-cols-2\" : \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n <div className=\"sm:p-8\">\n {logo && <div className=\"mb-7 flex justify-center\">{logo}</div>}\n {children}\n </div>\n\n {panel && (\n // Decorative: it must never carry information the form does not,\n // so it is hidden from assistive tech rather than described.\n <div\n aria-hidden=\"true\"\n className=\"hidden bg-muted md:block [&>*]:h-full [&>*]:w-full [&>img]:object-cover\"\n >\n {panel}\n </div>\n )}\n </div>\n\n {footer && (\n <div className=\"mt-6 text-center text-xs leading-relaxed text-muted-foreground\">\n {footer}\n </div>\n )}\n </div>\n </div>\n )\n}\n","import type { InvitationNoticeProps } from \"../types\"\n\n/**\n * Every app is reachable at contact@ its own apex domain, so the address is\n * derived rather than configured. Sub-domains are stripped because the app is\n * routinely served from app./admin. while the mailbox lives on the apex;\n * multi-part public suffixes (.co.uk) would need a real suffix list and no app\n * using this is on one.\n */\nfunction defaultSupportEmail(): string | undefined {\n if (typeof window === \"undefined\") return undefined\n const host = window.location.hostname\n if (!host || host === \"localhost\" || /^[\\d.]+$/.test(host)) return undefined\n const apex = host.split(\".\").slice(-2).join(\".\")\n return `contact@${apex}`\n}\n\nconst REASON_MESSAGE: Record<string, string> = {\n expired: \"Cette invitation a expiré.\",\n claimed: \"Cette invitation a déjà été utilisée.\",\n unknown: \"Ce lien d'invitation n'est pas valide.\",\n}\n\n/**\n * What an invitee sees when their link does not work.\n *\n * It names the reason instead of merging the cases into one message: \"expired\"\n * tells someone their link was real and that asking for another one is worth\n * it, which \"invalid\" does not. Short TTLs make that distinction routine.\n *\n * The only way forward offered is a mailto, deliberately. A self-service\n * \"request a new invitation\" form would mean a public endpoint accepting dead\n * tokens, a queue to moderate, and a way to probe which tokens once existed —\n * for a flow where the operator already knows the person by name.\n */\nexport function InvitationNotice({\n reason = \"unknown\",\n supportEmail,\n title = \"Invitation indisponible\",\n action,\n}: InvitationNoticeProps) {\n const contact = supportEmail ?? defaultSupportEmail()\n\n return (\n <div className=\"space-y-8\">\n <div>\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {REASON_MESSAGE[reason] ?? REASON_MESSAGE.unknown}\n </p>\n </div>\n\n {contact ? (\n <p className=\"text-sm text-muted-foreground\">\n Écrivez-nous à{\" \"}\n <a\n href={`mailto:${contact}`}\n className=\"font-medium text-foreground underline underline-offset-4 hover:text-foreground/80\"\n >\n {contact}\n </a>{\" \"}\n pour en recevoir une nouvelle.\n </p>\n ) : null}\n\n {action}\n </div>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs","useState","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","useState","jsx","jsxs","DEFAULTS","useState","useState","jsx","jsxs","jsx","jsxs","DEFAULTS","useState","useState","jsx","jsxs","DEFAULTS","useState","useState","jsx","jsxs","MIN_PASSWORD_LENGTH","DEFAULTS","useState","jsx","jsxs","jsx","jsxs"]}
@@ -1,4 +1,4 @@
1
- import { b as InvitationFailure } from './types-BEldQPou.js';
1
+ import { b as InvitationFailure } from './types-SieiPwdd.js';
2
2
 
3
3
  /**
4
4
  * What became of a claim, in terms the invitee can be told.
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Auth, BetterAuthOptions } from 'better-auth';
2
- import { d as PlatformAuthConfig } from './types-BEldQPou.js';
3
- export { h as PlatformSession, i as PlatformSessionData, j as PlatformUser } from './types-BEldQPou.js';
4
- export { a as ClaimInvitationOptions, C as ClaimOutcome, c as claimInvitation, b as completesSignup, h as holdInviteTokenCookie, d as invitationOutcomeCookie, e as inviteTokenFrom, i as isInvitationFailure, p as pinInviteToken, r as releaseInviteTokenCookie } from './invitation-aZz_6M7B.js';
2
+ import { d as PlatformAuthConfig } from './types-SieiPwdd.js';
3
+ export { h as PlatformSession, i as PlatformSessionData, j as PlatformUser } from './types-SieiPwdd.js';
4
+ export { a as ClaimInvitationOptions, C as ClaimOutcome, c as claimInvitation, b as completesSignup, h as holdInviteTokenCookie, d as invitationOutcomeCookie, e as inviteTokenFrom, i as isInvitationFailure, p as pinInviteToken, r as releaseInviteTokenCookie } from './invitation-BUqM_BF8.js';
5
5
 
6
6
  /**
7
7
  * Creates a Better Auth instance with platform defaults.
@@ -116,13 +116,34 @@ interface PlatformAuthClientConfig {
116
116
  /** Additional client plugins */
117
117
  plugins?: any[];
118
118
  }
119
- interface VerifyEmailFormProps {
119
+ interface VerifyEmailFormLabels {
120
+ title?: string;
121
+ subtitle?: string;
122
+ subtitleNoEmail?: string;
123
+ codePlaceholder?: string;
124
+ submit?: string;
125
+ submitPending?: string;
126
+ resend?: string;
127
+ resendPending?: string;
128
+ resent?: string;
129
+ alreadyVerified?: string;
130
+ login?: string;
131
+ codeRequired?: string;
132
+ invalidCode?: string;
133
+ resendFailed?: string;
134
+ missingEmail?: string;
135
+ }
136
+ interface VerifyEmailFormProps extends AuthHeadingProps {
120
137
  /** Email to verify */
121
138
  email: string;
122
139
  /** Callback on successful verification */
123
140
  onSuccess?: () => void;
124
141
  /** URL to navigate to on success */
125
142
  successUrl?: string;
143
+ /** Link to the login page */
144
+ loginUrl?: string;
145
+ /** Copy overrides; anything omitted keeps the French default */
146
+ labels?: VerifyEmailFormLabels;
126
147
  /** Auth client instance */
127
148
  authClient: any;
128
149
  }
@@ -174,9 +195,24 @@ interface RegisterFormLabels {
174
195
  passwordTooShort?: string;
175
196
  signUpFailed?: string;
176
197
  }
177
- interface LoginFormProps {
198
+ /**
199
+ * Styling of the form's own heading. The package fixes the structure and a
200
+ * neutral default; the app fixes the type. Passing a class replaces the
201
+ * default size and weight, so pass the whole look, not an addition to it.
202
+ */
203
+ interface AuthHeadingProps {
204
+ /** Replaces the default `text-2xl font-semibold tracking-tight` */
205
+ titleClassName?: string;
206
+ }
207
+ interface LoginFormProps extends AuthHeadingProps {
178
208
  /** Callback once the session cookie is set and the core token minted */
179
209
  onSuccess?: () => void;
210
+ /**
211
+ * Error raised outside the form — a failed OAuth round-trip coming back as
212
+ * `?error=`, a guard bouncing an unauthenticated visitor. Rendered in the
213
+ * same banner as the form's own errors, and superseded by them on submit.
214
+ */
215
+ error?: string;
180
216
  /** Link to the registration page */
181
217
  registerUrl?: string;
182
218
  /** Link to the password recovery page */
@@ -199,9 +235,11 @@ interface LoginFormProps {
199
235
  /** Auth client instance */
200
236
  authClient: any;
201
237
  }
202
- interface RegisterFormProps {
238
+ interface RegisterFormProps extends AuthHeadingProps {
203
239
  /** Callback on successful sign-up, receives the email to verify */
204
240
  onSuccess?: (email: string) => void;
241
+ /** Error raised outside the form, rendered in the same banner */
242
+ error?: string;
205
243
  /** Link to the login page */
206
244
  loginUrl?: string;
207
245
  /** Rendered under the submit button — typically terms and privacy links */
@@ -215,32 +253,70 @@ interface RegisterFormProps {
215
253
  /** Auth client instance */
216
254
  authClient: any;
217
255
  }
218
- interface ForgotPasswordFormProps {
256
+ interface ForgotPasswordFormLabels {
257
+ title?: string;
258
+ subtitle?: string;
259
+ emailPlaceholder?: string;
260
+ submit?: string;
261
+ submitPending?: string;
262
+ rememberPassword?: string;
263
+ login?: string;
264
+ emailRequired?: string;
265
+ sendFailed?: string;
266
+ }
267
+ interface ForgotPasswordFormProps extends AuthHeadingProps {
219
268
  /** Callback on successful OTP send, receives the email */
220
269
  onSuccess?: (email: string) => void;
221
270
  /** Link to login page */
222
271
  loginUrl?: string;
272
+ /** Copy overrides; anything omitted keeps the French default */
273
+ labels?: ForgotPasswordFormLabels;
223
274
  /** Auth client instance */
224
275
  authClient: any;
225
276
  }
226
- interface ResetPasswordFormProps {
277
+ interface ResetPasswordFormLabels {
278
+ title?: string;
279
+ subtitle?: string;
280
+ codePlaceholder?: string;
281
+ passwordPlaceholder?: string;
282
+ passwordHint?: string;
283
+ confirmPlaceholder?: string;
284
+ submit?: string;
285
+ submitPending?: string;
286
+ resend?: string;
287
+ resendPending?: string;
288
+ resent?: string;
289
+ rememberPassword?: string;
290
+ login?: string;
291
+ codeRequired?: string;
292
+ passwordTooShort?: string;
293
+ passwordMismatch?: string;
294
+ resetFailed?: string;
295
+ resendFailed?: string;
296
+ }
297
+ interface ResetPasswordFormProps extends AuthHeadingProps {
227
298
  /** Email address to reset password for */
228
299
  email: string;
229
300
  /** Callback on successful password reset */
230
301
  onSuccess?: () => void;
231
302
  /** Link to login page */
232
303
  loginUrl?: string;
304
+ /** Copy overrides; anything omitted keeps the French default */
305
+ labels?: ResetPasswordFormLabels;
233
306
  /** Auth client instance */
234
307
  authClient: any;
235
308
  }
236
309
  interface AuthLayoutProps {
237
- /** Logo element to display at the top */
310
+ /** The app's mark, rendered above the form inside the card */
238
311
  logo?: React.ReactNode;
239
- /** Page title */
240
- title: string;
241
- /** Subtitle below the title */
242
- subtitle?: string;
243
- /** Content to render inside the card */
312
+ /**
313
+ * Illustration filling the right half of the card from md up, dropped below
314
+ * it. Any node: an `<img className="h-full w-full object-cover">`, a
315
+ * gradient, a testimonial. Purely decorative — it is hidden from assistive
316
+ * technology, so it must not carry anything the form does not say.
317
+ */
318
+ panel?: React.ReactNode;
319
+ /** The form. It renders its own title. */
244
320
  children: React.ReactNode;
245
321
  /** Footer content below the card (e.g. legal links) */
246
322
  footer?: React.ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lalternative/auth",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Shared Better Auth wrapper for L'Alternative apps (server + React client + auth UI)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",