@lalternative/auth 0.9.1 → 0.9.3

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/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createAuthClient } from 'better-auth/react';
2
- import { d as AuthClientSurface, P as PlatformAuthClientConfig } from './types-DzG-qaSv.js';
2
+ import { d as AuthClientSurface, P as PlatformAuthClientConfig } from './types-BFmX_pUl.js';
3
3
  import 'better-auth';
4
4
 
5
5
  /**
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { L as LoginFormProps, R as RegisterFormProps, V as VerifyEmailFormProps, F as ForgotPasswordFormProps, a as ResetPasswordFormProps, A as AuthLayoutProps, I as InvitationNoticeProps, b as AuthClientResult, c as LinkComponent } from './types-DzG-qaSv.js';
2
- export { d as AuthClientSurface, e as AuthInviteProps, f as AuthNavProps, g as AuthThemeProps, h as InvitationFailure, i as LoginFormLabels, P as PlatformAuthClientConfig, j as PlatformAuthConfig, k as PlatformAuthMailer, l as PlatformAuthMailerArgs, m as PlatformAuthMailerType, n as PlatformSession, o as PlatformSessionData, p as PlatformUser, q as RegisterFormLabels } from './types-DzG-qaSv.js';
1
+ import { L as LoginFormProps, R as RegisterFormProps, V as VerifyEmailFormProps, F as ForgotPasswordFormProps, a as ResetPasswordFormProps, A as AuthLayoutProps, I as InvitationNoticeProps, b as AuthClientResult, c as LinkComponent } from './types-BFmX_pUl.js';
2
+ export { d as AuthClientSurface, e as AuthInviteProps, f as AuthNavProps, g as AuthThemeProps, h as InvitationFailure, i as LoginFormLabels, P as PlatformAuthClientConfig, j as PlatformAuthConfig, k as PlatformAuthMailer, l as PlatformAuthMailerArgs, m as PlatformAuthMailerType, n as PlatformSession, o as PlatformSessionData, p as PlatformUser, q as RegisterFormLabels } from './types-BFmX_pUl.js';
3
3
  import * as better_auth_react from 'better-auth/react';
4
4
  import * as better_auth from 'better-auth';
5
5
  import { PlatformAuthClient } from './client.js';
6
6
  import * as react from 'react';
7
7
  import { InputHTMLAttributes, ReactNode } from 'react';
8
- export { C as ClaimOutcome, i as isInvitationFailure } from './invitation-0CfjrVjI.js';
8
+ export { C as ClaimOutcome, i as isInvitationFailure } from './invitation-DwKUO5oh.js';
9
9
 
10
10
  /**
11
11
  * Returns a useSession hook bound to the given auth client.
@@ -109,7 +109,7 @@ declare namespace LoginForm {
109
109
  };
110
110
  }
111
111
 
112
- declare function RegisterForm({ onSuccess, loginUrl, legal, socialCallbackUrl, socialProviders, labels, submitClassName, fieldClassName, error: externalError, linkComponent, invite, authClient, }: RegisterFormProps): react.JSX.Element;
112
+ declare function RegisterForm({ lockedEmail, onSuccess, loginUrl, legal, socialCallbackUrl, socialProviders, labels, submitClassName, fieldClassName, error: externalError, linkComponent, invite, authClient, }: RegisterFormProps): react.JSX.Element;
113
113
  declare namespace RegisterForm {
114
114
  var defaults: {
115
115
  title: string;
package/dist/index.js CHANGED
@@ -166,8 +166,15 @@ function AuthAlert({ children, tone = "error" }) {
166
166
  "aria-live": tone === "error" ? "assertive" : "polite",
167
167
  className: children ? [
168
168
  "rounded-lg border px-3.5 py-3 text-sm leading-snug",
169
+ "motion-safe:animate-[auth-alert-in_180ms_ease-out]",
169
170
  tone === "error" ? "border-destructive/25 bg-destructive/10 text-destructive" : "border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400"
170
- ].join(" ") : void 0,
171
+ ].join(" ") : (
172
+ // Empty it must stay in the tree for the live region to fire, but
173
+ // it must not stay in the layout: a `space-y` counts an empty
174
+ // child as a row, which pushed the first field down by a full step
175
+ // on every screen with no message to show.
176
+ "hidden"
177
+ ),
171
178
  children
172
179
  }
173
180
  );
@@ -475,7 +482,9 @@ var DEFAULTS2 = {
475
482
  title: "Cr\xE9er un compte",
476
483
  subtitle: "Nous t'enverrons un code pour confirmer ton adresse e-mail.",
477
484
  namePlaceholder: "Nom complet",
485
+ optional: "facultatif",
478
486
  emailPlaceholder: "Adresse e-mail",
487
+ emailLocked: "Ton invitation est li\xE9e \xE0 cette adresse.",
479
488
  passwordPlaceholder: "Mot de passe",
480
489
  passwordHint: `Au moins ${MIN_PASSWORD_LENGTH} caract\xE8res.`,
481
490
  confirmPlaceholder: "Confirme le mot de passe",
@@ -484,12 +493,12 @@ var DEFAULTS2 = {
484
493
  submitPending: "Cr\xE9ation\u2026",
485
494
  haveAccount: "Tu as d\xE9j\xE0 un compte ?",
486
495
  login: "Se connecter",
487
- nameRequired: "Renseigne ton nom",
488
496
  emailRequired: "Renseigne ton adresse e-mail",
489
497
  passwordTooShort: `Le mot de passe doit faire au moins ${MIN_PASSWORD_LENGTH} caract\xE8res`,
490
498
  signUpFailed: "La cr\xE9ation du compte a \xE9chou\xE9"
491
499
  };
492
500
  function RegisterForm({
501
+ lockedEmail,
493
502
  onSuccess,
494
503
  loginUrl = "/login",
495
504
  legal,
@@ -505,7 +514,8 @@ function RegisterForm({
505
514
  }) {
506
515
  const t = { ...DEFAULTS2, ...labels };
507
516
  const [name, setName] = useState3("");
508
- const [email, setEmail] = useState3("");
517
+ const [typedEmail, setTypedEmail] = useState3("");
518
+ const email = lockedEmail ?? typedEmail;
509
519
  const [password, setPassword] = useState3("");
510
520
  const [confirmPassword, setConfirmPassword] = useState3("");
511
521
  const [ownError, setOwnError] = useState3();
@@ -516,10 +526,6 @@ function RegisterForm({
516
526
  const mismatch = confirmPassword.length > 0 && confirmPassword !== password;
517
527
  const handleSubmit = async (e) => {
518
528
  e.preventDefault();
519
- if (!name.trim()) {
520
- setError(t.nameRequired);
521
- return;
522
- }
523
529
  if (!email.trim()) {
524
530
  setError(t.emailRequired);
525
531
  return;
@@ -536,7 +542,10 @@ function RegisterForm({
536
542
  setIsPending(true);
537
543
  try {
538
544
  const res = await authClient.signUp.email({
539
- name: name.trim(),
545
+ // The key is omitted rather than sent empty when the field is left
546
+ // blank: the server decides what an account without a name is called,
547
+ // and the client does not invent one from the address.
548
+ ...name.trim() ? { name: name.trim() } : {},
540
549
  email: email.trim(),
541
550
  password
542
551
  });
@@ -574,10 +583,10 @@ function RegisterForm({
574
583
  type: "text",
575
584
  value: name,
576
585
  onChange: (e) => setName(e.target.value),
577
- required: true,
578
586
  disabled: isPending,
579
587
  autoComplete: "name",
580
- fieldClassName
588
+ fieldClassName,
589
+ hint: /* @__PURE__ */ jsx7("span", { className: "text-xs text-muted-foreground", children: t.optional })
581
590
  }
582
591
  ),
