@lalternative/auth 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js 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(" "),
@@ -245,8 +254,33 @@ function ProviderMark({ provider }) {
245
254
  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
255
  }
247
256
 
257
+ // src/components/auth-link.tsx
258
+ import { jsx as jsx5 } from "react/jsx-runtime";
259
+ function AuthLink({ to, as: Link, className, children }) {
260
+ if (Link) {
261
+ return /* @__PURE__ */ jsx5(Link, { to, className, children });
262
+ }
263
+ return /* @__PURE__ */ jsx5("a", { href: to, className, children });
264
+ }
265
+ 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";
266
+ 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";
267
+
268
+ // src/invite-token.ts
269
+ var MAX_TOKEN_LENGTH = 128;
270
+ function normalizeInviteToken(value) {
271
+ if (typeof value !== "string") return void 0;
272
+ const trimmed = value.trim();
273
+ if (!trimmed || trimmed.length > MAX_TOKEN_LENGTH) return void 0;
274
+ return trimmed;
275
+ }
276
+ function withInviteToken(href, token) {
277
+ if (!token) return href;
278
+ const separator = href.includes("?") ? "&" : "?";
279
+ return `${href}${separator}invite=${encodeURIComponent(token)}`;
280
+ }
281
+
248
282
  // src/components/login-form.tsx
