@poly-x/react 0.1.0-alpha.6 → 0.1.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +493 -2
- package/dist/index.d.cts +102 -1
- package/dist/index.d.mts +102 -1
- package/dist/index.mjs +495 -6
- package/dist/styles.css +78 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -352,7 +352,7 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
352
352
|
}
|
|
353
353
|
//#endregion
|
|
354
354
|
//#region src/components/sign-in.tsx
|
|
355
|
-
const DEFAULT_LABELS = {
|
|
355
|
+
const DEFAULT_LABELS$2 = {
|
|
356
356
|
heading: "Sign in",
|
|
357
357
|
subheading: "Enter your credentials to continue.",
|
|
358
358
|
tagline: "",
|
|
@@ -362,6 +362,7 @@ const DEFAULT_LABELS = {
|
|
|
362
362
|
passwordPlaceholder: "••••••••",
|
|
363
363
|
submit: "Sign in",
|
|
364
364
|
submitting: "Signing in…",
|
|
365
|
+
forgotPassword: "Forgot password?",
|
|
365
366
|
chooseWorkspace: "Choose a workspace",
|
|
366
367
|
chooseWorkspaceSubheading: "You have access to more than one workspace.",
|
|
367
368
|
securedBy: "Secured by PolyX",
|
|
@@ -393,7 +394,7 @@ function SignIn(props) {
|
|
|
393
394
|
const action = props.action ?? "/api/polyx/authenticate";
|
|
394
395
|
const variant = props.variant ?? "card";
|
|
395
396
|
const labels = {
|
|
396
|
-
...DEFAULT_LABELS,
|
|
397
|
+
...DEFAULT_LABELS$2,
|
|
397
398
|
...props.labels
|
|
398
399
|
};
|
|
399
400
|
const [email, setEmail] = (0, react.useState)("");
|
|
@@ -632,6 +633,14 @@ function SignIn(props) {
|
|
|
632
633
|
onChange: (event) => setPassword(event.target.value)
|
|
633
634
|
})]
|
|
634
635
|
}),
|
|
636
|
+
props.forgotPasswordUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
637
|
+
className: "polyx-signin__forgot",
|
|
638
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
639
|
+
className: "polyx-signin__link",
|
|
640
|
+
href: props.forgotPasswordUrl,
|
|
641
|
+
children: labels.forgotPassword
|
|
642
|
+
})
|
|
643
|
+
}) : null,
|
|
635
644
|
errorNode,
|
|
636
645
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
637
646
|
type: "submit",
|
|
@@ -668,6 +677,486 @@ function SignIn(props) {
|
|
|
668
677
|
});
|
|
669
678
|
}
|
|
670
679
|
//#endregion
|
|
680
|
+
//#region src/components/_recovery-status.tsx
|
|
681
|
+
const ICONS = {
|
|
682
|
+
mail: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
683
|
+
viewBox: "0 0 24 24",
|
|
684
|
+
fill: "none",
|
|
685
|
+
stroke: "currentColor",
|
|
686
|
+
strokeWidth: "1.75",
|
|
687
|
+
"aria-hidden": "true",
|
|
688
|
+
focusable: "false",
|
|
689
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
690
|
+
x: "3",
|
|
691
|
+
y: "5",
|
|
692
|
+
width: "18",
|
|
693
|
+
height: "14",
|
|
694
|
+
rx: "2.5"
|
|
695
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
696
|
+
d: "m4 7 8 6 8-6",
|
|
697
|
+
strokeLinecap: "round",
|
|
698
|
+
strokeLinejoin: "round"
|
|
699
|
+
})]
|
|
700
|
+
}),
|
|
701
|
+
check: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
702
|
+
viewBox: "0 0 24 24",
|
|
703
|
+
fill: "none",
|
|
704
|
+
stroke: "currentColor",
|
|
705
|
+
strokeWidth: "2",
|
|
706
|
+
"aria-hidden": "true",
|
|
707
|
+
focusable: "false",
|
|
708
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
709
|
+
d: "m5 12.5 4.5 4.5L19 7",
|
|
710
|
+
strokeLinecap: "round",
|
|
711
|
+
strokeLinejoin: "round"
|
|
712
|
+
})
|
|
713
|
+
}),
|
|
714
|
+
warning: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
715
|
+
viewBox: "0 0 24 24",
|
|
716
|
+
fill: "none",
|
|
717
|
+
stroke: "currentColor",
|
|
718
|
+
strokeWidth: "1.75",
|
|
719
|
+
"aria-hidden": "true",
|
|
720
|
+
focusable: "false",
|
|
721
|
+
children: [
|
|
722
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
723
|
+
cx: "12",
|
|
724
|
+
cy: "12",
|
|
725
|
+
r: "9"
|
|
726
|
+
}),
|
|
727
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
728
|
+
d: "M12 7.5v5",
|
|
729
|
+
strokeLinecap: "round"
|
|
730
|
+
}),
|
|
731
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
732
|
+
cx: "12",
|
|
733
|
+
cy: "16",
|
|
734
|
+
r: "0.6",
|
|
735
|
+
fill: "currentColor",
|
|
736
|
+
stroke: "none"
|
|
737
|
+
})
|
|
738
|
+
]
|
|
739
|
+
})
|
|
740
|
+
};
|
|
741
|
+
/**
|
|
742
|
+
* A centered, icon-topped confirmation card — the shape every recovery terminal
|
|
743
|
+
* state shares (link sent, password updated, link expired). Reuses the `<SignIn/>`
|
|
744
|
+
* class anatomy so it themes from the same `--polyx-*` custom properties.
|
|
745
|
+
*/
|
|
746
|
+
function RecoveryStatus(props) {
|
|
747
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
748
|
+
className: "polyx-signin__card",
|
|
749
|
+
children: [
|
|
750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
751
|
+
className: "polyx-signin__header polyx-signin__header--center",
|
|
752
|
+
children: [
|
|
753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
754
|
+
className: `polyx-signin__status-icon polyx-signin__status-icon--${props.tone ?? "brand"}`,
|
|
755
|
+
children: ICONS[props.icon]
|
|
756
|
+
}),
|
|
757
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
758
|
+
className: "polyx-signin__heading",
|
|
759
|
+
children: props.heading
|
|
760
|
+
}),
|
|
761
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
762
|
+
className: "polyx-signin__subheading",
|
|
763
|
+
children: props.children
|
|
764
|
+
})
|
|
765
|
+
]
|
|
766
|
+
}),
|
|
767
|
+
props.actions ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
768
|
+
className: "polyx-signin__actions",
|
|
769
|
+
children: props.actions
|
|
770
|
+
}) : null,
|
|
771
|
+
props.footer
|
|
772
|
+
]
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
776
|
+
//#region src/components/forgot-password.tsx
|
|
777
|
+
const DEFAULT_LABELS$1 = {
|
|
778
|
+
heading: "Reset your password",
|
|
779
|
+
subheading: "Enter your email and we'll send you a reset link.",
|
|
780
|
+
emailLabel: "Email",
|
|
781
|
+
emailPlaceholder: "you@company.com",
|
|
782
|
+
submit: "Send reset link",
|
|
783
|
+
submitting: "Sending…",
|
|
784
|
+
sentHeading: "Check your email",
|
|
785
|
+
sentSubheading: "If an account exists for {email}, a password reset link is on its way. It expires in 30 minutes.",
|
|
786
|
+
differentEmail: "Use a different email",
|
|
787
|
+
backToSignIn: "Back to sign in",
|
|
788
|
+
securedBy: "Secured by PolyX",
|
|
789
|
+
genericError: "Something went wrong. Please try again."
|
|
790
|
+
};
|
|
791
|
+
/** Splits a label on `{email}` and drops the entered address in, emphasized. */
|
|
792
|
+
function withEmail(template, email) {
|
|
793
|
+
return template.split("{email}").map((chunk, index, all) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [chunk, index < all.length - 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
794
|
+
className: "polyx-signin__email",
|
|
795
|
+
children: email || "that address"
|
|
796
|
+
}) : null] }, index));
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* The self-service "forgot password" form (poly-x v5 / F040). Renders an email field
|
|
800
|
+
* inside the consuming app and POSTs to the SDK's BFF (`@poly-x/next` `forgotPassword`),
|
|
801
|
+
* which asks poly-auth to email a single-use reset link SERVER-SIDE. The response is
|
|
802
|
+
* always the same — no token, no account-existence signal — so after submit the form
|
|
803
|
+
* shows an identical "check your email" state whether or not the account exists.
|
|
804
|
+
*
|
|
805
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
806
|
+
* via the `--polyx-*` custom properties. Target `.polyx-forgot` for form-specific tweaks.
|
|
807
|
+
*/
|
|
808
|
+
function ForgotPassword(props) {
|
|
809
|
+
const action = props.action ?? "/api/polyx/forgot-password";
|
|
810
|
+
const signInPath = props.signInPath ?? "/sign-in";
|
|
811
|
+
const labels = {
|
|
812
|
+
...DEFAULT_LABELS$1,
|
|
813
|
+
...props.labels
|
|
814
|
+
};
|
|
815
|
+
const [email, setEmail] = (0, react.useState)("");
|
|
816
|
+
const [submitting, setSubmitting] = (0, react.useState)(false);
|
|
817
|
+
const [sent, setSent] = (0, react.useState)(false);
|
|
818
|
+
const [error, setError] = (0, react.useState)(null);
|
|
819
|
+
async function onSubmit(event) {
|
|
820
|
+
event.preventDefault();
|
|
821
|
+
setSubmitting(true);
|
|
822
|
+
setError(null);
|
|
823
|
+
try {
|
|
824
|
+
if ((await (await fetch(action, {
|
|
825
|
+
method: "POST",
|
|
826
|
+
headers: { "content-type": "application/json" },
|
|
827
|
+
body: JSON.stringify({ email })
|
|
828
|
+
})).json().catch(() => ({}))).status === "accepted") {
|
|
829
|
+
setSent(true);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
setError(labels.genericError);
|
|
833
|
+
} catch {
|
|
834
|
+
setError(labels.genericError);
|
|
835
|
+
} finally {
|
|
836
|
+
setSubmitting(false);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
const rootClass = [
|
|
840
|
+
"polyx-signin",
|
|
841
|
+
"polyx-forgot",
|
|
842
|
+
props.className
|
|
843
|
+
].filter(Boolean).join(" ");
|
|
844
|
+
const footer = props.showSecuredBy === false ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
845
|
+
className: "polyx-signin__footer",
|
|
846
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
847
|
+
className: "polyx-signin__shield",
|
|
848
|
+
viewBox: "0 0 16 16",
|
|
849
|
+
fill: "currentColor",
|
|
850
|
+
"aria-hidden": "true",
|
|
851
|
+
focusable: "false",
|
|
852
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0.5 2 3v4.2c0 3.5 2.5 6.7 6 7.8 3.5-1.1 6-4.3 6-7.8V3L8 .5Zm2.9 5.6-3.4 3.4a.7.7 0 0 1-1 0L5.1 8.1a.7.7 0 1 1 1-1l.9.9 2.9-2.9a.7.7 0 0 1 1 1Z" })
|
|
853
|
+
}), labels.securedBy]
|
|
854
|
+
});
|
|
855
|
+
const header = (heading, subheading) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
856
|
+
className: "polyx-signin__header",
|
|
857
|
+
children: [
|
|
858
|
+
props.logo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
859
|
+
className: "polyx-signin__logo",
|
|
860
|
+
children: props.logo
|
|
861
|
+
}) : null,
|
|
862
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
863
|
+
className: "polyx-signin__heading",
|
|
864
|
+
children: heading
|
|
865
|
+
}),
|
|
866
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
867
|
+
className: "polyx-signin__subheading",
|
|
868
|
+
children: subheading
|
|
869
|
+
})
|
|
870
|
+
]
|
|
871
|
+
});
|
|
872
|
+
if (sent) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
873
|
+
className: rootClass,
|
|
874
|
+
"data-polyx-forgot": "sent",
|
|
875
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RecoveryStatus, {
|
|
876
|
+
icon: "mail",
|
|
877
|
+
heading: labels.sentHeading,
|
|
878
|
+
footer,
|
|
879
|
+
actions: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
880
|
+
type: "button",
|
|
881
|
+
className: "polyx-signin__link",
|
|
882
|
+
onClick: () => setSent(false),
|
|
883
|
+
children: labels.differentEmail
|
|
884
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
885
|
+
className: "polyx-signin__link",
|
|
886
|
+
href: signInPath,
|
|
887
|
+
children: labels.backToSignIn
|
|
888
|
+
})] }),
|
|
889
|
+
children: withEmail(labels.sentSubheading, email)
|
|
890
|
+
})
|
|
891
|
+
});
|
|
892
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
893
|
+
className: rootClass,
|
|
894
|
+
"data-polyx-forgot": "request",
|
|
895
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
896
|
+
className: "polyx-signin__card",
|
|
897
|
+
children: [
|
|
898
|
+
header(labels.heading, labels.subheading),
|
|
899
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
900
|
+
className: "polyx-signin__form",
|
|
901
|
+
onSubmit,
|
|
902
|
+
children: [
|
|
903
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
904
|
+
className: "polyx-signin__field",
|
|
905
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
906
|
+
className: "polyx-signin__label",
|
|
907
|
+
children: labels.emailLabel
|
|
908
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
909
|
+
className: "polyx-signin__input",
|
|
910
|
+
type: "email",
|
|
911
|
+
name: "email",
|
|
912
|
+
autoComplete: "username",
|
|
913
|
+
placeholder: labels.emailPlaceholder,
|
|
914
|
+
required: true,
|
|
915
|
+
value: email,
|
|
916
|
+
disabled: submitting,
|
|
917
|
+
onChange: (event) => setEmail(event.target.value)
|
|
918
|
+
})]
|
|
919
|
+
}),
|
|
920
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
921
|
+
role: "alert",
|
|
922
|
+
className: "polyx-signin__error",
|
|
923
|
+
children: error
|
|
924
|
+
}) : null,
|
|
925
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
926
|
+
type: "submit",
|
|
927
|
+
className: "polyx-signin__submit",
|
|
928
|
+
disabled: submitting,
|
|
929
|
+
children: submitting ? labels.submitting : labels.submit
|
|
930
|
+
})
|
|
931
|
+
]
|
|
932
|
+
}),
|
|
933
|
+
footer
|
|
934
|
+
]
|
|
935
|
+
})
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
//#endregion
|
|
939
|
+
//#region src/components/reset-password.tsx
|
|
940
|
+
const DEFAULT_LABELS = {
|
|
941
|
+
heading: "Choose a new password",
|
|
942
|
+
subheading: "Enter and confirm your new password.",
|
|
943
|
+
newPasswordLabel: "New password",
|
|
944
|
+
confirmPasswordLabel: "Confirm new password",
|
|
945
|
+
passwordPlaceholder: "••••••••",
|
|
946
|
+
submit: "Reset password",
|
|
947
|
+
submitting: "Resetting…",
|
|
948
|
+
successHeading: "Password updated",
|
|
949
|
+
successSubheading: "You can now sign in with your new password.",
|
|
950
|
+
invalidHeading: "This link is invalid or expired",
|
|
951
|
+
invalidSubheading: "Password reset links can be used once and expire quickly. Request a new one.",
|
|
952
|
+
requestNewLink: "Request a new link",
|
|
953
|
+
backToSignIn: "Back to sign in",
|
|
954
|
+
securedBy: "Secured by PolyX",
|
|
955
|
+
passwordMismatch: "Those passwords don't match.",
|
|
956
|
+
weakPassword: "Password must be at least 8 characters.",
|
|
957
|
+
genericError: "Something went wrong. Please try again."
|
|
958
|
+
};
|
|
959
|
+
/** Matches poly-auth's v5 reset floor (F040). */
|
|
960
|
+
const DEFAULT_MIN_LENGTH = 8;
|
|
961
|
+
/**
|
|
962
|
+
* The reset-completion form (poly-x v5 / F040). Reads the opaque token from the URL,
|
|
963
|
+
* collects a new password, and POSTs to the SDK's BFF (`@poly-x/next` `resetPassword`),
|
|
964
|
+
* which redeems the token single-use server-side. The token is never decoded in the
|
|
965
|
+
* browser — identity, validity, and expiry are all enforced by the backend.
|
|
966
|
+
*
|
|
967
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
968
|
+
* via the `--polyx-*` custom properties. Target `.polyx-reset` for form-specific tweaks.
|
|
969
|
+
*/
|
|
970
|
+
function ResetPassword(props) {
|
|
971
|
+
const action = props.action ?? "/api/polyx/reset-password";
|
|
972
|
+
const minLength = props.minLength ?? DEFAULT_MIN_LENGTH;
|
|
973
|
+
const signInPath = props.signInPath ?? "/sign-in";
|
|
974
|
+
const forgotPasswordPath = props.forgotPasswordPath ?? "/forgot-password";
|
|
975
|
+
const labels = {
|
|
976
|
+
...DEFAULT_LABELS,
|
|
977
|
+
...props.labels
|
|
978
|
+
};
|
|
979
|
+
const [token] = (0, react.useState)(() => props.token ?? (typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("token") ?? "" : ""));
|
|
980
|
+
const [newPassword, setNewPassword] = (0, react.useState)("");
|
|
981
|
+
const [confirmPassword, setConfirmPassword] = (0, react.useState)("");
|
|
982
|
+
const [submitting, setSubmitting] = (0, react.useState)(false);
|
|
983
|
+
const [error, setError] = (0, react.useState)(null);
|
|
984
|
+
const [step, setStep] = (0, react.useState)(token ? "form" : "invalid");
|
|
985
|
+
async function onSubmit(event) {
|
|
986
|
+
event.preventDefault();
|
|
987
|
+
if (newPassword !== confirmPassword) {
|
|
988
|
+
setError(labels.passwordMismatch);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
setSubmitting(true);
|
|
992
|
+
setError(null);
|
|
993
|
+
try {
|
|
994
|
+
switch ((await (await fetch(action, {
|
|
995
|
+
method: "POST",
|
|
996
|
+
headers: { "content-type": "application/json" },
|
|
997
|
+
body: JSON.stringify({
|
|
998
|
+
token,
|
|
999
|
+
newPassword,
|
|
1000
|
+
confirmPassword
|
|
1001
|
+
})
|
|
1002
|
+
})).json().catch(() => ({}))).status) {
|
|
1003
|
+
case "success":
|
|
1004
|
+
setStep("success");
|
|
1005
|
+
if (props.afterResetPath) window.location.assign(props.afterResetPath);
|
|
1006
|
+
return;
|
|
1007
|
+
case "weak_password":
|
|
1008
|
+
setError(labels.weakPassword);
|
|
1009
|
+
return;
|
|
1010
|
+
case "password_mismatch":
|
|
1011
|
+
setError(labels.passwordMismatch);
|
|
1012
|
+
return;
|
|
1013
|
+
case "invalid_or_expired":
|
|
1014
|
+
setStep("invalid");
|
|
1015
|
+
return;
|
|
1016
|
+
default:
|
|
1017
|
+
setError(labels.genericError);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
} catch {
|
|
1021
|
+
setError(labels.genericError);
|
|
1022
|
+
} finally {
|
|
1023
|
+
setSubmitting(false);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
const rootClass = [
|
|
1027
|
+
"polyx-signin",
|
|
1028
|
+
"polyx-reset",
|
|
1029
|
+
props.className
|
|
1030
|
+
].filter(Boolean).join(" ");
|
|
1031
|
+
const footer = props.showSecuredBy === false ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1032
|
+
className: "polyx-signin__footer",
|
|
1033
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1034
|
+
className: "polyx-signin__shield",
|
|
1035
|
+
viewBox: "0 0 16 16",
|
|
1036
|
+
fill: "currentColor",
|
|
1037
|
+
"aria-hidden": "true",
|
|
1038
|
+
focusable: "false",
|
|
1039
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0.5 2 3v4.2c0 3.5 2.5 6.7 6 7.8 3.5-1.1 6-4.3 6-7.8V3L8 .5Zm2.9 5.6-3.4 3.4a.7.7 0 0 1-1 0L5.1 8.1a.7.7 0 1 1 1-1l.9.9 2.9-2.9a.7.7 0 0 1 1 1Z" })
|
|
1040
|
+
}), labels.securedBy]
|
|
1041
|
+
});
|
|
1042
|
+
const header = (heading, subheading) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1043
|
+
className: "polyx-signin__header",
|
|
1044
|
+
children: [
|
|
1045
|
+
props.logo ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1046
|
+
className: "polyx-signin__logo",
|
|
1047
|
+
children: props.logo
|
|
1048
|
+
}) : null,
|
|
1049
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
1050
|
+
className: "polyx-signin__heading",
|
|
1051
|
+
children: heading
|
|
1052
|
+
}),
|
|
1053
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1054
|
+
className: "polyx-signin__subheading",
|
|
1055
|
+
children: subheading
|
|
1056
|
+
})
|
|
1057
|
+
]
|
|
1058
|
+
});
|
|
1059
|
+
if (step === "success") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1060
|
+
className: rootClass,
|
|
1061
|
+
"data-polyx-reset": "success",
|
|
1062
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RecoveryStatus, {
|
|
1063
|
+
icon: "check",
|
|
1064
|
+
heading: labels.successHeading,
|
|
1065
|
+
footer,
|
|
1066
|
+
actions: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1067
|
+
className: "polyx-signin__link",
|
|
1068
|
+
href: signInPath,
|
|
1069
|
+
children: labels.backToSignIn
|
|
1070
|
+
}),
|
|
1071
|
+
children: labels.successSubheading
|
|
1072
|
+
})
|
|
1073
|
+
});
|
|
1074
|
+
if (step === "invalid") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1075
|
+
className: rootClass,
|
|
1076
|
+
"data-polyx-reset": "invalid",
|
|
1077
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RecoveryStatus, {
|
|
1078
|
+
icon: "warning",
|
|
1079
|
+
tone: "danger",
|
|
1080
|
+
heading: labels.invalidHeading,
|
|
1081
|
+
footer,
|
|
1082
|
+
actions: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1083
|
+
className: "polyx-signin__link",
|
|
1084
|
+
href: forgotPasswordPath,
|
|
1085
|
+
children: labels.requestNewLink
|
|
1086
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1087
|
+
className: "polyx-signin__link",
|
|
1088
|
+
href: signInPath,
|
|
1089
|
+
children: labels.backToSignIn
|
|
1090
|
+
})] }),
|
|
1091
|
+
children: labels.invalidSubheading
|
|
1092
|
+
})
|
|
1093
|
+
});
|
|
1094
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1095
|
+
className: rootClass,
|
|
1096
|
+
"data-polyx-reset": step,
|
|
1097
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1098
|
+
className: "polyx-signin__card",
|
|
1099
|
+
children: [
|
|
1100
|
+
header(labels.heading, labels.subheading),
|
|
1101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
1102
|
+
className: "polyx-signin__form",
|
|
1103
|
+
onSubmit,
|
|
1104
|
+
children: [
|
|
1105
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1106
|
+
className: "polyx-signin__field",
|
|
1107
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1108
|
+
className: "polyx-signin__label",
|
|
1109
|
+
children: labels.newPasswordLabel
|
|
1110
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1111
|
+
className: "polyx-signin__input",
|
|
1112
|
+
type: "password",
|
|
1113
|
+
name: "newPassword",
|
|
1114
|
+
autoComplete: "new-password",
|
|
1115
|
+
placeholder: labels.passwordPlaceholder,
|
|
1116
|
+
required: true,
|
|
1117
|
+
minLength,
|
|
1118
|
+
value: newPassword,
|
|
1119
|
+
disabled: submitting,
|
|
1120
|
+
onChange: (event) => setNewPassword(event.target.value)
|
|
1121
|
+
})]
|
|
1122
|
+
}),
|
|
1123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1124
|
+
className: "polyx-signin__field",
|
|
1125
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1126
|
+
className: "polyx-signin__label",
|
|
1127
|
+
children: labels.confirmPasswordLabel
|
|
1128
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1129
|
+
className: "polyx-signin__input",
|
|
1130
|
+
type: "password",
|
|
1131
|
+
name: "confirmPassword",
|
|
1132
|
+
autoComplete: "new-password",
|
|
1133
|
+
placeholder: labels.passwordPlaceholder,
|
|
1134
|
+
required: true,
|
|
1135
|
+
minLength,
|
|
1136
|
+
value: confirmPassword,
|
|
1137
|
+
disabled: submitting,
|
|
1138
|
+
onChange: (event) => setConfirmPassword(event.target.value)
|
|
1139
|
+
})]
|
|
1140
|
+
}),
|
|
1141
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1142
|
+
role: "alert",
|
|
1143
|
+
className: "polyx-signin__error",
|
|
1144
|
+
children: error
|
|
1145
|
+
}) : null,
|
|
1146
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1147
|
+
type: "submit",
|
|
1148
|
+
className: "polyx-signin__submit",
|
|
1149
|
+
disabled: submitting,
|
|
1150
|
+
children: submitting ? labels.submitting : labels.submit
|
|
1151
|
+
})
|
|
1152
|
+
]
|
|
1153
|
+
}),
|
|
1154
|
+
footer
|
|
1155
|
+
]
|
|
1156
|
+
})
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
//#endregion
|
|
671
1160
|
//#region src/components/auth-callback.tsx
|
|
672
1161
|
/** Drop on your `/auth/callback` route: completes the sign-in exchange on mount. */
|
|
673
1162
|
function AuthCallback({ children = null }) {
|
|
@@ -689,11 +1178,13 @@ const version = "0.0.0";
|
|
|
689
1178
|
exports.AuthCallback = AuthCallback;
|
|
690
1179
|
exports.BroadcastAuthChannel = BroadcastAuthChannel;
|
|
691
1180
|
exports.BroadcastChannelSessionChannel = BroadcastChannelSessionChannel;
|
|
1181
|
+
exports.ForgotPassword = ForgotPassword;
|
|
692
1182
|
exports.PACKAGE_NAME = PACKAGE_NAME;
|
|
693
1183
|
exports.PolyXContext = PolyXContext;
|
|
694
1184
|
exports.PolyXProvider = PolyXProvider;
|
|
695
1185
|
exports.PopupCancelledError = PopupCancelledError;
|
|
696
1186
|
exports.Protected = Protected;
|
|
1187
|
+
exports.ResetPassword = ResetPassword;
|
|
697
1188
|
exports.SessionStoragePkceStore = SessionStoragePkceStore;
|
|
698
1189
|
exports.SignIn = SignIn;
|
|
699
1190
|
exports.WebLocksLock = WebLocksLock;
|
package/dist/index.d.cts
CHANGED
|
@@ -203,6 +203,7 @@ interface SignInLabels {
|
|
|
203
203
|
passwordPlaceholder: string;
|
|
204
204
|
submit: string;
|
|
205
205
|
submitting: string;
|
|
206
|
+
forgotPassword: string;
|
|
206
207
|
chooseWorkspace: string;
|
|
207
208
|
chooseWorkspaceSubheading: string;
|
|
208
209
|
securedBy: string;
|
|
@@ -235,6 +236,11 @@ interface SignInProps {
|
|
|
235
236
|
panel?: ReactNode;
|
|
236
237
|
/** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
|
|
237
238
|
action?: string;
|
|
239
|
+
/**
|
|
240
|
+
* URL of your reset flow (where `<ForgotPassword/>` lives). When set, a
|
|
241
|
+
* "Forgot password?" link appears under the password field. Omit to hide it.
|
|
242
|
+
*/
|
|
243
|
+
forgotPasswordUrl?: string;
|
|
238
244
|
/** Where to send the browser after success. Overrides the server's default redirect. */
|
|
239
245
|
afterSignInPath?: string;
|
|
240
246
|
/** Pre-scope the login to a single organization (skips org discovery). */
|
|
@@ -261,6 +267,101 @@ interface SignInProps {
|
|
|
261
267
|
*/
|
|
262
268
|
declare function SignIn(props: SignInProps): ReactNode;
|
|
263
269
|
//#endregion
|
|
270
|
+
//#region src/components/forgot-password.d.ts
|
|
271
|
+
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
272
|
+
interface ForgotPasswordLabels {
|
|
273
|
+
heading: string;
|
|
274
|
+
subheading: string;
|
|
275
|
+
emailLabel: string;
|
|
276
|
+
emailPlaceholder: string;
|
|
277
|
+
submit: string;
|
|
278
|
+
submitting: string;
|
|
279
|
+
/** Shown after submit — always, regardless of whether the account exists. */
|
|
280
|
+
sentHeading: string;
|
|
281
|
+
/** Confirmation body. `{email}` is replaced with the entered address, emphasized. */
|
|
282
|
+
sentSubheading: string;
|
|
283
|
+
differentEmail: string;
|
|
284
|
+
backToSignIn: string;
|
|
285
|
+
securedBy: string;
|
|
286
|
+
genericError: string;
|
|
287
|
+
}
|
|
288
|
+
interface ForgotPasswordProps {
|
|
289
|
+
/** BFF endpoint backed by `createAuthHandlers().forgotPassword`. Default `/api/polyx/forgot-password`. */
|
|
290
|
+
action?: string;
|
|
291
|
+
/** Where the "back to sign in" link points. Default `/sign-in`. */
|
|
292
|
+
signInPath?: string;
|
|
293
|
+
/** Rendered above the heading — your wordmark or logo. */
|
|
294
|
+
logo?: ReactNode;
|
|
295
|
+
/** Show the "Secured by PolyX" attribution under the form. Default true. */
|
|
296
|
+
showSecuredBy?: boolean;
|
|
297
|
+
/** Appended to the root element's class list, for escape-hatch styling. */
|
|
298
|
+
className?: string;
|
|
299
|
+
labels?: Partial<ForgotPasswordLabels>;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The self-service "forgot password" form (poly-x v5 / F040). Renders an email field
|
|
303
|
+
* inside the consuming app and POSTs to the SDK's BFF (`@poly-x/next` `forgotPassword`),
|
|
304
|
+
* which asks poly-auth to email a single-use reset link SERVER-SIDE. The response is
|
|
305
|
+
* always the same — no token, no account-existence signal — so after submit the form
|
|
306
|
+
* shows an identical "check your email" state whether or not the account exists.
|
|
307
|
+
*
|
|
308
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
309
|
+
* via the `--polyx-*` custom properties. Target `.polyx-forgot` for form-specific tweaks.
|
|
310
|
+
*/
|
|
311
|
+
declare function ForgotPassword(props: ForgotPasswordProps): ReactNode;
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/components/reset-password.d.ts
|
|
314
|
+
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
315
|
+
interface ResetPasswordLabels {
|
|
316
|
+
heading: string;
|
|
317
|
+
subheading: string;
|
|
318
|
+
newPasswordLabel: string;
|
|
319
|
+
confirmPasswordLabel: string;
|
|
320
|
+
passwordPlaceholder: string;
|
|
321
|
+
submit: string;
|
|
322
|
+
submitting: string;
|
|
323
|
+
successHeading: string;
|
|
324
|
+
successSubheading: string;
|
|
325
|
+
invalidHeading: string;
|
|
326
|
+
invalidSubheading: string;
|
|
327
|
+
requestNewLink: string;
|
|
328
|
+
backToSignIn: string;
|
|
329
|
+
securedBy: string;
|
|
330
|
+
passwordMismatch: string;
|
|
331
|
+
weakPassword: string;
|
|
332
|
+
genericError: string;
|
|
333
|
+
}
|
|
334
|
+
interface ResetPasswordProps {
|
|
335
|
+
/** BFF endpoint backed by `createAuthHandlers().resetPassword`. Default `/api/polyx/reset-password`. */
|
|
336
|
+
action?: string;
|
|
337
|
+
/** The opaque reset token. Defaults to `?token=` from the URL (client-side). */
|
|
338
|
+
token?: string;
|
|
339
|
+
/** Where to send the browser after a successful reset. Default `/sign-in`. */
|
|
340
|
+
afterResetPath?: string;
|
|
341
|
+
/** Where "request a new link" / "back to sign in" point. Default `/forgot-password` and `/sign-in`. */
|
|
342
|
+
forgotPasswordPath?: string;
|
|
343
|
+
signInPath?: string;
|
|
344
|
+
/** Rendered above the heading — your wordmark or logo. */
|
|
345
|
+
logo?: ReactNode;
|
|
346
|
+
/** Show the "Secured by PolyX" attribution under the form. Default true. */
|
|
347
|
+
showSecuredBy?: boolean;
|
|
348
|
+
/** Client-side minimum password length (the backend enforces the real floor). Default 8. */
|
|
349
|
+
minLength?: number;
|
|
350
|
+
/** Appended to the root element's class list, for escape-hatch styling. */
|
|
351
|
+
className?: string;
|
|
352
|
+
labels?: Partial<ResetPasswordLabels>;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The reset-completion form (poly-x v5 / F040). Reads the opaque token from the URL,
|
|
356
|
+
* collects a new password, and POSTs to the SDK's BFF (`@poly-x/next` `resetPassword`),
|
|
357
|
+
* which redeems the token single-use server-side. The token is never decoded in the
|
|
358
|
+
* browser — identity, validity, and expiry are all enforced by the backend.
|
|
359
|
+
*
|
|
360
|
+
* Reuses the `<SignIn/>` styling: `import "@poly-x/react/styles.css"` once, then theme
|
|
361
|
+
* via the `--polyx-*` custom properties. Target `.polyx-reset` for form-specific tweaks.
|
|
362
|
+
*/
|
|
363
|
+
declare function ResetPassword(props: ResetPasswordProps): ReactNode;
|
|
364
|
+
//#endregion
|
|
264
365
|
//#region src/components/auth-callback.d.ts
|
|
265
366
|
interface AuthCallbackProps {
|
|
266
367
|
/** Rendered while the exchange completes (e.g. a spinner). */
|
|
@@ -279,4 +380,4 @@ declare function AuthCallback({
|
|
|
279
380
|
declare const PACKAGE_NAME = "@poly-x/react";
|
|
280
381
|
declare const version = "0.0.0";
|
|
281
382
|
//#endregion
|
|
282
|
-
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type SignInVariant, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
383
|
+
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, ForgotPassword, type ForgotPasswordLabels, type ForgotPasswordProps, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, ResetPassword, type ResetPasswordLabels, type ResetPasswordProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type SignInVariant, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|