583
592
  /* @__PURE__ */ jsx7(
@@ -587,12 +596,14 @@ function RegisterForm({
587
596
  type: "email",
588
597
  inputMode: "email",
589
598
  value: email,
590
- onChange: (e) => setEmail(e.target.value),
599
+ onChange: (e) => setTypedEmail(e.target.value),
591
600
  required: true,
601
+ readOnly: !!lockedEmail,
592
602
  disabled: isPending,
593
603
  autoComplete: "email",
594
604
  autoCapitalize: "none",
595
605
  spellCheck: false,
606
+ description: lockedEmail ? t.emailLocked : void 0,
596
607
  fieldClassName
597
608
  }
598
609
  ),
@@ -685,7 +696,7 @@ function AuthOtpField({
685
696
  "label",
686
697
  {
687
698
  htmlFor: id,
688
- className: "block text-sm font-medium leading-none text-foreground",
699
+ className: "block text-[0.6875rem] font-semibold uppercase leading-none tracking-[0.09em] text-foreground/70",
689
700
  children: label
690
701
  }
691
702
  ),
@@ -1144,7 +1155,7 @@ function AuthHeading({
1144
1155
  titleClassName = "text-[2rem] font-semibold leading-[1.1] tracking-[-0.03em] sm:text-[2.25rem]"
1145
1156
  }) {
1146
1157
  return /* @__PURE__ */ jsxs10("header", { className: "space-y-2.5 text-center", children: [
1147
- /* @__PURE__ */ jsx12("h1", { className: `text-foreground ${titleClassName}`, children: title }),
1158
+ /* @__PURE__ */ jsx12("h1", { className: titleClassName, children: title }),
1148
1159
  subtitle && /* @__PURE__ */ jsx12("p", { className: "text-balance text-[0.9375rem] leading-relaxed text-muted-foreground", children: subtitle })
1149
1160
  ] });
1150
1161
  }
@@ -1160,47 +1171,87 @@ function AuthLayout({
1160
1171
  children,
1161
1172
  footer
1162
1173
  }) {
1163
- return /* @__PURE__ */ jsx13("div", { className: "flex min-h-dvh flex-col bg-background px-5 pb-12 pt-14 sm:items-center sm:bg-muted/30 sm:px-6 sm:py-20", children: /* @__PURE__ */ jsxs11("div", { className: `mx-auto w-full ${panel ? "max-w-4xl" : "max-w-[400px]"}`, children: [
1164
- (logo || title) && // The spacing is composed, not repeated: the mark sits close to the
1165
- // title it introduces, and the gap down to the card is the largest
1166
- // on the screen — that step is what separates "who this is" from
1167
- // "what you do here".
1168
- /* @__PURE__ */ jsxs11("div", { className: "mb-10 space-y-6", children: [
1169
- logo && /* @__PURE__ */ jsx13("div", { className: "flex justify-center", children: logo }),
1170
- title && /* @__PURE__ */ jsx13(
1171
- AuthHeading,
1172
- {
1173
- title,
1174
- subtitle,
1175
- titleClassName
1176
- }
1177
- )
1178
- ] }),
1179
- /* @__PURE__ */ jsxs11(
1180
- "div",
1181
- {
1182
- className: [
1183
- "sm:rounded-xl sm:border sm:border-foreground/[0.08] sm:bg-card",
1184
- "sm:shadow-[0_1px_2px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.10)]",
1185
- panel ? "sm:overflow-hidden md:grid md:grid-cols-2" : ""
1186
- ].filter(Boolean).join(" "),
1187
- children: [
1188
- panel && // Decorative: it must never carry information the form does not,
1189
- // so it is hidden from assistive tech rather than described.
1190
- /* @__PURE__ */ jsx13(
1174
+ return /* @__PURE__ */ jsxs11(
1175
+ "div",
1176
+ {
1177
+ className: [
1178
+ "relative flex min-h-dvh flex-col px-5 pb-12 pt-14 sm:items-center sm:px-6 sm:py-20",
1179
+ // With a panel the colour IS the mobile ground and the card sits on
1180
+ // it; the split card only exists once there is width for two columns.
1181
+ panel ? "bg-transparent md:bg-muted/30" : "bg-background sm:bg-muted/30"
1182
+ ].join(" "),
1183
+ children: [
1184
+ panel && // A band, not a full ground: the card below reaches the bottom of the
1185
+ // screen, so the colour only has to sit behind the heading. Covering
1186
+ // the whole height would leave the panel's own copy showing under the
1187
+ // card, which reads as a second, half-hidden screen.
1188
+ /* @__PURE__ */ jsx13(
1189
+ "div",
1190
+ {
1191
+ "aria-hidden": "true",
1192
+ className: "absolute inset-x-0 top-0 -z-10 h-64 overflow-hidden md:hidden [&>*]:h-full [&>*]:w-full [&>img]:object-cover",
1193
+ children: panel
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsxs11("div", { className: `mx-auto w-full ${panel ? "max-w-4xl" : "max-w-[440px]"}`, children: [
1197
+ (logo || title) && // The mark sits tight above the title so the two read as one block
1198
+ // rather than as a stray label; the gap down to the card is the
1199
+ // largest on the screen that step is what separates "who this is"
1200
+ // from "what you do here".
1201
+ /* @__PURE__ */ jsxs11(
1191
1202
  "div",
1192
1203
  {
1193
- "aria-hidden": "true",
1194
- className: "relative hidden overflow-hidden bg-muted md:block [&>*]:absolute [&>*]:inset-0 [&>*]:h-full [&>*]:w-full [&>img]:object-cover",
1195
- children: panel
1204
+ className: [
1205
+ "mb-8 space-y-3",
1206
+ // Over the colour field the heading is on the panel, not on the
1207
+ // page, so it takes the panel's ink until the split puts it back
1208
+ // on a light ground.
1209
+ panel ? "text-white [&_p]:text-white/75 md:text-foreground md:[&_p]:text-muted-foreground" : ""
1210
+ ].filter(Boolean).join(" "),
1211
+ children: [
1212
+ logo && /* @__PURE__ */ jsx13("div", { className: "flex justify-center", children: logo }),
1213
+ title && /* @__PURE__ */ jsx13(
1214
+ AuthHeading,
1215
+ {
1216
+ title,
1217
+ subtitle,
1218
+ titleClassName
1219
+ }
1220
+ )
1221
+ ]
1196
1222
  }
1197
1223
  ),
1198
- /* @__PURE__ */ jsx13("div", { className: "sm:px-7 sm:py-8", children })
1199
- ]
1200
- }
1201
- ),
1202
- footer && /* @__PURE__ */ jsx13("div", { className: "mt-8 text-center text-xs leading-relaxed text-muted-foreground", children: footer })
1203
- ] }) });
1224
+ /* @__PURE__ */ jsxs11(
1225
+ "div",
1226
+ {
1227
+ className: [
1228
+ "sm:rounded-xl sm:border sm:border-foreground/[0.08] sm:bg-card",
1229
+ "sm:shadow-[0_1px_2px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.10)]",
1230
+ // On the colour band the card is a sheet rising from the bottom of
1231
+ // the screen: rounded at the top only, and stretched past the
1232
+ // viewport so no ground shows beneath it however short the form.
1233
+ panel ? "-mx-5 -mb-12 min-h-[60vh] rounded-t-2xl bg-card shadow-[0_-8px_32px_-12px_rgba(0,0,0,0.18)] sm:mx-0 sm:mb-0 sm:min-h-0 sm:overflow-hidden sm:rounded-xl md:grid md:grid-cols-2" : ""
1234
+ ].filter(Boolean).join(" "),
1235
+ children: [
1236
+ panel && // Decorative: it must never carry information the form does not,
1237
+ // so it is hidden from assistive tech rather than described.
1238
+ /* @__PURE__ */ jsx13(
1239
+ "div",
1240
+ {
1241
+ "aria-hidden": "true",
1242
+ className: "relative hidden overflow-hidden bg-muted md:block [&>*]:absolute [&>*]:inset-0 [&>*]:h-full [&>*]:w-full [&>img]:object-cover",
1243
+ children: panel
1244
+ }
1245
+ ),
1246
+ /* @__PURE__ */ jsx13("div", { className: panel ? "px-5 pb-12 pt-7 sm:p-7" : "sm:p-7", children })
1247
+ ]
1248
+ }
1249
+ ),
1250
+ footer && /* @__PURE__ */ jsx13("div", { className: "mt-8 text-center text-xs leading-relaxed text-muted-foreground", children: footer })
1251
+ ] })
1252
+ ]
1253
+ }
1254
+ );
1204
1255
  }
1205
1256
 
1206
1257
  // src/components/invitation-notice.tsx
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/email-not-verified.ts","../src/components/auth-alert.tsx","../src/components/social-buttons.tsx","../src/components/auth-link.tsx","../src/invite-token.ts","../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-heading.tsx","../src/components/auth-layout.tsx","../src/components/invitation-notice.tsx","../src/oauth-error.ts"],"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 /** Replaces the default border and background utilities */\n fieldClassName?: string\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 fieldClassName = \"border-foreground/30 bg-background\",\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 {/* Small caps with wide tracking: at this size the label reads as a\n field marker rather than as prose competing with the heading. */}\n <label\n htmlFor={id}\n className=\"text-[0.6875rem] font-semibold uppercase leading-none tracking-[0.09em] text-foreground/70\"\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-md border px-3.5 text-base\",\n \"sm:h-[46px] sm:text-[0.9375rem]\",\n isPassword ? \"pr-12\" : \"\",\n \"text-foreground placeholder:text-muted-foreground/60\",\n // Focus reads as the border committing rather than as a halo\n // appearing beside it: the ring is tight and the border darkens to\n // full ink in the same 180ms, so the field answers the caret.\n \"transition-[border-color,box-shadow] duration-200 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-2\",\n invalid\n ? \"border-destructive bg-background focus-visible:border-destructive focus-visible:ring-destructive/15\"\n // The default border is foreground/30, not border-input: the\n // latter against a white card lands near 1.3:1, well under the\n // 3:1 WCAG 1.4.11 asks of a control's boundary — the field reads\n // as a faint tint rather than as something to type in.\n : `${fieldClassName} focus-visible:border-foreground focus-visible:ring-foreground/10`,\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 /** Replaces the default `bg-primary text-primary-foreground hover:bg-primary/90` */\n className?: string\n /**\n * Adds the step that detaches the action from the fields above it. The\n * inputs sit on a tighter rhythm so they read as one block to fill in; the\n * button is what you do once that block is done, and an even gap would put\n * it on the same footing as another field.\n */\n spacedAbove?: boolean\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 className = \"bg-primary text-primary-foreground hover:bg-primary/90\",\n spacedAbove = false,\n}: AuthSubmitProps) {\n return (\n <button\n type=\"submit\"\n disabled={disabled || pending}\n aria-busy={pending || undefined}\n className={[\n spacedAbove ? \"!mt-7\" : \"\",\n \"relative flex h-[52px] w-full items-center justify-center rounded-md sm:h-[46px]\",\n // Slightly tracked at this weight: a wide solid button set in plain\n // medium reads as a slab, and the letterspacing is what makes it read\n // as typeset rather than filled in.\n \"text-[0.9375rem] font-medium tracking-[0.01em]\",\n className,\n \"transition-[background-color,transform,box-shadow] duration-200 ease-out\",\n \"shadow-[0_1px_2px_rgba(0,0,0,0.08)] hover:shadow-[0_2px_8px_rgba(0,0,0,0.12)]\",\n \"active:translate-y-px active:shadow-[0_1px_2px_rgba(0,0,0,0.08)]\",\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 { isEmailNotVerified } from \"../email-not-verified\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthSubmit } from \"./auth-submit\"\nimport { SocialButtons } from \"./social-buttons\"\nimport { AUTH_HINT_LINK_CLASS, AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport { withInviteToken } from \"../invite-token\"\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 emailNotVerified:\n \"Ton adresse e-mail n'est pas encore confirmée. Vérifie ta boîte de réception.\",\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 onEmailNotVerified,\n registerUrl = \"/register\",\n forgotPasswordUrl = \"/forgot-password\",\n socialCallbackUrl = \"/\",\n socialProviders = [],\n coreTokenUrl = \"/api/auth/core-token\",\n labels,\n submitClassName,\n fieldClassName,\n error: externalError,\n linkComponent,\n invite,\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 if (isEmailNotVerified(res.error) && onEmailNotVerified) {\n onEmailNotVerified(email.trim())\n return\n }\n setError(\n isEmailNotVerified(res.error)\n ? t.emailNotVerified\n : (res.error.message ?? t.invalidCredentials),\n )\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 // The invitation rides the callback: an OAuth sign-up leaves the\n // browser, and the auth handler redeems the token on the way back.\n callbackURL: withInviteToken(socialCallbackUrl, invite),\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 <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" 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 fieldClassName={fieldClassName}\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 fieldClassName={fieldClassName}\n hint={\n <AuthLink\n to={forgotPasswordUrl}\n as={linkComponent}\n className={AUTH_HINT_LINK_CLASS}\n >\n {t.forgotPassword}\n </AuthLink>\n }\n />\n\n <AuthSubmit\n spacedAbove\n pending={isPending}\n disabled={!email.trim() || !password}\n pendingLabel={t.submitPending}\n className={submitClassName}\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 <AuthLink\n to={withInviteToken(registerUrl, invite)}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.register}\n </AuthLink>\n </p>\n </div>\n )\n}\n\n// The heading lives in AuthLayout, above the card, so the screen — not the\n// form — passes the copy. Exposing the defaults here keeps the wording in one\n// place: `<AuthLayout {...LoginForm.defaults}>` renders what the form used to.\nLoginForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\n}\n","import type { AuthClientResult } from \"./types\"\n\n/**\n * Whether a failed sign-in was refused for want of a confirmed address.\n *\n * Better Auth answers an unverified sign-in with EMAIL_NOT_VERIFIED, but only\n * when built with its error codes exposed; otherwise the refusal arrives as a\n * bare 403. No other credential failure on this route uses that status — a\n * wrong password is a 401 — so the status alone is a safe fallback.\n */\nexport function isEmailNotVerified(\n error: AuthClientResult[\"error\"],\n): boolean {\n if (!error) return false\n return error.code === \"EMAIL_NOT_VERIFIED\" || error.status === 403\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-lg 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","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-lg sm:h-11\",\n \"border border-foreground/25 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 type { ReactNode } from \"react\"\nimport type { LinkComponent } from \"../types\"\n\ninterface AuthLinkProps {\n to: string\n as?: LinkComponent\n className?: string\n children: ReactNode\n}\n\n/**\n * A link between auth screens.\n *\n * Falls back to an anchor, which is right for an app without a router and wrong\n * for every app with one: the full page load it triggers restarts the app and\n * loses whatever the URL was carrying.\n */\nexport function AuthLink({ to, as: Link, className, children }: AuthLinkProps) {\n if (Link) {\n return (\n <Link to={to} className={className}>\n {children}\n </Link>\n )\n }\n return (\n <a href={to} className={className}>\n {children}\n </a>\n )\n}\n\nexport const AUTH_LINK_CLASS =\n \"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\nexport const AUTH_HINT_LINK_CLASS =\n \"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 * An invitation token as it may appear in a URL, reduced to something safe to\n * carry through a route search schema.\n *\n * Callers pass raw search params (`Route.validateSearch`), so the input is\n * whatever the address bar held: an array when the param repeats, a number, or\n * a string long enough to be an attack rather than a token. Anything that is\n * not one plausible token collapses to undefined, which reads as \"no\n * invitation\" everywhere downstream.\n */\nconst MAX_TOKEN_LENGTH = 128\n\nexport function normalizeInviteToken(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined\n const trimmed = value.trim()\n if (!trimmed || trimmed.length > MAX_TOKEN_LENGTH) return undefined\n return trimmed\n}\n\n/**\n * Appends the invitation to a link between auth screens.\n *\n * An invitee who lands on /register and clicks through to /login must keep the\n * offer: the token lives only in the URL, so a plain href to the sibling screen\n * silently drops it and the account is created on the default tier.\n */\nexport function withInviteToken(href: string, token?: string): string {\n if (!token) return href\n const separator = href.includes(\"?\") ? \"&\" : \"?\"\n return `${href}${separator}invite=${encodeURIComponent(token)}`\n}\n","import { useState, type FormEvent } from \"react\"\nimport { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport { withInviteToken } from \"../invite-token\"\nimport type { RegisterFormLabels, RegisterFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\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 confirmPlaceholder: \"Confirme le mot de passe\",\n passwordMismatch: \"Les deux mots de passe ne correspondent pas\",\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 submitClassName,\n fieldClassName,\n error: externalError,\n linkComponent,\n invite,\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 [confirmPassword, setConfirmPassword] = 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 const mismatch = confirmPassword.length > 0 && confirmPassword !== password\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 if (password !== confirmPassword) {\n setError(t.passwordMismatch)\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 // The invitation rides the callback: an OAuth sign-up leaves the\n // browser, and the auth handler redeems the token on the way back.\n callbackURL: withInviteToken(socialCallbackUrl, invite),\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 <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" 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 fieldClassName={fieldClassName}\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 fieldClassName={fieldClassName}\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 fieldClassName={fieldClassName}\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={isPending}\n autoComplete=\"new-password\"\n invalid={mismatch}\n fieldClassName={fieldClassName}\n />\n\n <AuthSubmit\n spacedAbove\n pending={isPending}\n pendingLabel={t.submitPending}\n className={submitClassName}\n >\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 <AuthLink\n to={withInviteToken(loginUrl, invite)}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nRegisterForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\n}\n","import { useState, type FormEvent } from \"react\"\nimport { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport type { VerifyEmailFormLabels, VerifyEmailFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\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 submitClassName,\n fieldClassName,\n linkComponent,\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 <AuthAlert>{error}</AuthAlert>\n <AuthAlert tone=\"success\">{resendMessage}</AuthAlert>\n\n <form onSubmit={handleVerify} className=\"space-y-[1.125rem]\" noValidate>\n <AuthOtpField\n id=\"verify-email-otp\"\n label={t.codePlaceholder}\n value={otp}\n onChange={setOtp}\n disabled={isVerifying}\n fieldClassName={fieldClassName}\n />\n\n <AuthSubmit\n spacedAbove\n pending={isVerifying}\n disabled={otp.length < OTP_LENGTH}\n pendingLabel={t.submitPending}\n className={submitClassName}\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 <AuthLink\n to={loginUrl}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nVerifyEmailForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\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 /** Replaces the default border and background utilities */\n fieldClassName?: 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 fieldClassName = \"border-foreground/25 bg-background\",\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-lg border 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 bg-background focus-visible:border-destructive focus-visible:ring-destructive/20\"\n : `${fieldClassName} 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 { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport type {\n ForgotPasswordFormLabels,\n ForgotPasswordFormProps,\n} from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\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 submitClassName,\n fieldClassName,\n linkComponent,\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 <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" 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 spacedAbove\n pending={isPending}\n disabled={!email.trim()}\n pendingLabel={t.submitPending}\n className={submitClassName}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.rememberPassword}{\" \"}\n <AuthLink\n to={loginUrl}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nForgotPasswordForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\n}\n","import { useState, type FormEvent } from \"react\"\nimport { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport type { ResetPasswordFormLabels, ResetPasswordFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\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 submitClassName,\n fieldClassName,\n linkComponent,\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 <AuthAlert>{error}</AuthAlert>\n <AuthAlert tone=\"success\">{resendMessage}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" noValidate>\n <AuthOtpField\n id=\"reset-password-otp\"\n label={t.codePlaceholder}\n value={otp}\n onChange={setOtp}\n disabled={isResetting}\n fieldClassName={fieldClassName}\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 spacedAbove\n pending={isResetting}\n disabled={otp.length < OTP_LENGTH || !password || !confirmPassword}\n pendingLabel={t.submitPending}\n className={submitClassName}\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 <AuthLink\n to={loginUrl}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nResetPasswordForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\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.\n *\n * This screen is the only one someone sees before they have any reason to\n * trust the product, so the type carries it: the title is the screen's centre\n * of gravity, set large and tightly tracked, with the subtitle stepping well\n * back rather than competing.\n *\n * Optical sizing matters at this weight — `text-balance` keeps a two-line\n * subtitle from breaking into a lonely last word, which reads as an accident\n * where everything else is deliberate.\n */\nexport function AuthHeading({\n title,\n subtitle,\n titleClassName = \"text-[2rem] font-semibold leading-[1.1] tracking-[-0.03em] sm:text-[2.25rem]\",\n}: AuthHeadingProps) {\n return (\n <header className=\"space-y-2.5 text-center\">\n <h1 className={`text-foreground ${titleClassName}`}>{title}</h1>\n {subtitle && (\n <p className=\"text-balance text-[0.9375rem] leading-relaxed text-muted-foreground\">\n {subtitle}\n </p>\n )}\n </header>\n )\n}\n","import type { AuthLayoutProps } from \"../types\"\nimport { AuthHeading } from \"./auth-heading\"\n\n/**\n * The frame every auth screen sits in. It owns the ground, the heading and the\n * app's own marks (logo, illustration, legal footer); the card holds only the\n * fields and their actions.\n *\n * The heading sits ABOVE the card rather than inside it: the card is then\n * exactly the thing you fill in, and the mark reads as the app's rather than\n * as the form's first row.\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 * A `panel` is an app-supplied illustration and nothing else: without one the\n * card stays a single column rather than inventing decoration to fill a half\n * it has no content for. When given, it takes the LEFT half from md up and is\n * dropped below that width — a decorative half-screen above a form costs a\n * full swipe before the first field.\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 title,\n subtitle,\n titleClassName,\n children,\n footer,\n}: AuthLayoutProps) {\n return (\n <div className=\"flex min-h-dvh flex-col bg-background px-5 pb-12 pt-14 sm:items-center sm:bg-muted/30 sm:px-6 sm:py-20\">\n <div className={`mx-auto w-full ${panel ? \"max-w-4xl\" : \"max-w-[400px]\"}`}>\n {(logo || title) && (\n // The spacing is composed, not repeated: the mark sits close to the\n // title it introduces, and the gap down to the card is the largest\n // on the screen — that step is what separates \"who this is\" from\n // \"what you do here\".\n <div className=\"mb-10 space-y-6\">\n {logo && <div className=\"flex justify-center\">{logo}</div>}\n {title && (\n <AuthHeading\n title={title}\n subtitle={subtitle}\n titleClassName={titleClassName}\n />\n )}\n </div>\n )}\n\n <div\n className={[\n \"sm:rounded-xl sm:border sm:border-foreground/[0.08] sm:bg-card\",\n \"sm:shadow-[0_1px_2px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.10)]\",\n panel ? \"sm:overflow-hidden md:grid md:grid-cols-2\" : \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\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=\"relative hidden overflow-hidden bg-muted md:block [&>*]:absolute [&>*]:inset-0 [&>*]:h-full [&>*]:w-full [&>img]:object-cover\"\n >\n {panel}\n </div>\n )}\n\n <div className=\"sm:px-7 sm:py-8\">{children}</div>\n </div>\n\n {footer && (\n <div className=\"mt-8 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","export type OAuthErrorLabels = {\n accountNotLinked: string\n socialCancelled: string\n socialFailed: string\n}\n\n/**\n * Turns Better Auth's machine-readable OAuth failure code into something an\n * invitee can act on.\n *\n * `account_not_linked` is the one worth naming: createPlatformAuth disables\n * account linking on purpose, so \"Continue with Google\" on an address already\n * registered with a password is REFUSED. Better Auth's default error route\n * bounces the browser back with nothing shown, so without this the button\n * simply looks broken.\n */\nexport function oauthErrorMessage(\n code: string | null | undefined,\n labels: OAuthErrorLabels,\n): string {\n switch (code) {\n case \"account_not_linked\":\n return labels.accountNotLinked\n case \"access_denied\":\n return labels.socialCancelled\n default:\n return labels.socialFailed\n }\n}\n\n/**\n * Reads the failure Better Auth redirected back with.\n *\n * A social sign-in leaves the app entirely, so component state does not survive\n * it: the only carrier left when the browser returns is the `?error=` Better\n * Auth appends to the errorCallbackURL. Read from the address bar rather than\n * from a typed route search, because the auth routes deliberately declare no\n * search schema — adding one would make `search` required on every navigate to\n * them across the app.\n */\nexport function initialOAuthError(\n labels: OAuthErrorLabels,\n param = \"error\",\n): string | undefined {\n if (typeof window === \"undefined\") return undefined\n const code = new URLSearchParams(window.location.search).get(param)\n if (!code) return undefined\n return oauthErrorMessage(code, labels)\n}\n\n/**\n * Drops the failure from the address bar once it has been shown.\n *\n * Without this the message comes back on every reload, and outlives the retry\n * that succeeded. replaceState rather than a router navigate: these screens\n * have no typed search to navigate against, and the entry being rewritten is\n * the one the OAuth provider pushed, not one the person chose.\n */\nexport function clearOAuthError(param = \"error\"): void {\n if (typeof window === \"undefined\") return\n const url = new URL(window.location.href)\n if (!url.searchParams.has(param)) return\n url.searchParams.delete(param)\n window.history.replaceState({}, \"\", url.toString())\n}\n"],"mappings":"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","names":["jsx","jsxs","useState","jsx","jsx","jsxs","jsx","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","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/email-not-verified.ts","../src/components/auth-alert.tsx","../src/components/social-buttons.tsx","../src/components/auth-link.tsx","../src/invite-token.ts","../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-heading.tsx","../src/components/auth-layout.tsx","../src/components/invitation-notice.tsx","../src/oauth-error.ts"],"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 /** Replaces the default border and background utilities */\n fieldClassName?: string\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 fieldClassName = \"border-foreground/30 bg-background\",\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 {/* Small caps with wide tracking: at this size the label reads as a\n field marker rather than as prose competing with the heading. */}\n <label\n htmlFor={id}\n className=\"text-[0.6875rem] font-semibold uppercase leading-none tracking-[0.09em] text-foreground/70\"\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-md border px-3.5 text-base\",\n \"sm:h-[46px] sm:text-[0.9375rem]\",\n isPassword ? \"pr-12\" : \"\",\n \"text-foreground placeholder:text-muted-foreground/60\",\n // Focus reads as the border committing rather than as a halo\n // appearing beside it: the ring is tight and the border darkens to\n // full ink in the same 180ms, so the field answers the caret.\n \"transition-[border-color,box-shadow] duration-200 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-2\",\n invalid\n ? \"border-destructive bg-background focus-visible:border-destructive focus-visible:ring-destructive/15\"\n // The default border is foreground/30, not border-input: the\n // latter against a white card lands near 1.3:1, well under the\n // 3:1 WCAG 1.4.11 asks of a control's boundary — the field reads\n // as a faint tint rather than as something to type in.\n : `${fieldClassName} focus-visible:border-foreground focus-visible:ring-foreground/10`,\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 /** Replaces the default `bg-primary text-primary-foreground hover:bg-primary/90` */\n className?: string\n /**\n * Adds the step that detaches the action from the fields above it. The\n * inputs sit on a tighter rhythm so they read as one block to fill in; the\n * button is what you do once that block is done, and an even gap would put\n * it on the same footing as another field.\n */\n spacedAbove?: boolean\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 className = \"bg-primary text-primary-foreground hover:bg-primary/90\",\n spacedAbove = false,\n}: AuthSubmitProps) {\n return (\n <button\n type=\"submit\"\n disabled={disabled || pending}\n aria-busy={pending || undefined}\n className={[\n spacedAbove ? \"!mt-7\" : \"\",\n \"relative flex h-[52px] w-full items-center justify-center rounded-md sm:h-[46px]\",\n // Slightly tracked at this weight: a wide solid button set in plain\n // medium reads as a slab, and the letterspacing is what makes it read\n // as typeset rather than filled in.\n \"text-[0.9375rem] font-medium tracking-[0.01em]\",\n className,\n \"transition-[background-color,transform,box-shadow] duration-200 ease-out\",\n \"shadow-[0_1px_2px_rgba(0,0,0,0.08)] hover:shadow-[0_2px_8px_rgba(0,0,0,0.12)]\",\n \"active:translate-y-px active:shadow-[0_1px_2px_rgba(0,0,0,0.08)]\",\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 { isEmailNotVerified } from \"../email-not-verified\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\nimport { AuthSubmit } from \"./auth-submit\"\nimport { SocialButtons } from \"./social-buttons\"\nimport { AUTH_HINT_LINK_CLASS, AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport { withInviteToken } from \"../invite-token\"\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 emailNotVerified:\n \"Ton adresse e-mail n'est pas encore confirmée. Vérifie ta boîte de réception.\",\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 onEmailNotVerified,\n registerUrl = \"/register\",\n forgotPasswordUrl = \"/forgot-password\",\n socialCallbackUrl = \"/\",\n socialProviders = [],\n coreTokenUrl = \"/api/auth/core-token\",\n labels,\n submitClassName,\n fieldClassName,\n error: externalError,\n linkComponent,\n invite,\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 if (isEmailNotVerified(res.error) && onEmailNotVerified) {\n onEmailNotVerified(email.trim())\n return\n }\n setError(\n isEmailNotVerified(res.error)\n ? t.emailNotVerified\n : (res.error.message ?? t.invalidCredentials),\n )\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 // The invitation rides the callback: an OAuth sign-up leaves the\n // browser, and the auth handler redeems the token on the way back.\n callbackURL: withInviteToken(socialCallbackUrl, invite),\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 <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" 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 fieldClassName={fieldClassName}\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 fieldClassName={fieldClassName}\n hint={\n <AuthLink\n to={forgotPasswordUrl}\n as={linkComponent}\n className={AUTH_HINT_LINK_CLASS}\n >\n {t.forgotPassword}\n </AuthLink>\n }\n />\n\n <AuthSubmit\n spacedAbove\n pending={isPending}\n disabled={!email.trim() || !password}\n pendingLabel={t.submitPending}\n className={submitClassName}\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 <AuthLink\n to={withInviteToken(registerUrl, invite)}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.register}\n </AuthLink>\n </p>\n </div>\n )\n}\n\n// The heading lives in AuthLayout, above the card, so the screen — not the\n// form — passes the copy. Exposing the defaults here keeps the wording in one\n// place: `<AuthLayout {...LoginForm.defaults}>` renders what the form used to.\nLoginForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\n}\n","import type { AuthClientResult } from \"./types\"\n\n/**\n * Whether a failed sign-in was refused for want of a confirmed address.\n *\n * Better Auth answers an unverified sign-in with EMAIL_NOT_VERIFIED, but only\n * when built with its error codes exposed; otherwise the refusal arrives as a\n * bare 403. No other credential failure on this route uses that status — a\n * wrong password is a 401 — so the status alone is a safe fallback.\n */\nexport function isEmailNotVerified(\n error: AuthClientResult[\"error\"],\n): boolean {\n if (!error) return false\n return error.code === \"EMAIL_NOT_VERIFIED\" || error.status === 403\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-lg border px-3.5 py-3 text-sm leading-snug\",\n \"motion-safe:animate-[auth-alert-in_180ms_ease-out]\",\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 : // Empty it must stay in the tree for the live region to fire, but\n // it must not stay in the layout: a `space-y` counts an empty\n // child as a row, which pushed the first field down by a full step\n // on every screen with no message to show.\n \"hidden\"\n }\n >\n {children}\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 {/* 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-lg sm:h-11\",\n \"border border-foreground/25 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 type { ReactNode } from \"react\"\nimport type { LinkComponent } from \"../types\"\n\ninterface AuthLinkProps {\n to: string\n as?: LinkComponent\n className?: string\n children: ReactNode\n}\n\n/**\n * A link between auth screens.\n *\n * Falls back to an anchor, which is right for an app without a router and wrong\n * for every app with one: the full page load it triggers restarts the app and\n * loses whatever the URL was carrying.\n */\nexport function AuthLink({ to, as: Link, className, children }: AuthLinkProps) {\n if (Link) {\n return (\n <Link to={to} className={className}>\n {children}\n </Link>\n )\n }\n return (\n <a href={to} className={className}>\n {children}\n </a>\n )\n}\n\nexport const AUTH_LINK_CLASS =\n \"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\nexport const AUTH_HINT_LINK_CLASS =\n \"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 * An invitation token as it may appear in a URL, reduced to something safe to\n * carry through a route search schema.\n *\n * Callers pass raw search params (`Route.validateSearch`), so the input is\n * whatever the address bar held: an array when the param repeats, a number, or\n * a string long enough to be an attack rather than a token. Anything that is\n * not one plausible token collapses to undefined, which reads as \"no\n * invitation\" everywhere downstream.\n */\nconst MAX_TOKEN_LENGTH = 128\n\nexport function normalizeInviteToken(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined\n const trimmed = value.trim()\n if (!trimmed || trimmed.length > MAX_TOKEN_LENGTH) return undefined\n return trimmed\n}\n\n/**\n * Appends the invitation to a link between auth screens.\n *\n * An invitee who lands on /register and clicks through to /login must keep the\n * offer: the token lives only in the URL, so a plain href to the sibling screen\n * silently drops it and the account is created on the default tier.\n */\nexport function withInviteToken(href: string, token?: string): string {\n if (!token) return href\n const separator = href.includes(\"?\") ? \"&\" : \"?\"\n return `${href}${separator}invite=${encodeURIComponent(token)}`\n}\n","import { useState, type FormEvent } from \"react\"\nimport { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport { withInviteToken } from \"../invite-token\"\nimport type { RegisterFormLabels, RegisterFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\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 optional: \"facultatif\",\n emailPlaceholder: \"Adresse e-mail\",\n emailLocked: \"Ton invitation est liée à cette adresse.\",\n passwordPlaceholder: \"Mot de passe\",\n passwordHint: `Au moins ${MIN_PASSWORD_LENGTH} caractères.`,\n confirmPlaceholder: \"Confirme le mot de passe\",\n passwordMismatch: \"Les deux mots de passe ne correspondent pas\",\n submit: \"Créer mon compte\",\n submitPending: \"Création…\",\n haveAccount: \"Tu as déjà un compte ?\",\n login: \"Se connecter\",\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 lockedEmail,\n onSuccess,\n loginUrl = \"/login\",\n legal,\n socialCallbackUrl = \"/\",\n socialProviders = [],\n labels,\n submitClassName,\n fieldClassName,\n error: externalError,\n linkComponent,\n invite,\n authClient,\n}: RegisterFormProps) {\n const t = { ...DEFAULTS, ...labels }\n const [name, setName] = useState(\"\")\n const [typedEmail, setTypedEmail] = useState(\"\")\n const email = lockedEmail ?? typedEmail\n const [password, setPassword] = useState(\"\")\n const [confirmPassword, setConfirmPassword] = 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 const mismatch = confirmPassword.length > 0 && confirmPassword !== password\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault()\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 if (password !== confirmPassword) {\n setError(t.passwordMismatch)\n return\n }\n setError(undefined)\n setIsPending(true)\n try {\n const res = await authClient.signUp.email({\n // The key is omitted rather than sent empty when the field is left\n // blank: the server decides what an account without a name is called,\n // and the client does not invent one from the address.\n ...(name.trim() ? { 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 // The invitation rides the callback: an OAuth sign-up leaves the\n // browser, and the auth handler redeems the token on the way back.\n callbackURL: withInviteToken(socialCallbackUrl, invite),\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 <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" noValidate>\n <AuthField\n label={t.namePlaceholder}\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n disabled={isPending}\n autoComplete=\"name\"\n fieldClassName={fieldClassName}\n hint={\n <span className=\"text-xs text-muted-foreground\">\n {t.optional}\n </span>\n }\n />\n\n <AuthField\n label={t.emailPlaceholder}\n type=\"email\"\n inputMode=\"email\"\n value={email}\n onChange={(e) => setTypedEmail(e.target.value)}\n required\n // readOnly rather than disabled: a disabled field is skipped by the\n // tab order and drops out of the accessibility tree, so the address\n // the account is being created for would go unread.\n readOnly={!!lockedEmail}\n disabled={isPending}\n autoComplete=\"email\"\n autoCapitalize=\"none\"\n spellCheck={false}\n description={lockedEmail ? t.emailLocked : undefined}\n fieldClassName={fieldClassName}\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 fieldClassName={fieldClassName}\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={isPending}\n autoComplete=\"new-password\"\n invalid={mismatch}\n fieldClassName={fieldClassName}\n />\n\n <AuthSubmit\n spacedAbove\n pending={isPending}\n pendingLabel={t.submitPending}\n className={submitClassName}\n >\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 <AuthLink\n to={withInviteToken(loginUrl, invite)}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nRegisterForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\n}\n","import { useState, type FormEvent } from \"react\"\nimport { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport type { VerifyEmailFormLabels, VerifyEmailFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\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 submitClassName,\n fieldClassName,\n linkComponent,\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 <AuthAlert>{error}</AuthAlert>\n <AuthAlert tone=\"success\">{resendMessage}</AuthAlert>\n\n <form onSubmit={handleVerify} className=\"space-y-[1.125rem]\" noValidate>\n <AuthOtpField\n id=\"verify-email-otp\"\n label={t.codePlaceholder}\n value={otp}\n onChange={setOtp}\n disabled={isVerifying}\n fieldClassName={fieldClassName}\n />\n\n <AuthSubmit\n spacedAbove\n pending={isVerifying}\n disabled={otp.length < OTP_LENGTH}\n pendingLabel={t.submitPending}\n className={submitClassName}\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 <AuthLink\n to={loginUrl}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nVerifyEmailForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\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 /** Replaces the default border and background utilities */\n fieldClassName?: 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 fieldClassName = \"border-foreground/25 bg-background\",\n}: AuthOtpFieldProps) {\n return (\n <div className=\"space-y-2\">\n <label\n htmlFor={id}\n className=\"block text-[0.6875rem] font-semibold uppercase leading-none tracking-[0.09em] text-foreground/70\"\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-lg border 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 bg-background focus-visible:border-destructive focus-visible:ring-destructive/20\"\n : `${fieldClassName} 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 { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport type {\n ForgotPasswordFormLabels,\n ForgotPasswordFormProps,\n} from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\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 submitClassName,\n fieldClassName,\n linkComponent,\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 <AuthAlert>{error}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" 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 spacedAbove\n pending={isPending}\n disabled={!email.trim()}\n pendingLabel={t.submitPending}\n className={submitClassName}\n >\n {t.submit}\n </AuthSubmit>\n </form>\n\n <p className=\"text-center text-sm text-muted-foreground\">\n {t.rememberPassword}{\" \"}\n <AuthLink\n to={loginUrl}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nForgotPasswordForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\n}\n","import { useState, type FormEvent } from \"react\"\nimport { AUTH_LINK_CLASS, AuthLink } from \"./auth-link\"\nimport type { ResetPasswordFormLabels, ResetPasswordFormProps } from \"../types\"\nimport { AuthAlert } from \"./auth-alert\"\nimport { AuthField } from \"./auth-field\"\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 submitClassName,\n fieldClassName,\n linkComponent,\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 <AuthAlert>{error}</AuthAlert>\n <AuthAlert tone=\"success\">{resendMessage}</AuthAlert>\n\n <form onSubmit={handleSubmit} className=\"space-y-[1.125rem]\" noValidate>\n <AuthOtpField\n id=\"reset-password-otp\"\n label={t.codePlaceholder}\n value={otp}\n onChange={setOtp}\n disabled={isResetting}\n fieldClassName={fieldClassName}\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 spacedAbove\n pending={isResetting}\n disabled={otp.length < OTP_LENGTH || !password || !confirmPassword}\n pendingLabel={t.submitPending}\n className={submitClassName}\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 <AuthLink\n to={loginUrl}\n as={linkComponent}\n className={AUTH_LINK_CLASS}\n >\n {t.login}\n </AuthLink>\n </p>\n </div>\n </div>\n )\n}\n\n// See LoginForm.defaults.\nResetPasswordForm.defaults = {\n title: DEFAULTS.title,\n subtitle: DEFAULTS.subtitle,\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.\n *\n * This screen is the only one someone sees before they have any reason to\n * trust the product, so the type carries it: the title is the screen's centre\n * of gravity, set large and tightly tracked, with the subtitle stepping well\n * back rather than competing.\n *\n * Optical sizing matters at this weight — `text-balance` keeps a two-line\n * subtitle from breaking into a lonely last word, which reads as an accident\n * where everything else is deliberate.\n */\nexport function AuthHeading({\n title,\n subtitle,\n titleClassName = \"text-[2rem] font-semibold leading-[1.1] tracking-[-0.03em] sm:text-[2.25rem]\",\n}: AuthHeadingProps) {\n return (\n <header className=\"space-y-2.5 text-center\">\n <h1 className={titleClassName}>{title}</h1>\n {subtitle && (\n <p className=\"text-balance text-[0.9375rem] leading-relaxed text-muted-foreground\">\n {subtitle}\n </p>\n )}\n </header>\n )\n}\n","import type { AuthLayoutProps } from \"../types\"\nimport { AuthHeading } from \"./auth-heading\"\n\n/**\n * The frame every auth screen sits in. It owns the ground, the heading and the\n * app's own marks (logo, illustration, legal footer); the card holds only the\n * fields and their actions.\n *\n * The heading sits ABOVE the card rather than inside it: the card is then\n * exactly the thing you fill in, and the mark reads as the app's rather than\n * as the form's first row.\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 * A `panel` is an app-supplied illustration and nothing else: without one the\n * card stays a single column rather than inventing decoration to fill a half\n * it has no content for. When given, it takes the LEFT half from md up and is\n * dropped below that width — a decorative half-screen above a form costs a\n * full swipe before the first field.\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 title,\n subtitle,\n titleClassName,\n children,\n footer,\n}: AuthLayoutProps) {\n return (\n <div\n className={[\n \"relative flex min-h-dvh flex-col px-5 pb-12 pt-14 sm:items-center sm:px-6 sm:py-20\",\n // With a panel the colour IS the mobile ground and the card sits on\n // it; the split card only exists once there is width for two columns.\n panel\n ? \"bg-transparent md:bg-muted/30\"\n : \"bg-background sm:bg-muted/30\",\n ].join(\" \")}\n >\n {panel && (\n // A band, not a full ground: the card below reaches the bottom of the\n // screen, so the colour only has to sit behind the heading. Covering\n // the whole height would leave the panel's own copy showing under the\n // card, which reads as a second, half-hidden screen.\n <div\n aria-hidden=\"true\"\n className=\"absolute inset-x-0 top-0 -z-10 h-64 overflow-hidden md:hidden [&>*]:h-full [&>*]:w-full [&>img]:object-cover\"\n >\n {panel}\n </div>\n )}\n\n <div className={`mx-auto w-full ${panel ? \"max-w-4xl\" : \"max-w-[440px]\"}`}>\n {(logo || title) && (\n // The mark sits tight above the title so the two read as one block\n // rather than as a stray label; the gap down to the card is the\n // largest on the screen — that step is what separates \"who this is\"\n // from \"what you do here\".\n <div\n className={[\n \"mb-8 space-y-3\",\n // Over the colour field the heading is on the panel, not on the\n // page, so it takes the panel's ink until the split puts it back\n // on a light ground.\n panel\n ? \"text-white [&_p]:text-white/75 md:text-foreground md:[&_p]:text-muted-foreground\"\n : \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n {logo && <div className=\"flex justify-center\">{logo}</div>}\n {title && (\n <AuthHeading\n title={title}\n subtitle={subtitle}\n titleClassName={titleClassName}\n />\n )}\n </div>\n )}\n\n <div\n className={[\n \"sm:rounded-xl sm:border sm:border-foreground/[0.08] sm:bg-card\",\n \"sm:shadow-[0_1px_2px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.10)]\",\n // On the colour band the card is a sheet rising from the bottom of\n // the screen: rounded at the top only, and stretched past the\n // viewport so no ground shows beneath it however short the form.\n panel\n ? \"-mx-5 -mb-12 min-h-[60vh] rounded-t-2xl bg-card shadow-[0_-8px_32px_-12px_rgba(0,0,0,0.18)] sm:mx-0 sm:mb-0 sm:min-h-0 sm:overflow-hidden sm:rounded-xl md:grid md:grid-cols-2\"\n : \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\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=\"relative hidden overflow-hidden bg-muted md:block [&>*]:absolute [&>*]:inset-0 [&>*]:h-full [&>*]:w-full [&>img]:object-cover\"\n >\n {panel}\n </div>\n )}\n\n <div className={panel ? \"px-5 pb-12 pt-7 sm:p-7\" : \"sm:p-7\"}>\n {children}\n </div>\n </div>\n\n {footer && (\n <div className=\"mt-8 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","export type OAuthErrorLabels = {\n accountNotLinked: string\n socialCancelled: string\n socialFailed: string\n}\n\n/**\n * Turns Better Auth's machine-readable OAuth failure code into something an\n * invitee can act on.\n *\n * `account_not_linked` is the one worth naming: createPlatformAuth disables\n * account linking on purpose, so \"Continue with Google\" on an address already\n * registered with a password is REFUSED. Better Auth's default error route\n * bounces the browser back with nothing shown, so without this the button\n * simply looks broken.\n */\nexport function oauthErrorMessage(\n code: string | null | undefined,\n labels: OAuthErrorLabels,\n): string {\n switch (code) {\n case \"account_not_linked\":\n return labels.accountNotLinked\n case \"access_denied\":\n return labels.socialCancelled\n default:\n return labels.socialFailed\n }\n}\n\n/**\n * Reads the failure Better Auth redirected back with.\n *\n * A social sign-in leaves the app entirely, so component state does not survive\n * it: the only carrier left when the browser returns is the `?error=` Better\n * Auth appends to the errorCallbackURL. Read from the address bar rather than\n * from a typed route search, because the auth routes deliberately declare no\n * search schema — adding one would make `search` required on every navigate to\n * them across the app.\n */\nexport function initialOAuthError(\n labels: OAuthErrorLabels,\n param = \"error\",\n): string | undefined {\n if (typeof window === \"undefined\") return undefined\n const code = new URLSearchParams(window.location.search).get(param)\n if (!code) return undefined\n return oauthErrorMessage(code, labels)\n}\n\n/**\n * Drops the failure from the address bar once it has been shown.\n *\n * Without this the message comes back on every reload, and outlives the retry\n * that succeeded. replaceState rather than a router navigate: these screens\n * have no typed search to navigate against, and the entry being rewritten is\n * the one the OAuth provider pushed, not one the person chose.\n */\nexport function clearOAuthError(param = \"error\"): void {\n if (typeof window === \"undefined\") return\n const url = new URL(window.location.href)\n if (!url.searchParams.has(param)) return\n url.searchParams.delete(param)\n window.history.replaceState({}, \"\", url.toString())\n}\n"],"mappings":";;;;;AAMO,SAAS,WAAW,YAAgC;AACzD,SAAO,WAAW,WAAW;AAC/B;AAMO,SAAS,UAAU,YAAgC;AACxD,QAAM,UAAU,YAAY;AAC1B,UAAM,WAAW,QAAQ;AAAA,EAC3B;AACA,SAAO;AACT;;;ACnBA,SAAS,OAAO,gBAA0D;AA+CpE,SAGE,KAHF;AAjBC,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,GAAG;AACL,GAAmB;AACjB,QAAM,KAAK,MAAM;AACjB,QAAM,gBAAgB,cAAc,GAAG,EAAE,iBAAiB;AAC1D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAE9C,QAAM,aAAa,MAAM,SAAS;AAClC,QAAM,OAAO,cAAc,WAAW,SAAS,MAAM;AAErD,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,6CAGb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MACC;AAAA,OACH;AAAA,IAEA,qBAAC,SAAI,WAAU,YACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ;AAAA,UACA;AAAA,UACA,gBAAc,WAAW;AAAA,UACzB,oBAAkB;AAAA,UAClB,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA,aAAa,UAAU;AAAA,YACvB;AAAA;AAAA;AAAA;AAAA,YAIA;AAAA,YACA;AAAA,YACA,UACI,wGAKA,GAAG,cAAc;AAAA,YACrB;AAAA,UACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA;AAAA,MACb;AAAA,MAEC,cACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,UACpC,UAAU,MAAM;AAAA,UAChB,gBAAc;AAAA,UACd,cACE,WAAW,4BAA4B;AAAA,UAEzC,WAAU;AAAA,UAEV,8BAAC,WAAQ,MAAM,UAAU;AAAA;AAAA,MAC3B;AAAA,OAEJ;AAAA,IAEC,eACC,oBAAC,OAAE,IAAI,eAAe,WAAU,iCAC7B,uBACH;AAAA,KAEJ;AAEJ;AAIA,SAAS,QAAQ,EAAE,KAAK,GAAsB;AAC5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,QAAO;AAAA,MACP,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAY;AAAA,MAEZ;AAAA,4BAAC,UAAK,GAAE,kGAAiG;AAAA,QACzG,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,QAC7B,CAAC,QAAQ,oBAAC,UAAK,GAAE,cAAa;AAAA;AAAA;AAAA,EACjC;AAEJ;;;ACjGI,SAoBI,OAAAA,MApBJ,QAAAC,aAAA;AATG,SAAS,WAAW;AAAA,EACzB,UAAU;AAAA,EACV,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAChB,GAAoB;AAClB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU,YAAY;AAAA,MACtB,aAAW,WAAW;AAAA,MACtB,WAAW;AAAA,QACT,cAAc,UAAU;AAAA,QACxB;AAAA;AAAA;AAAA;AAAA,QAIA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,MAET;AAAA,mBACC,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA;AAAA,QACZ;AAAA,QAED,UAAU,eAAe;AAAA;AAAA;AAAA,EAC5B;AAEJ;;;ACjEA,SAAS,YAAAE,iBAAgC;;;ACUlC,SAAS,mBACd,OACS;AACT,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,SAAS,wBAAwB,MAAM,WAAW;AACjE;;;ACEI,gBAAAC,YAAA;AAFG,SAAS,UAAU,EAAE,UAAU,OAAO,QAAQ,GAAmB;AACtE,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,SAAS,UAAU,UAAU;AAAA,MACnC,aAAW,SAAS,UAAU,cAAc;AAAA,MAC5C,WACE,WACI;AAAA,QACE;AAAA,QACA;AAAA,QACA,SAAS,UACL,6DACA;AAAA,MACN,EAAE,KAAK,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,QAKV;AAAA;AAAA,MAGL;AAAA;AAAA,EACH;AAEJ;;;ACRM,SACE,OAAAC,MADF,QAAAC,aAAA;AA/BN,IAAM,SAAiC;AAAA,EACrC,QAAQ;AAAA,EACR,QAAQ;AACV;AAYO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,YAAY;AAAA,EACZ;AACF,GAAuB;AACrB,MAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,QAAM,OAAO,EAAE,GAAG,QAAQ,GAAG,OAAO;AAEpC,SACE,gBAAAA,MAAC,SAAI,WAAU,aAIb;AAAA,oBAAAA,MAAC,SAAI,eAAY,QAAO,WAAU,2BAChC;AAAA,sBAAAD,KAAC,UAAK,WAAU,yBAAwB;AAAA,MACxC,gBAAAA,KAAC,UAAK,WAAU,iEACb,qBACH;AAAA,MACA,gBAAAA,KAAC,UAAK,WAAU,yBAAwB;AAAA,OAC1C;AAAA,IAMA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WACE,UAAU,SAAS,IACf,gCACA;AAAA,QAGL,oBAAU,IAAI,CAAC,aACd,gBAAAC;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,SAAS,MAAM,SAAS,QAAQ;AAAA,YAChC;AAAA,YACA,cAAY,KAAK,QAAQ,KAAK;AAAA,YAC9B,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF,EAAE,KAAK,GAAG;AAAA,YAEV;AAAA,8BAAAD,KAAC,gBAAa,UAAoB;AAAA,cAClC,gBAAAA,KAAC,UAAK,WAAW,UAAU,SAAS,IAAI,cAAc,IACnD,eAAK,QAAQ,KAAK,UACrB;AAAA;AAAA;AAAA,UAjBK;AAAA,QAkBP,CACD;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAKA,SAAS,aAAa,EAAE,SAAS,GAAsC;AACrE,MAAI,aAAa,UAAU;AACzB,WACE,gBAAAC,MAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,eAAY,QAC1D;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,GAAE;AAAA;AAAA,MACJ;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,GAAE;AAAA;AAAA,MACJ;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,GAAE;AAAA;AAAA,MACJ;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,GAAE;AAAA;AAAA,MACJ;AAAA,OACF;AAAA,EAEJ;AACA,SACE,gBAAAA,KAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,gBAAe,eAAY,QAC9E,0BAAAA,KAAC,UAAK,GAAE,0dAAyd,GACne;AAEJ;;;ACxFM,gBAAAE,YAAA;AAHC,SAAS,SAAS,EAAE,IAAI,IAAI,MAAM,WAAW,SAAS,GAAkB;AAC7E,MAAI,MAAM;AACR,WACE,gBAAAA,KAAC,QAAK,IAAQ,WACX,UACH;AAAA,EAEJ;AACA,SACE,gBAAAA,KAAC,OAAE,MAAM,IAAI,WACV,UACH;AAEJ;AAEO,IAAM,kBACX;AAEK,IAAM,uBACX;;;AC1BF,IAAM,mBAAmB;AAElB,SAAS,qBAAqB,OAAoC;AACvE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,WAAW,QAAQ,SAAS,iBAAkB,QAAO;AAC1D,SAAO;AACT;AASO,SAAS,gBAAgB,MAAc,OAAwB;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,YAAY,KAAK,SAAS,GAAG,IAAI,MAAM;AAC7C,SAAO,GAAG,IAAI,GAAG,SAAS,UAAU,mBAAmB,KAAK,CAAC;AAC/D;;;ALgGM,gBAAAC,MAEA,QAAAC,aAFA;AApHN,IAAM,WAAsC;AAAA,EAC1C,OAAO;AAAA,EACP,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,kBACE;AAAA;AAAA;AAAA;AAAA,EAIF,kBACE;AAAA,EACF,iBAAiB;AAAA,EACjB,cAAc;AAChB;AAEO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,kBAAkB,CAAC;AAAA,EACnB,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,IAAI,EAAE,GAAG,UAAU,GAAG,OAAO;AACnC,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,EAAE;AAC3C,QAAM,CAAC,UAAU,WAAW,IAAIA,UAA6B;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAGhD,QAAM,QAAQ,YAAY;AAC1B,QAAM,WAAW;AAEjB,QAAM,eAAe,OAAO,MAAiB;AAC3C,MAAE,eAAe;AACjB,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,eAAS,EAAE,aAAa;AACxB;AAAA,IACF;AACA,QAAI,CAAC,UAAU;AACb,eAAS,EAAE,gBAAgB;AAC3B;AAAA,IACF;AACA,aAAS,MAAS;AAClB,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,MAAM,MAAM,WAAW,OAAO,MAAM;AAAA,QACxC,OAAO,MAAM,KAAK;AAAA,QAClB;AAAA,MACF,CAAC;AACD,UAAI,KAAK,OAAO;AACd,YAAI,mBAAmB,IAAI,KAAK,KAAK,oBAAoB;AACvD,6BAAmB,MAAM,KAAK,CAAC;AAC/B;AAAA,QACF;AACA;AAAA,UACE,mBAAmB,IAAI,KAAK,IACxB,EAAE,mBACD,IAAI,MAAM,WAAW,EAAE;AAAA,QAC9B;AACA;AAAA,MACF;AAIA,UAAI,cAAc;AAChB,cAAM,MAAM,cAAc,EAAE,aAAa,UAAU,CAAC;AAAA,MACtD;AACA,kBAAY;AAAA,IACd,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,kBAAkB;AAAA,IACpE,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,eAAe,OAAO,aAAkC;AAC5D,aAAS,MAAS;AAClB,QAAI;AACF,YAAM,WAAW,OAAO,OAAO;AAAA,QAC7B;AAAA;AAAA;AAAA,QAGA,aAAa,gBAAgB,mBAAmB,MAAM;AAAA,MACxD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,OAAO,eAAe,QAAQ,IAAI,UAAU;AAClD;AAAA,QACE,SAAS,uBACL,EAAE,mBACF,SAAS,kBACP,EAAE,kBACF,EAAE;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,SACE,gBAAAD,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAD,KAAC,aAAW,iBAAM;AAAA,IAElB,gBAAAC,MAAC,UAAK,UAAU,cAAc,WAAU,sBAAqB,YAAU,MACrE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,gBAAe;AAAA,UACf,YAAY;AAAA,UACZ,SAAS,CAAC,CAAC;AAAA,UACX;AAAA;AAAA,MACF;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,UAC3C,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,SAAS,CAAC,CAAC;AAAA,UACX;AAAA,UACA,MACE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAI;AAAA,cACJ,IAAI;AAAA,cACJ,WAAW;AAAA,cAEV,YAAE;AAAA;AAAA,UACL;AAAA;AAAA,MAEJ;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAW;AAAA,UACX,SAAS;AAAA,UACT,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC;AAAA,UAC5B,cAAc,EAAE;AAAA,UAChB,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,UAAU;AAAA,QACV,UAAU;AAAA;AAAA,IACZ;AAAA,IAEA,gBAAAC,MAAC,OAAE,WAAU,6CACV;AAAA,QAAE;AAAA,MAAW;AAAA,MACd,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAI,gBAAgB,aAAa,MAAM;AAAA,UACvC,IAAI;AAAA,UACJ,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,KACF;AAEJ;AAKA,UAAU,WAAW;AAAA,EACnB,OAAO,SAAS;AAAA,EAChB,UAAU,SAAS;AACrB;;;AM1MA,SAAS,YAAAG,iBAAgC;AAsHnC,gBAAAC,MAEA,QAAAC,aAFA;AA7GN,IAAM,sBAAsB;AAE5B,IAAMC,YAAyC;AAAA,EAC7C,OAAO;AAAA,EACP,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,cAAc,YAAY,mBAAmB;AAAA,EAC7C,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,aAAa;AAAA,EACb,OAAO;AAAA,EACP,eAAe;AAAA,EACf,kBAAkB,uCAAuC,mBAAmB;AAAA,EAC5E,cAAc;AAChB;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,oBAAoB;AAAA,EACpB,kBAAkB,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,IAAI,EAAE,GAAGA,WAAU,GAAG,OAAO;AACnC,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAS,EAAE;AACnC,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,EAAE;AAC/C,QAAM,QAAQ,eAAe;AAC7B,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,EAAE;AAC3C,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,EAAE;AACzD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAA6B;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAGhD,QAAM,QAAQ,YAAY;AAC1B,QAAM,WAAW;AAEjB,QAAM,WAAW,SAAS,SAAS,KAAK,SAAS,SAAS;AAC1D,QAAM,WAAW,gBAAgB,SAAS,KAAK,oBAAoB;AAEnE,QAAM,eAAe,OAAO,MAAiB;AAC3C,MAAE,eAAe;AACjB,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,eAAS,EAAE,aAAa;AACxB;AAAA,IACF;AACA,QAAI,SAAS,SAAS,qBAAqB;AACzC,eAAS,EAAE,gBAAgB;AAC3B;AAAA,IACF;AACA,QAAI,aAAa,iBAAiB;AAChC,eAAS,EAAE,gBAAgB;AAC3B;AAAA,IACF;AACA,aAAS,MAAS;AAClB,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,MAAM,MAAM,WAAW,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA,QAIxC,GAAI,KAAK,KAAK,IAAI,EAAE,MAAM,KAAK,KAAK,EAAE,IAAI,CAAC;AAAA,QAC3C,OAAO,MAAM,KAAK;AAAA,QAClB;AAAA,MACF,CAAC;AACD,UAAI,KAAK,OAAO;AACd,iBAAS,IAAI,MAAM,WAAW,EAAE,YAAY;AAC5C;AAAA,MACF;AAIA,kBAAY,MAAM,KAAK,CAAC;AAAA,IAC1B,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,YAAY;AAAA,IAC9D,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,eAAe,OAAO,aAAkC;AAC5D,aAAS,MAAS;AAClB,QAAI;AACF,YAAM,WAAW,OAAO,OAAO;AAAA,QAC7B;AAAA;AAAA;AAAA,QAGA,aAAa,gBAAgB,mBAAmB,MAAM;AAAA,MACxD,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,YAAY;AAAA,IAC9D;AAAA,EACF;AAEA,SACE,gBAAAF,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAD,KAAC,aAAW,iBAAM;AAAA,IAElB,gBAAAC,MAAC,UAAK,UAAU,cAAc,WAAU,sBAAqB,YAAU,MACrE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,UACvC,UAAU;AAAA,UACV,cAAa;AAAA,UACb;AAAA,UACA,MACE,gBAAAA,KAAC,UAAK,WAAU,iCACb,YAAE,UACL;AAAA;AAAA,MAEJ;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK;AAAA,UAC7C,UAAQ;AAAA,UAIR,UAAU,CAAC,CAAC;AAAA,UACZ,UAAU;AAAA,UACV,cAAa;AAAA,UACb,gBAAe;AAAA,UACf,YAAY;AAAA,UACZ,aAAa,cAAc,EAAE,cAAc;AAAA,UAC3C;AAAA;AAAA,MACF;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,UAC3C,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,aAAa,EAAE;AAAA,UAGf,SAAS;AAAA,UACT;AAAA;AAAA,MACF;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,mBAAmB,EAAE,OAAO,KAAK;AAAA,UAClD,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,SAAS;AAAA,UACT;AAAA;AAAA,MACF;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAW;AAAA,UACX,SAAS;AAAA,UACT,cAAc,EAAE;AAAA,UAChB,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,MAEC,SACC,gBAAAA,KAAC,OAAE,WAAU,6DACV,iBACH;AAAA,OAEJ;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,UAAU;AAAA,QACV,UAAU;AAAA;AAAA,IACZ;AAAA,IAEA,gBAAAC,MAAC,OAAE,WAAU,6CACV;AAAA,QAAE;AAAA,MAAa;AAAA,MAChB,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAI,gBAAgB,UAAU,MAAM;AAAA,UACpC,IAAI;AAAA,UACJ,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,KACF;AAEJ;AAGA,aAAa,WAAW;AAAA,EACtB,OAAOE,UAAS;AAAA,EAChB,UAAUA,UAAS;AACrB;;;AC9NA,SAAS,YAAAE,iBAAgC;;;AC+BrC,SACE,OAAAC,MADF,QAAAC,aAAA;AApBG,IAAM,aAAa;AAUnB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,UAAU;AAAA,EACV;AAAA,EACA,iBAAiB;AACnB,GAAsB;AACpB,SACE,gBAAAA,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QAET;AAAA;AAAA,IACH;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAQ;AAAA,QACR,WAAW;AAAA,QACX;AAAA,QACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,MAAM,QAAQ,OAAO,EAAE,CAAC;AAAA,QAC3D,UAAQ;AAAA,QACR;AAAA,QACA,cAAa;AAAA,QACb,gBAAc,WAAW;AAAA,QACzB,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,UACI,wGACA,GAAG,cAAc;AAAA,UACrB;AAAA,QACF,EAAE,KAAK,GAAG;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;;;ADwBM,gBAAAE,MAGA,QAAAC,aAHA;AAjFN,IAAMC,YAA4C;AAAA,EAChD,OAAO;AAAA,EACP,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAChB;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,IAAI,EAAE,GAAGA,WAAU,GAAG,OAAO;AACnC,QAAM,CAAC,KAAK,MAAM,IAAIC,UAAS,EAAE;AACjC,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAA6B;AACvE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAA6B;AAEvD,QAAM,eAAe,OAAO,MAAiB;AAC3C,MAAE,eAAe;AACjB,QAAI,IAAI,SAAS,YAAY;AAC3B,eAAS,EAAE,YAAY;AACvB;AAAA,IACF;AACA,aAAS,MAAS;AAClB,qBAAiB,MAAS;AAC1B,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,MAAM,MAAM,WAAW,SAAS,YAAY,EAAE,OAAO,IAAI,CAAC;AAChE,UAAI,KAAK,OAAO;AACd,iBAAS,IAAI,MAAM,WAAW,EAAE,WAAW;AAC3C;AAAA,MACF;AACA,kBAAY;AAAA,IACd,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,WAAW;AAAA,IAC7D,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,eAAe,YAAY;AAC/B,QAAI,CAAC,OAAO;AACV,eAAS,EAAE,YAAY;AACvB;AAAA,IACF;AACA,aAAS,MAAS;AAClB,qBAAiB,MAAS;AAC1B,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,WAAW,SAAS,oBAAoB;AAAA,QAC5C;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AACD,uBAAiB,EAAE,MAAM;AAAA,IAC3B,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,YAAY;AAAA,IAC9D,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,SACE,gBAAAF,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAD,KAAC,aAAW,iBAAM;AAAA,IAClB,gBAAAA,KAAC,aAAU,MAAK,WAAW,yBAAc;AAAA,IAEzC,gBAAAC,MAAC,UAAK,UAAU,cAAc,WAAU,sBAAqB,YAAU,MACrE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAG;AAAA,UACH,OAAO,EAAE;AAAA,UACT,OAAO;AAAA,UACP,UAAU;AAAA,UACV,UAAU;AAAA,UACV;AAAA;AAAA,MACF;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAW;AAAA,UACX,SAAS;AAAA,UACT,UAAU,IAAI,SAAS;AAAA,UACvB,cAAc,EAAE;AAAA,UAChB,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,IAEA,gBAAAC,MAAC,SAAI,WAAU,uDACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,UACV,WAAU;AAAA,UAET,wBAAc,EAAE,gBAAgB,EAAE;AAAA;AAAA,MACrC;AAAA,MAEA,gBAAAC,MAAC,OACE;AAAA,UAAE;AAAA,QAAiB;AAAA,QACpB,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,WAAW;AAAA,YAEV,YAAE;AAAA;AAAA,QACL;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;AAGA,gBAAgB,WAAW;AAAA,EACzB,OAAOE,UAAS;AAAA,EAChB,UAAUA,UAAS;AACrB;;;AE7IA,SAAS,YAAAE,iBAAgC;AAgEnC,gBAAAC,OAEA,QAAAC,aAFA;AAtDN,IAAMC,YAA+C;AAAA,EACnD,OAAO;AAAA,EACP,UACE;AAAA,EACF,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,eAAe;AAAA,EACf,YAAY;AACd;AAEO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,IAAI,EAAE,GAAGA,WAAU,GAAG,OAAO;AACnC,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,EAAE;AACrC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAA6B;AACvD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAEhD,QAAM,eAAe,OAAO,MAAiB;AAC3C,MAAE,eAAe;AACjB,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,eAAS,EAAE,aAAa;AACxB;AAAA,IACF;AACA,aAAS,MAAS;AAClB,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,MAAM,MAAM,WAAW,SAAS,oBAAoB;AAAA,QACxD,OAAO,MAAM,KAAK;AAAA,QAClB,MAAM;AAAA,MACR,CAAC;AACD,UAAI,KAAK,OAAO;AACd,iBAAS,IAAI,MAAM,WAAW,EAAE,UAAU;AAC1C;AAAA,MACF;AACA,kBAAY,MAAM,KAAK,CAAC;AAAA,IAC1B,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,UAAU;AAAA,IAC5D,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE,gBAAAF,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAD,MAAC,aAAW,iBAAM;AAAA,IAElB,gBAAAC,MAAC,UAAK,UAAU,cAAc,WAAU,sBAAqB,YAAU,MACrE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,gBAAe;AAAA,UACf,YAAY;AAAA,UACZ,SAAS,CAAC,CAAC;AAAA;AAAA,MACb;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAW;AAAA,UACX,SAAS;AAAA,UACT,UAAU,CAAC,MAAM,KAAK;AAAA,UACtB,cAAc,EAAE;AAAA,UAChB,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,IAEA,gBAAAC,MAAC,OAAE,WAAU,6CACV;AAAA,QAAE;AAAA,MAAkB;AAAA,MACrB,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,KACF;AAEJ;AAGA,mBAAmB,WAAW;AAAA,EAC5B,OAAOE,UAAS;AAAA,EAChB,UAAUA,UAAS;AACrB;;;AC9GA,SAAS,YAAAE,iBAAgC;AA4GnC,gBAAAC,OAGA,QAAAC,aAHA;AApGN,IAAMC,uBAAsB;AAE5B,IAAMC,YAA8C;AAAA,EAClD,OAAO;AAAA,EACP,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,cAAc,YAAYD,oBAAmB;AAAA,EAC7C,oBAAoB;AAAA,EACpB,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,cAAc;AAAA,EACd,kBAAkB,uCAAuCA,oBAAmB;AAAA,EAC5E,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,cAAc;AAChB;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,IAAI,EAAE,GAAGC,WAAU,GAAG,OAAO;AACnC,QAAM,CAAC,KAAK,MAAM,IAAIC,UAAS,EAAE;AACjC,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,EAAE;AAC3C,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,EAAE;AACzD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAA6B;AACvD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAA6B;AAEvE,QAAM,WAAW,SAAS,SAAS,KAAK,SAAS,SAASF;AAC1D,QAAM,WAAW,gBAAgB,SAAS,KAAK,oBAAoB;AAEnE,QAAM,eAAe,OAAO,MAAiB;AAC3C,MAAE,eAAe;AACjB,QAAI,IAAI,SAAS,YAAY;AAC3B,eAAS,EAAE,YAAY;AACvB;AAAA,IACF;AACA,QAAI,SAAS,SAASA,sBAAqB;AACzC,eAAS,EAAE,gBAAgB;AAC3B;AAAA,IACF;AACA,QAAI,aAAa,iBAAiB;AAChC,eAAS,EAAE,gBAAgB;AAC3B;AAAA,IACF;AACA,aAAS,MAAS;AAClB,qBAAiB,MAAS;AAC1B,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,sCAAsC;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,SAAS,CAAC;AAAA,MAC/C,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAC9C,iBAAS,MAAM,WAAW,EAAE,WAAW;AACvC;AAAA,MACF;AACA,kBAAY;AAAA,IACd,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,WAAW;AAAA,IAC7D,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,eAAe,YAAY;AAC/B,aAAS,MAAS;AAClB,qBAAiB,MAAS;AAC1B,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,WAAW,SAAS,oBAAoB;AAAA,QAC5C;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AACD,uBAAiB,EAAE,MAAM;AAAA,IAC3B,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,EAAE,YAAY;AAAA,IAC9D,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,SACE,gBAAAD,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAD,MAAC,aAAW,iBAAM;AAAA,IAClB,gBAAAA,MAAC,aAAU,MAAK,WAAW,yBAAc;AAAA,IAEzC,gBAAAC,MAAC,UAAK,UAAU,cAAc,WAAU,sBAAqB,YAAU,MACrE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAG;AAAA,UACH,OAAO,EAAE;AAAA,UACT,OAAO;AAAA,UACP,UAAU;AAAA,UACV,UAAU;AAAA,UACV;AAAA;AAAA,MACF;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,UAC3C,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,aAAa,EAAE;AAAA,UACf,SAAS;AAAA;AAAA,MACX;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,mBAAmB,EAAE,OAAO,KAAK;AAAA,UAClD,UAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAa;AAAA,UACb,SAAS;AAAA;AAAA,MACX;AAAA,MAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAW;AAAA,UACX,SAAS;AAAA,UACT,UAAU,IAAI,SAAS,cAAc,CAAC,YAAY,CAAC;AAAA,UACnD,cAAc,EAAE;AAAA,UAChB,WAAW;AAAA,UAEV,YAAE;AAAA;AAAA,MACL;AAAA,OACF;AAAA,IAEA,gBAAAC,MAAC,SAAI,WAAU,uDACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,UAAU;AAAA,UACV,WAAU;AAAA,UAET,wBAAc,EAAE,gBAAgB,EAAE;AAAA;AAAA,MACrC;AAAA,MAEA,gBAAAC,MAAC,OACE;AAAA,UAAE;AAAA,QAAkB;AAAA,QACrB,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,WAAW;AAAA,YAEV,YAAE;AAAA;AAAA,QACL;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;AAGA,kBAAkB,WAAW;AAAA,EAC3B,OAAOG,UAAS;AAAA,EAChB,UAAUA,UAAS;AACrB;;;AC/JI,SACE,OAAAE,OADF,QAAAC,cAAA;AANG,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,iBAAiB;AACnB,GAAqB;AACnB,SACE,gBAAAA,OAAC,YAAO,WAAU,2BAChB;AAAA,oBAAAD,MAAC,QAAG,WAAW,gBAAiB,iBAAM;AAAA,IACrC,YACC,gBAAAA,MAAC,OAAE,WAAU,uEACV,oBACH;AAAA,KAEJ;AAEJ;;;ACkBQ,gBAAAE,OAcE,QAAAC,cAdF;AAzBD,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA;AAAA;AAAA,QAGA,QACI,kCACA;AAAA,MACN,EAAE,KAAK,GAAG;AAAA,MAET;AAAA;AAAA;AAAA;AAAA;AAAA,QAKC,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,QAGF,gBAAAC,OAAC,SAAI,WAAW,kBAAkB,QAAQ,cAAc,eAAe,IACnE;AAAA,mBAAQ;AAAA;AAAA;AAAA;AAAA,UAKR,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA;AAAA;AAAA;AAAA,gBAIA,QACI,qFACA;AAAA,cACN,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,cAEV;AAAA,wBAAQ,gBAAAD,MAAC,SAAI,WAAU,uBAAuB,gBAAK;AAAA,gBACnD,SACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA;AAAA,gBACF;AAAA;AAAA;AAAA,UAEJ;AAAA,UAGF,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA;AAAA;AAAA;AAAA,gBAIA,QACI,mLACA;AAAA,cACN,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,cAEV;AAAA;AAAA;AAAA,gBAGC,gBAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,eAAY;AAAA,oBACZ,WAAU;AAAA,oBAET;AAAA;AAAA,gBACH;AAAA,gBAGF,gBAAAA,MAAC,SAAI,WAAW,QAAQ,2BAA2B,UAChD,UACH;AAAA;AAAA;AAAA,UACF;AAAA,UAEC,UACC,gBAAAA,MAAC,SAAI,WAAU,kEACZ,kBACH;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACnFM,SACE,OAAAE,OADF,QAAAC,cAAA;AApCN,SAAS,sBAA0C;AACjD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,CAAC,QAAQ,SAAS,eAAe,WAAW,KAAK,IAAI,EAAG,QAAO;AACnE,QAAM,OAAO,KAAK,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG;AAC/C,SAAO,WAAW,IAAI;AACxB;AAEA,IAAM,iBAAyC;AAAA,EAC7C,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACX;AAcO,SAAS,iBAAiB;AAAA,EAC/B,SAAS;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,EACR;AACF,GAA0B;AACxB,QAAM,UAAU,gBAAgB,oBAAoB;AAEpD,SACE,gBAAAA,OAAC,SAAI,WAAU,aACb;AAAA,oBAAAA,OAAC,SACC;AAAA,sBAAAD,MAAC,QAAG,WAAU,qCAAqC,iBAAM;AAAA,MACzD,gBAAAA,MAAC,OAAE,WAAU,sCACV,yBAAe,MAAM,KAAK,eAAe,SAC5C;AAAA,OACF;AAAA,IAEC,UACC,gBAAAC,OAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,MAC5B;AAAA,MACf,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,UAAU,OAAO;AAAA,UACvB,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAAK;AAAA,MAAI;AAAA,OAEX,IACE;AAAA,IAEH;AAAA,KACH;AAEJ;;;ACpDO,SAAS,kBACd,MACA,QACQ;AACR,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB;AACE,aAAO,OAAO;AAAA,EAClB;AACF;AAYO,SAAS,kBACd,QACA,QAAQ,SACY;AACpB,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,OAAO,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI,KAAK;AAClE,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,kBAAkB,MAAM,MAAM;AACvC;AAUO,SAAS,gBAAgB,QAAQ,SAAe;AACrD,MAAI,OAAO,WAAW,YAAa;AACnC,QAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,MAAI,CAAC,IAAI,aAAa,IAAI,KAAK,EAAG;AAClC,MAAI,aAAa,OAAO,KAAK;AAC7B,SAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;AACpD;","names":["jsx","jsxs","useState","jsx","jsx","jsxs","jsx","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","jsx","jsxs"]}
@@ -1,4 +1,4 @@
1
- import { h as InvitationFailure } from './types-DzG-qaSv.js';
1
+ import { h as InvitationFailure } from './types-BFmX_pUl.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 { j as PlatformAuthConfig } from './types-DzG-qaSv.js';
3
- export { n as PlatformSession, o as PlatformSessionData, p as PlatformUser } from './types-DzG-qaSv.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-0CfjrVjI.js';
2
+ import { j as PlatformAuthConfig } from './types-BFmX_pUl.js';
3
+ export { n as PlatformSession, o as PlatformSessionData, p as PlatformUser } from './types-BFmX_pUl.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-DwKUO5oh.js';
5
5
 
6
6
  /**
7
7
  * Creates a Better Auth instance with platform defaults.
@@ -184,7 +184,9 @@ interface RegisterFormLabels {
184
184
  title?: string;
185
185
  subtitle?: string;
186
186
  namePlaceholder?: string;
187
+ optional?: string;
187
188
  emailPlaceholder?: string;
189
+ emailLocked?: string;
188
190
  passwordPlaceholder?: string;
189
191
  passwordHint?: string;
190
192
  confirmPlaceholder?: string;
@@ -193,7 +195,6 @@ interface RegisterFormLabels {
193
195
  submitPending?: string;
194
196
  haveAccount?: string;
195
197
  login?: string;
196
- nameRequired?: string;
197
198
  emailRequired?: string;
198
199
  passwordTooShort?: string;
199
200
  signUpFailed?: string;
@@ -247,7 +248,8 @@ interface AuthClientSurface {
247
248
  };
248
249
  signUp: {
249
250
  email(input: {
250
- name: string;
251
+ /** Omitted when the sign-up form's optional name field is left blank */
252
+ name?: string;
251
253
  email: string;
252
254
  password: string;
253
255
  }): Promise<AuthClientResult>;