249
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
283
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
250
284
  var DEFAULTS = {
251
285
  title: "Connexion",
252
286
  subtitle: "Content de te revoir. Entre tes identifiants pour continuer.",
@@ -278,6 +312,8 @@ function LoginForm({
278
312
  submitClassName,
279
313
  fieldClassName,
280
314
  error: externalError,
315
+ linkComponent,
316
+ invite,
281
317
  authClient
282
318
  }) {
283
319
  const t = { ...DEFAULTS, ...labels };
@@ -323,7 +359,9 @@ function LoginForm({
323
359
  try {
324
360
  await authClient.signIn.social({
325
361
  provider,
326
- callbackURL: socialCallbackUrl
362
+ // The invitation rides the callback: an OAuth sign-up leaves the
363
+ // browser, and the auth handler redeems the token on the way back.
364
+ callbackURL: withInviteToken(socialCallbackUrl, invite)
327
365
  });
328
366
  } catch (err) {
329
367
  const code = err instanceof Error ? err.message : "";
@@ -332,10 +370,10 @@ function LoginForm({
332
370
  );
333
371
  }
334
372
  };
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(
373
+ return /* @__PURE__ */ jsxs4("div", { className: "space-y-7", children: [
374
+ /* @__PURE__ */ jsx6(AuthAlert, { children: error }),
375
+ /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
376
+ /* @__PURE__ */ jsx6(
339
377
  AuthField,
340
378
  {
341
379
  label: t.emailPlaceholder,
@@ -352,7 +390,7 @@ function LoginForm({
352
390
  fieldClassName
353
391
  }
354
392
  ),
355
- /* @__PURE__ */ jsx5(
393
+ /* @__PURE__ */ jsx6(
356
394
  AuthField,
357
395
  {
358
396
  label: t.passwordPlaceholder,
@@ -364,19 +402,21 @@ function LoginForm({
364
402
  autoComplete: "current-password",
365
403
  invalid: !!error,
366
404
  fieldClassName,
367
- hint: /* @__PURE__ */ jsx5(
368
- "a",
405
+ hint: /* @__PURE__ */ jsx6(
406
+ AuthLink,
369
407
  {
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",
408
+ to: forgotPasswordUrl,
409
+ as: linkComponent,
410
+ className: AUTH_HINT_LINK_CLASS,
372
411
  children: t.forgotPassword
373
412
  }
374
413
  )
375
414
  }
376
415
  ),
377
- /* @__PURE__ */ jsx5(
416
+ /* @__PURE__ */ jsx6(
378
417
  AuthSubmit,
379
418
  {
419
+ spacedAbove: true,
380
420
  pending: isPending,
381
421
  disabled: !email.trim() || !password,
382
422
  pendingLabel: t.submitPending,
@@ -385,7 +425,7 @@ function LoginForm({
385
425
  }
386
426
  )
387
427
  ] }),
388
- /* @__PURE__ */ jsx5(
428
+ /* @__PURE__ */ jsx6(
389
429
  SocialButtons,
390
430
  {
391
431
  providers: socialProviders,
@@ -396,11 +436,12 @@ function LoginForm({
396
436
  /* @__PURE__ */ jsxs4("p", { className: "text-center text-sm text-muted-foreground", children: [
397
437
  t.noAccount,
398
438
  " ",
399
- /* @__PURE__ */ jsx5(
400
- "a",
439
+ /* @__PURE__ */ jsx6(
440
+ AuthLink,
401
441
  {
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",
442
+ to: withInviteToken(registerUrl, invite),
443
+ as: linkComponent,
444
+ className: AUTH_LINK_CLASS,
404
445
  children: t.register
405
446
  }
406
447
  )
@@ -414,7 +455,7 @@ LoginForm.defaults = {
414
455
 
415
456
  // src/components/register-form.tsx
416
457
  import { useState as useState3 } from "react";
417
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
458
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
418
459
  var MIN_PASSWORD_LENGTH = 8;
419
460
  var DEFAULTS2 = {
420
461
  title: "Cr\xE9er un compte",
@@ -444,6 +485,8 @@ function RegisterForm({
444
485
  submitClassName,
445
486
  fieldClassName,
446
487
  error: externalError,
488
+ linkComponent,
489
+ invite,
447
490
  authClient
448
491
  }) {
449
492
  const t = { ...DEFAULTS2, ...labels };
@@ -499,16 +542,18 @@ function RegisterForm({
499
542
  try {
500
543
  await authClient.signIn.social({
501
544
  provider,
502
- callbackURL: socialCallbackUrl
545
+ // The invitation rides the callback: an OAuth sign-up leaves the
546
+ // browser, and the auth handler redeems the token on the way back.
547
+ callbackURL: withInviteToken(socialCallbackUrl, invite)
503
548
  });
504
549
  } catch (err) {
505
550
  setError(err instanceof Error ? err.message : t.signUpFailed);
506
551
  }
507
552
  };
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(
553
+ return /* @__PURE__ */ jsxs5("div", { className: "space-y-7", children: [
554
+ /* @__PURE__ */ jsx7(AuthAlert, { children: error }),
555
+ /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
556
+ /* @__PURE__ */ jsx7(
512
557
  AuthField,
513
558
  {
514
559
  label: t.namePlaceholder,
@@ -521,7 +566,7 @@ function RegisterForm({
521
566
  fieldClassName
522
567
  }
523
568
  ),
524
- /* @__PURE__ */ jsx6(
569
+ /* @__PURE__ */ jsx7(
525
570
  AuthField,
526
571
  {
527
572
  label: t.emailPlaceholder,
@@ -537,7 +582,7 @@ function RegisterForm({
537
582
  fieldClassName
538
583
  }
539
584
  ),
540
- /* @__PURE__ */ jsx6(
585
+ /* @__PURE__ */ jsx7(
541
586
  AuthField,
542
587
  {
543
588
  label: t.passwordPlaceholder,
@@ -552,7 +597,7 @@ function RegisterForm({
552
597
  fieldClassName
553
598
  }
554
599
  ),
555
- /* @__PURE__ */ jsx6(
600
+ /* @__PURE__ */ jsx7(
556
601
  AuthField,
557
602
  {
558
603
  label: t.confirmPlaceholder,
@@ -566,18 +611,19 @@ function RegisterForm({
566
611
  fieldClassName
567
612
  }
568
613
  ),
569
- /* @__PURE__ */ jsx6(
614
+ /* @__PURE__ */ jsx7(
570
615
  AuthSubmit,
571
616
  {
617
+ spacedAbove: true,
572
618
  pending: isPending,
573
619
  pendingLabel: t.submitPending,
574
620
  className: submitClassName,
575
621
  children: t.submit
576
622
  }
577
623
  ),
578
- legal && /* @__PURE__ */ jsx6("p", { className: "text-center text-xs leading-relaxed text-muted-foreground", children: legal })
624
+ legal && /* @__PURE__ */ jsx7("p", { className: "text-center text-xs leading-relaxed text-muted-foreground", children: legal })
579
625
  ] }),
580
- /* @__PURE__ */ jsx6(
626
+ /* @__PURE__ */ jsx7(
581
627
  SocialButtons,
582
628
  {
583
629
  providers: socialProviders,
@@ -588,11 +634,12 @@ function RegisterForm({
588
634
  /* @__PURE__ */ jsxs5("p", { className: "text-center text-sm text-muted-foreground", children: [
589
635
  t.haveAccount,
590
636
  " ",
591
- /* @__PURE__ */ jsx6(
592
- "a",
637
+ /* @__PURE__ */ jsx7(
638
+ AuthLink,
593
639
  {
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",
640
+ to: withInviteToken(loginUrl, invite),
641
+ as: linkComponent,
642
+ className: AUTH_LINK_CLASS,
596
643
  children: t.login
597
644
  }
598
645
  )
@@ -608,7 +655,7 @@ RegisterForm.defaults = {
608
655
  import { useState as useState4 } from "react";
609
656
 
610
657
  // src/components/auth-otp-field.tsx
611
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
658
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
612
659
  var OTP_LENGTH = 6;
613
660
  function AuthOtpField({
614
661
  label,
@@ -620,7 +667,7 @@ function AuthOtpField({
620
667
  fieldClassName = "border-foreground/25 bg-background"
621
668
  }) {
622
669
  return /* @__PURE__ */ jsxs6("div", { className: "space-y-2", children: [
623
- /* @__PURE__ */ jsx7(
670
+ /* @__PURE__ */ jsx8(
624
671
  "label",
625
672
  {
626
673
  htmlFor: id,
@@ -628,7 +675,7 @@ function AuthOtpField({
628
675
  children: label
629
676
  }
630
677
  ),
631
- /* @__PURE__ */ jsx7(
678
+ /* @__PURE__ */ jsx8(
632
679
  "input",
633
680
  {
634
681
  id,
@@ -657,7 +704,7 @@ function AuthOtpField({
657
704
  }
658
705
 
659
706
  // src/components/verify-email-form.tsx
660
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
707
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
661
708
  var DEFAULTS3 = {
662
709
  title: "V\xE9rifie ton adresse",
663
710
  subtitle: "Entre le code \xE0 6 chiffres envoy\xE9 \xE0",
@@ -682,6 +729,7 @@ function VerifyEmailForm({
682
729
  labels,
683
730
  submitClassName,
684
731
  fieldClassName,
732
+ linkComponent,
685
733
  authClient
686
734
  }) {
687
735
  const t = { ...DEFAULTS3, ...labels };
@@ -732,11 +780,11 @@ function VerifyEmailForm({
732
780
  setIsResending(false);
733
781
  }
734
782
  };
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(
783
+ return /* @__PURE__ */ jsxs7("div", { className: "space-y-7", children: [
784
+ /* @__PURE__ */ jsx9(AuthAlert, { children: error }),
785
+ /* @__PURE__ */ jsx9(AuthAlert, { tone: "success", children: resendMessage }),
786
+ /* @__PURE__ */ jsxs7("form", { onSubmit: handleVerify, className: "space-y-[1.125rem]", noValidate: true, children: [
787
+ /* @__PURE__ */ jsx9(
740
788
  AuthOtpField,
741
789
  {
742
790
  id: "verify-email-otp",
@@ -747,9 +795,10 @@ function VerifyEmailForm({
747
795
  fieldClassName
748
796
  }
749
797
  ),
750
- /* @__PURE__ */ jsx8(
798
+ /* @__PURE__ */ jsx9(
751
799
  AuthSubmit,
752
800
  {
801
+ spacedAbove: true,
753
802
  pending: isVerifying,
754
803
  disabled: otp.length < OTP_LENGTH,
755
804
  pendingLabel: t.submitPending,
@@ -759,7 +808,7 @@ function VerifyEmailForm({
759
808
  )
760
809
  ] }),
761
810
  /* @__PURE__ */ jsxs7("div", { className: "space-y-4 text-center text-sm text-muted-foreground", children: [
762
- /* @__PURE__ */ jsx8(
811
+ /* @__PURE__ */ jsx9(
763
812
  "button",
764
813
  {
765
814
  type: "button",
@@ -772,11 +821,12 @@ function VerifyEmailForm({
772
821
  /* @__PURE__ */ jsxs7("p", { children: [
773
822
  t.alreadyVerified,
774
823
  " ",
775
- /* @__PURE__ */ jsx8(
776
- "a",
824
+ /* @__PURE__ */ jsx9(
825
+ AuthLink,
777
826
  {
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",
827
+ to: loginUrl,
828
+ as: linkComponent,
829
+ className: AUTH_LINK_CLASS,
780
830
  children: t.login
781
831
  }
782
832
  )
@@ -791,7 +841,7 @@ VerifyEmailForm.defaults = {
791
841
 
792
842
  // src/components/forgot-password-form.tsx
793
843
  import { useState as useState5 } from "react";
794
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
844
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
795
845
  var DEFAULTS4 = {
796
846
  title: "Mot de passe oubli\xE9 ?",
797
847
  subtitle: "Entre ton adresse e-mail et nous t'enverrons un code pour le r\xE9initialiser.",
@@ -809,6 +859,7 @@ function ForgotPasswordForm({
809
859
  labels,
810
860
  submitClassName,
811
861
  fieldClassName,
862
+ linkComponent,
812
863
  authClient
813
864
  }) {
814
865
  const t = { ...DEFAULTS4, ...labels };
@@ -839,10 +890,10 @@ function ForgotPasswordForm({
839
890
  setIsPending(false);
840
891
  }
841
892
  };
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(
893
+ return /* @__PURE__ */ jsxs8("div", { className: "space-y-7", children: [
894
+ /* @__PURE__ */ jsx10(AuthAlert, { children: error }),
895
+ /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
896
+ /* @__PURE__ */ jsx10(
846
897
  AuthField,
847
898
  {
848
899
  label: t.emailPlaceholder,
@@ -858,9 +909,10 @@ function ForgotPasswordForm({
858
909
  invalid: !!error
859
910
  }
860
911
  ),
861
- /* @__PURE__ */ jsx9(
912
+ /* @__PURE__ */ jsx10(
862
913
  AuthSubmit,
863
914
  {
915
+ spacedAbove: true,
864
916
  pending: isPending,
865
917
  disabled: !email.trim(),
866
918
  pendingLabel: t.submitPending,
@@ -872,11 +924,12 @@ function ForgotPasswordForm({
872
924
  /* @__PURE__ */ jsxs8("p", { className: "text-center text-sm text-muted-foreground", children: [
873
925
  t.rememberPassword,
874
926
  " ",
875
- /* @__PURE__ */ jsx9(
876
- "a",
927
+ /* @__PURE__ */ jsx10(
928
+ AuthLink,
877
929
  {
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",
930
+ to: loginUrl,
931
+ as: linkComponent,
932
+ className: AUTH_LINK_CLASS,
880
933
  children: t.login
881
934
  }
882
935
  )
@@ -890,7 +943,7 @@ ForgotPasswordForm.defaults = {
890
943
 
891
944
  // src/components/reset-password-form.tsx
892
945
  import { useState as useState6 } from "react";
893
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
946
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
894
947
  var MIN_PASSWORD_LENGTH2 = 8;
895
948
  var DEFAULTS5 = {
896
949
  title: "Nouveau mot de passe",
@@ -919,6 +972,7 @@ function ResetPasswordForm({
919
972
  labels,
920
973
  submitClassName,
921
974
  fieldClassName,
975
+ linkComponent,
922
976
  authClient
923
977
  }) {
924
978
  const t = { ...DEFAULTS5, ...labels };
@@ -982,11 +1036,11 @@ function ResetPasswordForm({
982
1036
  setIsResending(false);
983
1037
  }
984
1038
  };
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(
1039
+ return /* @__PURE__ */ jsxs9("div", { className: "space-y-7", children: [
1040
+ /* @__PURE__ */ jsx11(AuthAlert, { children: error }),
1041
+ /* @__PURE__ */ jsx11(AuthAlert, { tone: "success", children: resendMessage }),
1042
+ /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, className: "space-y-[1.125rem]", noValidate: true, children: [
1043
+ /* @__PURE__ */ jsx11(
990
1044
  AuthOtpField,
991
1045
  {
992
1046
  id: "reset-password-otp",
@@ -997,7 +1051,7 @@ function ResetPasswordForm({
997
1051
  fieldClassName
998
1052
  }
999
1053
  ),
1000
- /* @__PURE__ */ jsx10(
1054
+ /* @__PURE__ */ jsx11(
1001
1055
  AuthField,
1002
1056
  {
1003
1057
  label: t.passwordPlaceholder,
@@ -1011,7 +1065,7 @@ function ResetPasswordForm({
1011
1065
  invalid: tooShort
1012
1066
  }
1013
1067
  ),
1014
- /* @__PURE__ */ jsx10(
1068
+ /* @__PURE__ */ jsx11(
1015
1069
  AuthField,
1016
1070
  {
1017
1071
  label: t.confirmPlaceholder,
@@ -1024,9 +1078,10 @@ function ResetPasswordForm({
1024
1078
  invalid: mismatch
1025
1079
  }
1026
1080
  ),
1027
- /* @__PURE__ */ jsx10(
1081
+ /* @__PURE__ */ jsx11(
1028
1082
  AuthSubmit,
1029
1083
  {
1084
+ spacedAbove: true,
1030
1085
  pending: isResetting,
1031
1086
  disabled: otp.length < OTP_LENGTH || !password || !confirmPassword,
1032
1087
  pendingLabel: t.submitPending,
@@ -1036,7 +1091,7 @@ function ResetPasswordForm({
1036
1091
  )
1037
1092
  ] }),
1038
1093
  /* @__PURE__ */ jsxs9("div", { className: "space-y-4 text-center text-sm text-muted-foreground", children: [
1039
- /* @__PURE__ */ jsx10(
1094
+ /* @__PURE__ */ jsx11(
1040
1095
  "button",
1041
1096
  {
1042
1097
  type: "button",
@@ -1049,11 +1104,12 @@ function ResetPasswordForm({
1049
1104
  /* @__PURE__ */ jsxs9("p", { children: [
1050
1105
  t.rememberPassword,
1051
1106
  " ",
1052
- /* @__PURE__ */ jsx10(
1053
- "a",
1107
+ /* @__PURE__ */ jsx11(
1108
+ AuthLink,
1054
1109
  {
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",
1110
+ to: loginUrl,
1111
+ as: linkComponent,
1112
+ className: AUTH_LINK_CLASS,
1057
1113
  children: t.login
1058
1114
  }
1059
1115
  )
@@ -1067,20 +1123,20 @@ ResetPasswordForm.defaults = {
1067
1123
  };
1068
1124
 
1069
1125
  // src/components/auth-heading.tsx
1070
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1126
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1071
1127
  function AuthHeading({
1072
1128
  title,
1073
1129
  subtitle,
1074
- titleClassName = "text-2xl font-semibold tracking-tight"
1130
+ titleClassName = "text-[2rem] font-semibold leading-[1.1] tracking-[-0.03em] sm:text-[2.25rem]"
1075
1131
  }) {
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 })
1132
+ return /* @__PURE__ */ jsxs10("header", { className: "space-y-2.5 text-center", children: [
1133
+ /* @__PURE__ */ jsx12("h1", { className: `text-foreground ${titleClassName}`, children: title }),
1134
+ subtitle && /* @__PURE__ */ jsx12("p", { className: "text-balance text-[0.9375rem] leading-relaxed text-muted-foreground", children: subtitle })
1079
1135
  ] });
1080
1136
  }
1081
1137
 
1082
1138
  // src/components/auth-layout.tsx
1083
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1139
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1084
1140
  function AuthLayout({
1085
1141
  logo,
1086
1142
  panel,
@@ -1090,10 +1146,14 @@ function AuthLayout({
1090
1146
  children,
1091
1147
  footer
1092
1148
  }) {
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(
1149
+ 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: [
1150
+ (logo || title) && // The spacing is composed, not repeated: the mark sits close to the
1151
+ // title it introduces, and the gap down to the card is the largest
1152
+ // on the screen that step is what separates "who this is" from
1153
+ // "what you do here".
1154
+ /* @__PURE__ */ jsxs11("div", { className: "mb-10 space-y-6", children: [
1155
+ logo && /* @__PURE__ */ jsx13("div", { className: "flex justify-center", children: logo }),
1156
+ title && /* @__PURE__ */ jsx13(
1097
1157
  AuthHeading,
1098
1158
  {
1099
1159
  title,
@@ -1106,13 +1166,14 @@ function AuthLayout({
1106
1166
  "div",
1107
1167
  {
1108
1168
  className: [
1109
- "sm:rounded-lg sm:border sm:border-border/70 sm:bg-card sm:shadow-sm",
1169
+ "sm:rounded-xl sm:border sm:border-foreground/[0.08] sm:bg-card",
1170
+ "sm:shadow-[0_1px_2px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.10)]",
1110
1171
  panel ? "sm:overflow-hidden md:grid md:grid-cols-2" : ""
1111
1172
  ].filter(Boolean).join(" "),
1112
1173
  children: [
1113
1174
  panel && // Decorative: it must never carry information the form does not,
1114
1175
  // so it is hidden from assistive tech rather than described.
1115
- /* @__PURE__ */ jsx12(
1176
+ /* @__PURE__ */ jsx13(
1116
1177
  "div",
1117
1178
  {
1118
1179
  "aria-hidden": "true",
@@ -1120,16 +1181,16 @@ function AuthLayout({
1120
1181
  children: panel
1121
1182
  }
1122
1183
  ),
1123
- /* @__PURE__ */ jsx12("div", { className: "flex flex-col justify-center sm:p-8", children })
1184
+ /* @__PURE__ */ jsx13("div", { className: "sm:px-7 sm:py-8", children })
1124
1185
  ]
1125
1186
  }
1126
1187
  ),
1127
- footer && /* @__PURE__ */ jsx12("div", { className: "mt-6 text-center text-xs leading-relaxed text-muted-foreground", children: footer })
1188
+ footer && /* @__PURE__ */ jsx13("div", { className: "mt-8 text-center text-xs leading-relaxed text-muted-foreground", children: footer })
1128
1189
  ] }) });
1129
1190
  }
1130
1191
 
1131
1192
  // src/components/invitation-notice.tsx
1132
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1193
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1133
1194
  function defaultSupportEmail() {
1134
1195
  if (typeof window === "undefined") return void 0;
1135
1196
  const host = window.location.hostname;
@@ -1151,13 +1212,13 @@ function InvitationNotice({
1151
1212
  const contact = supportEmail ?? defaultSupportEmail();
1152
1213
  return /* @__PURE__ */ jsxs12("div", { className: "space-y-8", children: [
1153
1214
  /* @__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 })
1215
+ /* @__PURE__ */ jsx14("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
1216
+ /* @__PURE__ */ jsx14("p", { className: "mt-1 text-sm text-muted-foreground", children: REASON_MESSAGE[reason] ?? REASON_MESSAGE.unknown })
1156
1217
  ] }),
1157
1218
  contact ? /* @__PURE__ */ jsxs12("p", { className: "text-sm text-muted-foreground", children: [
1158
1219
  "\xC9crivez-nous \xE0",
1159
1220
  " ",
1160
- /* @__PURE__ */ jsx13(
1221
+ /* @__PURE__ */ jsx14(
1161
1222
  "a",
1162
1223
  {
1163
1224
  href: `mailto:${contact}`,
@@ -1171,9 +1232,35 @@ function InvitationNotice({
1171
1232
  action
1172
1233
  ] });
1173
1234
  }
1235
+
1236
+ // src/oauth-error.ts
1237
+ function oauthErrorMessage(code, labels) {
1238
+ switch (code) {
1239
+ case "account_not_linked":
1240
+ return labels.accountNotLinked;
1241
+ case "access_denied":
1242
+ return labels.socialCancelled;
1243
+ default:
1244
+ return labels.socialFailed;
1245
+ }
1246
+ }
1247
+ function initialOAuthError(labels, param = "error") {
1248
+ if (typeof window === "undefined") return void 0;
1249
+ const code = new URLSearchParams(window.location.search).get(param);
1250
+ if (!code) return void 0;
1251
+ return oauthErrorMessage(code, labels);
1252
+ }
1253
+ function clearOAuthError(param = "error") {
1254
+ if (typeof window === "undefined") return;
1255
+ const url = new URL(window.location.href);
1256
+ if (!url.searchParams.has(param)) return;
1257
+ url.searchParams.delete(param);
1258
+ window.history.replaceState({}, "", url.toString());
1259
+ }
1174
1260
  export {
1175
1261
  AuthField,
1176
1262
  AuthLayout,
1263
+ AuthLink,
1177
1264
  AuthSubmit,
1178
1265
  ForgotPasswordForm,
1179
1266
  InvitationNotice,
@@ -1182,8 +1269,13 @@ export {
1182
1269
  ResetPasswordForm,
1183
1270
  SocialButtons,
1184
1271
  VerifyEmailForm,
1272
+ clearOAuthError,
1273
+ initialOAuthError,
1185
1274
  isInvitationFailure,
1275
+ normalizeInviteToken,
1276
+ oauthErrorMessage,
1186
1277
  useLogout,
1187
- useSession
1278
+ useSession,
1279
+ withInviteToken
1188
1280
  };
1189
1281
  //# sourceMappingURL=index.js.map