@lalternative/auth 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ function AuthField({
21
21
  hint,
22
22
  description,
23
23
  invalid = false,
24
- fieldClassName = "border-foreground/25 bg-background",
24
+ fieldClassName = "border-foreground/30 bg-background",
25
25
  ...props
26
26
  }) {
27
27
  const id = useId();
@@ -35,7 +35,7 @@ function AuthField({
35
35
  "label",
36
36
  {
37
37
  htmlFor: id,
38
- className: "text-sm font-medium leading-none text-foreground",
38
+ className: "text-[0.6875rem] font-semibold uppercase leading-none tracking-[0.09em] text-foreground/70",
39
39
  children: label
40
40
  }
41
41
  ),
@@ -51,13 +51,16 @@ function AuthField({
51
51
  "aria-invalid": invalid || void 0,
52
52
  "aria-describedby": descriptionId,
53
53
  className: [
54
- "h-[52px] w-full rounded-lg border px-4 text-base",
55
- "sm:h-11 sm:text-sm",
54
+ "h-[52px] w-full rounded-md border px-3.5 text-base",
55
+ "sm:h-[46px] sm:text-[0.9375rem]",
56
56
  isPassword ? "pr-12" : "",
57
- "text-foreground placeholder:text-muted-foreground/70",
58
- "transition-[border-color,box-shadow] duration-150 ease-out",
59
- "focus-visible:outline-none focus-visible:ring-[3px]",
60
- invalid ? "border-destructive bg-background focus-visible:border-destructive focus-visible:ring-destructive/20" : `${fieldClassName} focus-visible:border-ring focus-visible:ring-ring/15`,
57
+ "text-foreground placeholder:text-muted-foreground/60",
58
+ // Focus reads as the border committing rather than as a halo
59
+ // appearing beside it: the ring is tight and the border darkens to
60
+ // full ink in the same 180ms, so the field answers the caret.
61
+ "transition-[border-color,box-shadow] duration-200 ease-out",
62
+ "focus-visible:outline-none focus-visible:ring-2",
63
+ invalid ? "border-destructive bg-background focus-visible:border-destructive focus-visible:ring-destructive/15" : `${fieldClassName} focus-visible:border-foreground focus-visible:ring-foreground/10`,
61
64
  "disabled:cursor-not-allowed disabled:opacity-60"
62
65
  ].filter(Boolean).join(" ")
63
66
  }
@@ -107,7 +110,8 @@ function AuthSubmit({
107
110
  disabled = false,
108
111
  pendingLabel,
109
112
  children,
110
- className = "bg-primary text-primary-foreground hover:bg-primary/90"
113
+ className = "bg-primary text-primary-foreground hover:bg-primary/90",
114
+ spacedAbove = false
111
115
  }) {
112
116
  return /* @__PURE__ */ jsxs2(
113
117
  "button",
@@ -116,11 +120,16 @@ function AuthSubmit({
116
120
  disabled: disabled || pending,
117
121
  "aria-busy": pending || void 0,
118
122
  className: [
119
- "relative flex h-[52px] w-full items-center justify-center rounded-lg sm:h-11",
120
- "text-base font-medium sm:text-sm",
123
+ spacedAbove ? "!mt-7" : "",
124
+ "relative flex h-[52px] w-full items-center justify-center rounded-md sm:h-[46px]",
125
+ // Slightly tracked at this weight: a wide solid button set in plain
126
+ // medium reads as a slab, and the letterspacing is what makes it read
127
+ // as typeset rather than filled in.
128
+ "text-[0.9375rem] font-medium tracking-[0.01em]",
121
129
  className,
122
- "shadow-sm transition-[background-color,transform,box-shadow] duration-150 ease-out",
123
- "active:translate-y-px",
130
+ "transition-[background-color,transform,box-shadow] duration-200 ease-out",
131
+ "shadow-[0_1px_2px_rgba(0,0,0,0.08)] hover:shadow-[0_2px_8px_rgba(0,0,0,0.12)]",
132
+ "active:translate-y-px active:shadow-[0_1px_2px_rgba(0,0,0,0.08)]",
124
133
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
125
134
  "disabled:pointer-events-none disabled:opacity-55"
126
135
  ].join(" "),
@@ -141,6 +150,12 @@ function AuthSubmit({
141
150
  // src/components/login-form.tsx
142
151
  import { useState as useState2 } from "react";
143
152
 
153
+ // src/email-not-verified.ts
154
+ function isEmailNotVerified(error) {
155
+ if (!error) return false;
156
+ return error.code === "EMAIL_NOT_VERIFIED" || error.status === 403;
157
+ }
158
+
144
159
  // src/components/auth-alert.tsx
145
160
  import { jsx as jsx3 } from "react/jsx-runtime";
146
161
  function AuthAlert({ children, tone = "error" }) {
@@ -245,8 +260,33 @@ function ProviderMark({ provider }) {
245
260
  return /* @__PURE__ */ jsx4("svg", { width: "17", height: "17", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("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" }) });
246
261
  }
247
262
 
263
+ // src/components/auth-link.tsx
264
+ import { jsx as jsx5 } from "react/jsx-runtime";
265
+ function AuthLink({ to, as: Link, className, children }) {
266
+ if (Link) {
267
+ return /* @__PURE__ */ jsx5(Link, { to, className, children });
268
+ }
269
+ return /* @__PURE__ */ jsx5("a", { href: to, className, children });
270
+ }
271
+ var AUTH_LINK_CLASS = "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";
272
+ var AUTH_HINT_LINK_CLASS = "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";
273
+
274
+ // src/invite-token.ts
275
+ var MAX_TOKEN_LENGTH = 128;
276
+ function normalizeInviteToken(value) {
277
+ if (typeof value !== "string") return void 0;
278
+ const trimmed = value.trim();
279
+ if (!trimmed || trimmed.length > MAX_TOKEN_LENGTH) return void 0;
280
+ return trimmed;
281
+ }
282
+ function withInviteToken(href, token) {
283
+ if (!token) return href;
284
+ const separator = href.includes("?") ? "&" : "?";
285
+ return `${href}${separator}invite=${encodeURIComponent(token)}`;
286
+ }
287
+
248
288
  // src/components/login-form.tsx
249
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
289
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
250
290
  var DEFAULTS = {
251
291
  title: "Connexion",
252
292
  subtitle: "Content de te revoir. Entre tes identifiants pour continuer.",
@@ -260,6 +300,7 @@ var DEFAULTS = {
260
300
  emailRequired: "Renseigne ton adresse e-mail",
261
301
  passwordRequired: "Renseigne ton mot de passe",
262
302
  invalidCredentials: "Adresse e-mail ou mot de passe incorrect",
303
+ emailNotVerified: "Ton adresse e-mail n'est pas encore confirm\xE9e. V\xE9rifie ta bo\xEEte de r\xE9ception.",
263
304
  // accountLinking is disabled in createPlatformAuth, so a social sign-in on an
264
305
  // address already registered with a password is refused with this code.
265
306
  // Without a message the button reads as broken rather than as a rejection.
@@ -269,6 +310,7 @@ var DEFAULTS = {
269
310
  };
270
311
  function LoginForm({
271
312
  onSuccess,
313
+ onEmailNotVerified,
272
314
  registerUrl = "/register",
273
315
  forgotPasswordUrl = "/forgot-password",
274
316
  socialCallbackUrl = "/",
@@ -278,6 +320,8 @@ function LoginForm({
278
320
  submitClassName,
279
321
  fieldClassName,
280
322
  error: externalError,
323
+ linkComponent,
324
+ invite,
281
325
  authClient
282
326
  }) {
283
327
  const t = { ...DEFAULTS, ...labels };
@@ -305,7 +349,13 @@ function LoginForm({
305
349
  password
306
350
  });
307
351
  if (res?.error) {
308
- setError(res.error.message ?? t.invalidCredentials);
352
+ if (isEmailNotVerified(res.error) && onEmailNotVerified) {
353
+ onEmailNotVerified(email.trim());
354
+ return;
355
+ }
356
+ setError(
357
+ isEmailNotVerified(res.error) ? t.emailNotVerified : res.error.message ?? t.invalidCredentials
358
+ );
309
359
  return;
310
360
  }
311
361
  if (coreTokenUrl) {
@@ -323,7 +373,9 @@ function LoginForm({
323
373
  try {
324
374
  await authClient.signIn.social({
325
375
  provider,
326
- callbackURL: socialCallbackUrl
376
+ // The invitation rides the callback: an OAuth sign-up leaves the
377
+ // browser, and the auth handler redeems the token on the way back.
378
+ callbackURL: withInviteToken(socialCallbackUrl, invite)
327
379
  });
328
380
  } catch (err) {
329
381
  const code = err instanceof Error ? err.message : "";
@@ -332,10 +384,10 @@ function LoginForm({
332
384
  );
333
385
  }
334
386
  };
335
- return /* @__PURE__ */ jsxs4("div", { className: "space-y-6", children: [
336
- /* @__PURE__ */ jsx5(AuthAlert, { children: error }),
337
- /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className: "space-y-5", noValidate: true, children: [
338
- /* @__PURE__ */ jsx5(
387
+ return /* @__PURE__ */ jsxs4("div", { className: "space-y-7", children: [
388
+ /* @__PURE__ */ jsx6(AuthAlert, { children: error }),
389
+ /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
390
+ /* @__PURE__ */ jsx6(
339
391
  AuthField,
340
392
  {
341
393
  label: t.emailPlaceholder,
@@ -352,7 +404,7 @@ function LoginForm({
352
404
  fieldClassName
353
405
  }
354
406
  ),
355
- /* @__PURE__ */ jsx5(
407
+ /* @__PURE__ */ jsx6(
356
408
  AuthField,
357
409
  {
358
410
  label: t.passwordPlaceholder,
@@ -364,19 +416,21 @@ function LoginForm({
364
416
  autoComplete: "current-password",
365
417
  invalid: !!error,
366
418
  fieldClassName,
367
- hint: /* @__PURE__ */ jsx5(
368
- "a",
419
+ hint: /* @__PURE__ */ jsx6(
420
+ AuthLink,
369
421
  {
370
- href: forgotPasswordUrl,
371
- className: "rounded text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
422
+ to: forgotPasswordUrl,
423
+ as: linkComponent,
424
+ className: AUTH_HINT_LINK_CLASS,
372
425
  children: t.forgotPassword
373
426
  }
374
427
  )
375
428
  }
376
429
  ),
377
- /* @__PURE__ */ jsx5(
430
+ /* @__PURE__ */ jsx6(
378
431
  AuthSubmit,
379
432
  {
433
+ spacedAbove: true,
380
434
  pending: isPending,
381
435
  disabled: !email.trim() || !password,
382
436
  pendingLabel: t.submitPending,
@@ -385,7 +439,7 @@ function LoginForm({
385
439
  }
386
440
  )
387
441
  ] }),
388
- /* @__PURE__ */ jsx5(
442
+ /* @__PURE__ */ jsx6(
389
443
  SocialButtons,
390
444
  {
391
445
  providers: socialProviders,
@@ -396,11 +450,12 @@ function LoginForm({
396
450
  /* @__PURE__ */ jsxs4("p", { className: "text-center text-sm text-muted-foreground", children: [
397
451
  t.noAccount,
398
452
  " ",
399
- /* @__PURE__ */ jsx5(
400
- "a",
453
+ /* @__PURE__ */ jsx6(
454
+ AuthLink,
401
455
  {
402
- href: registerUrl,
403
- 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",
456
+ to: withInviteToken(registerUrl, invite),
457
+ as: linkComponent,
458
+ className: AUTH_LINK_CLASS,
404
459
  children: t.register
405
460
  }
406
461
  )
@@ -414,7 +469,7 @@ LoginForm.defaults = {
414
469
 
415
470
  // src/components/register-form.tsx
416
471
  import { useState as useState3 } from "react";
417
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
472
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
418
473
  var MIN_PASSWORD_LENGTH = 8;
419
474
  var DEFAULTS2 = {
420
475
  title: "Cr\xE9er un compte",
@@ -444,6 +499,8 @@ function RegisterForm({
444
499
  submitClassName,
445
500
  fieldClassName,
446
501
  error: externalError,
502
+ linkComponent,
503
+ invite,
447
504
  authClient
448
505
  }) {
449
506
  const t = { ...DEFAULTS2, ...labels };
@@ -499,16 +556,18 @@ function RegisterForm({
499
556
  try {
500
557
  await authClient.signIn.social({
501
558
  provider,
502
- callbackURL: socialCallbackUrl
559
+ // The invitation rides the callback: an OAuth sign-up leaves the
560
+ // browser, and the auth handler redeems the token on the way back.
561
+ callbackURL: withInviteToken(socialCallbackUrl, invite)
503
562
  });
504
563
  } catch (err) {
505
564
  setError(err instanceof Error ? err.message : t.signUpFailed);
506
565
  }
507
566
  };
508
- return /* @__PURE__ */ jsxs5("div", { className: "space-y-6", children: [
509
- /* @__PURE__ */ jsx6(AuthAlert, { children: error }),
510
- /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className: "space-y-5", noValidate: true, children: [
511
- /* @__PURE__ */ jsx6(
567
+ return /* @__PURE__ */ jsxs5("div", { className: "space-y-7", children: [
568
+ /* @__PURE__ */ jsx7(AuthAlert, { children: error }),
569
+ /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
570
+ /* @__PURE__ */ jsx7(
512
571
  AuthField,
513
572
  {
514
573
  label: t.namePlaceholder,
@@ -521,7 +580,7 @@ function RegisterForm({
521
580
  fieldClassName
522
581
  }
523
582
  ),
524
- /* @__PURE__ */ jsx6(
583
+ /* @__PURE__ */ jsx7(
525
584
  AuthField,
526
585
  {
527
586
  label: t.emailPlaceholder,
@@ -537,7 +596,7 @@ function RegisterForm({
537
596
  fieldClassName
538
597
  }
539
598
  ),
540
- /* @__PURE__ */ jsx6(
599
+ /* @__PURE__ */ jsx7(
541
600
  AuthField,
542
601
  {
543
602
  label: t.passwordPlaceholder,
@@ -552,7 +611,7 @@ function RegisterForm({
552
611
  fieldClassName
553
612
  }
554
613
  ),
555
- /* @__PURE__ */ jsx6(
614
+ /* @__PURE__ */ jsx7(
556
615
  AuthField,
557
616
  {
558
617
  label: t.confirmPlaceholder,
@@ -566,18 +625,19 @@ function RegisterForm({
566
625
  fieldClassName
567
626
  }
568
627
  ),
569
- /* @__PURE__ */ jsx6(
628
+ /* @__PURE__ */ jsx7(
570
629
  AuthSubmit,
571
630
  {
631
+ spacedAbove: true,
572
632
  pending: isPending,
573
633
  pendingLabel: t.submitPending,
574
634
  className: submitClassName,
575
635
  children: t.submit
576
636
  }
577
637
  ),
578
- legal && /* @__PURE__ */ jsx6("p", { className: "text-center text-xs leading-relaxed text-muted-foreground", children: legal })
638
+ legal && /* @__PURE__ */ jsx7("p", { className: "text-center text-xs leading-relaxed text-muted-foreground", children: legal })
579
639
  ] }),
580
- /* @__PURE__ */ jsx6(
640
+ /* @__PURE__ */ jsx7(
581
641
  SocialButtons,
582
642
  {
583
643
  providers: socialProviders,
@@ -588,11 +648,12 @@ function RegisterForm({
588
648
  /* @__PURE__ */ jsxs5("p", { className: "text-center text-sm text-muted-foreground", children: [
589
649
  t.haveAccount,
590
650
  " ",
591
- /* @__PURE__ */ jsx6(
592
- "a",
651
+ /* @__PURE__ */ jsx7(
652
+ AuthLink,
593
653
  {
594
- href: loginUrl,
595
- 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",
654
+ to: withInviteToken(loginUrl, invite),
655
+ as: linkComponent,
656
+ className: AUTH_LINK_CLASS,
596
657
  children: t.login
597
658
  }
598
659
  )
@@ -608,7 +669,7 @@ RegisterForm.defaults = {
608
669
  import { useState as useState4 } from "react";
609
670
 
610
671
  // src/components/auth-otp-field.tsx
611
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
672
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
612
673
  var OTP_LENGTH = 6;
613
674
  function AuthOtpField({
614
675
  label,
@@ -620,7 +681,7 @@ function AuthOtpField({
620
681
  fieldClassName = "border-foreground/25 bg-background"
621
682
  }) {
622
683
  return /* @__PURE__ */ jsxs6("div", { className: "space-y-2", children: [
623
- /* @__PURE__ */ jsx7(
684
+ /* @__PURE__ */ jsx8(
624
685
  "label",
625
686
  {
626
687
  htmlFor: id,
@@ -628,7 +689,7 @@ function AuthOtpField({
628
689
  children: label
629
690
  }
630
691
  ),
631
- /* @__PURE__ */ jsx7(
692
+ /* @__PURE__ */ jsx8(
632
693
  "input",
633
694
  {
634
695
  id,
@@ -657,7 +718,7 @@ function AuthOtpField({
657
718
  }
658
719
 
659
720
  // src/components/verify-email-form.tsx
660
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
721
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
661
722
  var DEFAULTS3 = {
662
723
  title: "V\xE9rifie ton adresse",
663
724
  subtitle: "Entre le code \xE0 6 chiffres envoy\xE9 \xE0",
@@ -682,6 +743,7 @@ function VerifyEmailForm({
682
743
  labels,
683
744
  submitClassName,
684
745
  fieldClassName,
746
+ linkComponent,
685
747
  authClient
686
748
  }) {
687
749
  const t = { ...DEFAULTS3, ...labels };
@@ -732,11 +794,11 @@ function VerifyEmailForm({
732
794
  setIsResending(false);
733
795
  }
734
796
  };
735
- return /* @__PURE__ */ jsxs7("div", { className: "space-y-6", children: [
736
- /* @__PURE__ */ jsx8(AuthAlert, { children: error }),
737
- /* @__PURE__ */ jsx8(AuthAlert, { tone: "success", children: resendMessage }),
738
- /* @__PURE__ */ jsxs7("form", { onSubmit: handleVerify, className: "space-y-5", noValidate: true, children: [
739
- /* @__PURE__ */ jsx8(
797
+ return /* @__PURE__ */ jsxs7("div", { className: "space-y-7", children: [
798
+ /* @__PURE__ */ jsx9(AuthAlert, { children: error }),
799
+ /* @__PURE__ */ jsx9(AuthAlert, { tone: "success", children: resendMessage }),
800
+ /* @__PURE__ */ jsxs7("form", { onSubmit: handleVerify, className: "space-y-[1.125rem]", noValidate: true, children: [
801
+ /* @__PURE__ */ jsx9(
740
802
  AuthOtpField,
741
803
  {
742
804
  id: "verify-email-otp",
@@ -747,9 +809,10 @@ function VerifyEmailForm({
747
809
  fieldClassName
748
810
  }
749
811
  ),
750
- /* @__PURE__ */ jsx8(
812
+ /* @__PURE__ */ jsx9(
751
813
  AuthSubmit,
752
814
  {
815
+ spacedAbove: true,
753
816
  pending: isVerifying,
754
817
  disabled: otp.length < OTP_LENGTH,
755
818
  pendingLabel: t.submitPending,
@@ -759,7 +822,7 @@ function VerifyEmailForm({
759
822
  )
760
823
  ] }),
761
824
  /* @__PURE__ */ jsxs7("div", { className: "space-y-4 text-center text-sm text-muted-foreground", children: [
762
- /* @__PURE__ */ jsx8(
825
+ /* @__PURE__ */ jsx9(
763
826
  "button",
764
827
  {
765
828
  type: "button",
@@ -772,11 +835,12 @@ function VerifyEmailForm({
772
835
  /* @__PURE__ */ jsxs7("p", { children: [
773
836
  t.alreadyVerified,
774
837
  " ",
775
- /* @__PURE__ */ jsx8(
776
- "a",
838
+ /* @__PURE__ */ jsx9(
839
+ AuthLink,
777
840
  {
778
- href: loginUrl,
779
- 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",
841
+ to: loginUrl,
842
+ as: linkComponent,
843
+ className: AUTH_LINK_CLASS,
780
844
  children: t.login
781
845
  }
782
846
  )
@@ -791,7 +855,7 @@ VerifyEmailForm.defaults = {
791
855
 
792
856
  // src/components/forgot-password-form.tsx
793
857
  import { useState as useState5 } from "react";
794
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
858
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
795
859
  var DEFAULTS4 = {
796
860
  title: "Mot de passe oubli\xE9 ?",
797
861
  subtitle: "Entre ton adresse e-mail et nous t'enverrons un code pour le r\xE9initialiser.",
@@ -809,6 +873,7 @@ function ForgotPasswordForm({
809
873
  labels,
810
874
  submitClassName,
811
875
  fieldClassName,
876
+ linkComponent,
812
877
  authClient
813
878
  }) {
814
879
  const t = { ...DEFAULTS4, ...labels };
@@ -839,10 +904,10 @@ function ForgotPasswordForm({
839
904
  setIsPending(false);
840
905
  }
841
906
  };
842
- return /* @__PURE__ */ jsxs8("div", { className: "space-y-6", children: [
843
- /* @__PURE__ */ jsx9(AuthAlert, { children: error }),
844
- /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, className: "space-y-5", noValidate: true, children: [
845
- /* @__PURE__ */ jsx9(
907
+ return /* @__PURE__ */ jsxs8("div", { className: "space-y-7", children: [
908
+ /* @__PURE__ */ jsx10(AuthAlert, { children: error }),
909
+ /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
910
+ /* @__PURE__ */ jsx10(
846
911
  AuthField,
847
912
  {
848
913
  label: t.emailPlaceholder,
@@ -858,9 +923,10 @@ function ForgotPasswordForm({
858
923
  invalid: !!error
859
924
  }
860
925
  ),
861
- /* @__PURE__ */ jsx9(
926
+ /* @__PURE__ */ jsx10(
862
927
  AuthSubmit,
863
928
  {
929
+ spacedAbove: true,
864
930
  pending: isPending,
865
931
  disabled: !email.trim(),
866
932
  pendingLabel: t.submitPending,
@@ -872,11 +938,12 @@ function ForgotPasswordForm({
872
938
  /* @__PURE__ */ jsxs8("p", { className: "text-center text-sm text-muted-foreground", children: [
873
939
  t.rememberPassword,
874
940
  " ",
875
- /* @__PURE__ */ jsx9(
876
- "a",
941
+ /* @__PURE__ */ jsx10(
942
+ AuthLink,
877
943
  {
878
- href: loginUrl,
879
- 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",
944
+ to: loginUrl,
945
+ as: linkComponent,
946
+ className: AUTH_LINK_CLASS,
880
947
  children: t.login
881
948
  }
882
949
  )
@@ -890,7 +957,7 @@ ForgotPasswordForm.defaults = {
890
957
 
891
958
  // src/components/reset-password-form.tsx
892
959
  import { useState as useState6 } from "react";
893
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
960
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
894
961
  var MIN_PASSWORD_LENGTH2 = 8;
895
962
  var DEFAULTS5 = {
896
963
  title: "Nouveau mot de passe",
@@ -919,6 +986,7 @@ function ResetPasswordForm({
919
986
  labels,
920
987
  submitClassName,
921
988
  fieldClassName,
989
+ linkComponent,
922
990
  authClient
923
991
  }) {
924
992
  const t = { ...DEFAULTS5, ...labels };
@@ -982,11 +1050,11 @@ function ResetPasswordForm({
982
1050
  setIsResending(false);
983
1051
  }
984
1052
  };
985
- return /* @__PURE__ */ jsxs9("div", { className: "space-y-6", children: [
986
- /* @__PURE__ */ jsx10(AuthAlert, { children: error }),
987
- /* @__PURE__ */ jsx10(AuthAlert, { tone: "success", children: resendMessage }),
988
- /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, className: "space-y-5", noValidate: true, children: [
989
- /* @__PURE__ */ jsx10(
1053
+ return /* @__PURE__ */ jsxs9("div", { className: "space-y-7", children: [
1054
+ /* @__PURE__ */ jsx11(AuthAlert, { children: error }),
1055
+ /* @__PURE__ */ jsx11(AuthAlert, { tone: "success", children: resendMessage }),
1056
+ /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
1057
+ /* @__PURE__ */ jsx11(
990
1058
  AuthOtpField,
991
1059
  {
992
1060
  id: "reset-password-otp",
@@ -997,7 +1065,7 @@ function ResetPasswordForm({
997
1065
  fieldClassName
998
1066
  }
999
1067
  ),
1000
- /* @__PURE__ */ jsx10(
1068
+ /* @__PURE__ */ jsx11(
1001
1069
  AuthField,
1002
1070
  {
1003
1071
  label: t.passwordPlaceholder,
@@ -1011,7 +1079,7 @@ function ResetPasswordForm({
1011
1079
  invalid: tooShort
1012
1080
  }
1013
1081
  ),
1014
- /* @__PURE__ */ jsx10(
1082
+ /* @__PURE__ */ jsx11(
1015
1083
  AuthField,
1016
1084
  {
1017
1085
  label: t.confirmPlaceholder,
@@ -1024,9 +1092,10 @@ function ResetPasswordForm({
1024
1092
  invalid: mismatch
1025
1093
  }
1026
1094
  ),
1027
- /* @__PURE__ */ jsx10(
1095
+ /* @__PURE__ */ jsx11(
1028
1096
  AuthSubmit,
1029
1097
  {
1098
+ spacedAbove: true,
1030
1099
  pending: isResetting,
1031
1100
  disabled: otp.length < OTP_LENGTH || !password || !confirmPassword,
1032
1101
  pendingLabel: t.submitPending,
@@ -1036,7 +1105,7 @@ function ResetPasswordForm({
1036
1105
  )
1037
1106
  ] }),
1038
1107
  /* @__PURE__ */ jsxs9("div", { className: "space-y-4 text-center text-sm text-muted-foreground", children: [
1039
- /* @__PURE__ */ jsx10(
1108
+ /* @__PURE__ */ jsx11(
1040
1109
  "button",
1041
1110
  {
1042
1111
  type: "button",
@@ -1049,11 +1118,12 @@ function ResetPasswordForm({
1049
1118
  /* @__PURE__ */ jsxs9("p", { children: [
1050
1119
  t.rememberPassword,
1051
1120
  " ",
1052
- /* @__PURE__ */ jsx10(
1053
- "a",
1121
+ /* @__PURE__ */ jsx11(
1122
+ AuthLink,
1054
1123
  {
1055
- href: loginUrl,
1056
- 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",
1124
+ to: loginUrl,
1125
+ as: linkComponent,
1126
+ className: AUTH_LINK_CLASS,
1057
1127
  children: t.login
1058
1128
  }
1059
1129
  )
@@ -1067,20 +1137,20 @@ ResetPasswordForm.defaults = {
1067
1137
  };
1068
1138
 
1069
1139
  // src/components/auth-heading.tsx
1070
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1140
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1071
1141
  function AuthHeading({
1072
1142
  title,
1073
1143
  subtitle,
1074
- titleClassName = "text-2xl font-semibold tracking-tight"
1144
+ titleClassName = "text-[2rem] font-semibold leading-[1.1] tracking-[-0.03em] sm:text-[2.25rem]"
1075
1145
  }) {
1076
- return /* @__PURE__ */ jsxs10("header", { className: "space-y-1.5 text-center", children: [
1077
- /* @__PURE__ */ jsx11("h1", { className: `leading-tight text-foreground ${titleClassName}`, children: title }),
1078
- subtitle && /* @__PURE__ */ jsx11("p", { className: "text-balance text-sm leading-relaxed text-muted-foreground", children: subtitle })
1146
+ return /* @__PURE__ */ jsxs10("header", { className: "space-y-2.5 text-center", children: [
1147
+ /* @__PURE__ */ jsx12("h1", { className: `text-foreground ${titleClassName}`, children: title }),
1148
+ subtitle && /* @__PURE__ */ jsx12("p", { className: "text-balance text-[0.9375rem] leading-relaxed text-muted-foreground", children: subtitle })
1079
1149
  ] });
1080
1150
  }
1081
1151
 
1082
1152
  // src/components/auth-layout.tsx
1083
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1153
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1084
1154
  function AuthLayout({
1085
1155
  logo,
1086
1156
  panel,
@@ -1090,10 +1160,14 @@ function AuthLayout({
1090
1160
  children,
1091
1161
  footer
1092
1162
  }) {
1093
- return /* @__PURE__ */ jsx12("div", { className: "flex min-h-dvh flex-col bg-background px-5 pb-10 pt-12 sm:items-center sm:bg-muted/40 sm:px-6 sm:py-16", children: /* @__PURE__ */ jsxs11("div", { className: `mx-auto w-full ${panel ? "max-w-4xl" : "max-w-[420px]"}`, children: [
1094
- (logo || title) && /* @__PURE__ */ jsxs11("div", { className: "mb-7 space-y-5", children: [
1095
- logo && /* @__PURE__ */ jsx12("div", { className: "flex justify-center", children: logo }),
1096
- title && /* @__PURE__ */ jsx12(
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(
1097
1171
  AuthHeading,
1098
1172
  {
1099
1173
  title,
@@ -1106,13 +1180,14 @@ function AuthLayout({
1106
1180
  "div",
1107
1181
  {
1108
1182
  className: [
1109
- "sm:rounded-lg sm:border sm:border-border/70 sm:bg-card sm:shadow-sm",
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)]",
1110
1185
  panel ? "sm:overflow-hidden md:grid md:grid-cols-2" : ""
1111
1186
  ].filter(Boolean).join(" "),
1112
1187
  children: [
1113
1188
  panel && // Decorative: it must never carry information the form does not,
1114
1189
  // so it is hidden from assistive tech rather than described.
1115
- /* @__PURE__ */ jsx12(
1190
+ /* @__PURE__ */ jsx13(
1116
1191
  "div",
1117
1192
  {
1118
1193
  "aria-hidden": "true",
@@ -1120,16 +1195,16 @@ function AuthLayout({
1120
1195
  children: panel
1121
1196
  }
1122
1197
  ),
1123
- /* @__PURE__ */ jsx12("div", { className: "flex flex-col justify-center sm:p-8", children })
1198
+ /* @__PURE__ */ jsx13("div", { className: "sm:px-7 sm:py-8", children })
1124
1199
  ]
1125
1200
  }
1126
1201
  ),
1127
- footer && /* @__PURE__ */ jsx12("div", { className: "mt-6 text-center text-xs leading-relaxed text-muted-foreground", children: footer })
1202
+ footer && /* @__PURE__ */ jsx13("div", { className: "mt-8 text-center text-xs leading-relaxed text-muted-foreground", children: footer })
1128
1203
  ] }) });
1129
1204
  }
1130
1205
 
1131
1206
  // src/components/invitation-notice.tsx
1132
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1207
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1133
1208
  function defaultSupportEmail() {
1134
1209
  if (typeof window === "undefined") return void 0;
1135
1210
  const host = window.location.hostname;
@@ -1151,13 +1226,13 @@ function InvitationNotice({
1151
1226
  const contact = supportEmail ?? defaultSupportEmail();
1152
1227
  return /* @__PURE__ */ jsxs12("div", { className: "space-y-8", children: [
1153
1228
  /* @__PURE__ */ jsxs12("div", { children: [
1154
- /* @__PURE__ */ jsx13("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
1155
- /* @__PURE__ */ jsx13("p", { className: "mt-1 text-sm text-muted-foreground", children: REASON_MESSAGE[reason] ?? REASON_MESSAGE.unknown })
1229
+ /* @__PURE__ */ jsx14("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
1230
+ /* @__PURE__ */ jsx14("p", { className: "mt-1 text-sm text-muted-foreground", children: REASON_MESSAGE[reason] ?? REASON_MESSAGE.unknown })
1156
1231
  ] }),
1157
1232
  contact ? /* @__PURE__ */ jsxs12("p", { className: "text-sm text-muted-foreground", children: [
1158
1233
  "\xC9crivez-nous \xE0",
1159
1234
  " ",
1160
- /* @__PURE__ */ jsx13(
1235
+ /* @__PURE__ */ jsx14(
1161
1236
  "a",
1162
1237
  {
1163
1238
  href: `mailto:${contact}`,
@@ -1171,9 +1246,35 @@ function InvitationNotice({
1171
1246
  action
1172
1247
  ] });
1173
1248
  }
1249
+
1250
+ // src/oauth-error.ts
1251
+ function oauthErrorMessage(code, labels) {
1252
+ switch (code) {
1253
+ case "account_not_linked":
1254
+ return labels.accountNotLinked;
1255
+ case "access_denied":
1256
+ return labels.socialCancelled;
1257
+ default:
1258
+ return labels.socialFailed;
1259
+ }
1260
+ }
1261
+ function initialOAuthError(labels, param = "error") {
1262
+ if (typeof window === "undefined") return void 0;
1263
+ const code = new URLSearchParams(window.location.search).get(param);
1264
+ if (!code) return void 0;
1265
+ return oauthErrorMessage(code, labels);
1266
+ }
1267
+ function clearOAuthError(param = "error") {
1268
+ if (typeof window === "undefined") return;
1269
+ const url = new URL(window.location.href);
1270
+ if (!url.searchParams.has(param)) return;
1271
+ url.searchParams.delete(param);
1272
+ window.history.replaceState({}, "", url.toString());
1273
+ }
1174
1274
  export {
1175
1275
  AuthField,
1176
1276
  AuthLayout,
1277
+ AuthLink,
1177
1278
  AuthSubmit,
1178
1279
  ForgotPasswordForm,
1179
1280
  InvitationNotice,
@@ -1182,8 +1283,14 @@ export {
1182
1283
  ResetPasswordForm,
1183
1284
  SocialButtons,
1184
1285
  VerifyEmailForm,
1286
+ clearOAuthError,
1287
+ initialOAuthError,
1288
+ isEmailNotVerified,
1185
1289
  isInvitationFailure,
1290
+ normalizeInviteToken,
1291
+ oauthErrorMessage,
1186
1292
  useLogout,
1187
- useSession
1293
+ useSession,
1294
+ withInviteToken
1188
1295
  };
1189
1296
  //# sourceMappingURL=index.js.map