@@ -340,6 +342,12 @@ interface LoginFormProps extends AuthThemeProps, AuthNavProps, AuthInviteProps {
340
342
  authClient: AuthClientSurface;
341
343
  }
342
344
  interface RegisterFormProps extends AuthThemeProps, AuthNavProps, AuthInviteProps {
345
+ /**
346
+ * Address the invitation was issued to. Given, it fills the email field and
347
+ * fixes it: an invitation grants its tier to one address, so signing up with
348
+ * another would drop the grant with nothing said about it.
349
+ */
350
+ lockedEmail?: string;
343
351
  /** Callback on successful sign-up, receives the email to verify */
344
352
  onSuccess?: (email: string) => void;
345
353
  /** Error raised outside the form, rendered in the same banner */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lalternative/auth",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
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",
@@ -27,7 +27,8 @@
27
27
  "dev": "tsup --watch",
28
28
  "typecheck": "tsc --noEmit",
29
29
  "prepublishOnly": "pnpm build",
30
- "test": "node --experimental-strip-types --test \"src/**/*.test.ts\""
30
+ "test": "node --experimental-strip-types --test \"src/**/*.test.ts\"",
31
+ "playground": "vite --config playground/vite.config.ts"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "better-auth": ">=1.4.0",
@@ -35,13 +36,17 @@
35
36
  "react-dom": ">=18.0.0"
36
37
  },
37
38
  "devDependencies": {
39
+ "@tailwindcss/vite": "^4.1.0",
38
40
  "@types/react": "^19.0.0",
39
41
  "@types/react-dom": "^19.0.0",
42
+ "@vitejs/plugin-react": "^5.0.0",
40
43
  "better-auth": "^1.6.11",
41
44
  "react": "^19.0.0",
42
45
  "react-dom": "^19.0.0",
46
+ "tailwindcss": "^4.1.0",
43
47
  "tsup": "^8.5.1",
44
48
  "typescript": "^5.7.0",
49
+ "vite": "^7.1.7",
45
50
  "zod": "^4.4.3"
46
51
  },
47
52
  "publishConfig": {