@poly-x/react 0.1.0 → 0.2.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/README.md +33 -2
- package/dist/index.cjs +646 -81
- package/dist/index.d.cts +107 -2
- package/dist/index.d.mts +107 -2
- package/dist/index.mjs +646 -83
- package/dist/styles.css +151 -0
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { AuthClient, FetchTransport, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, createSessionEngine, generatePkce, randomState, resolveConfig } from "@poly-x/core";
|
|
2
|
+
import { AuthClient, FetchTransport, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, PROFILE_FIELDS, buildProfileUpdate, conflictField, createSessionEngine, generatePkce, randomState, resolveConfig, validateProfileDraft, validateProfileImage } from "@poly-x/core";
|
|
3
3
|
import { Children, Fragment, createContext, isValidElement, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
5
6
|
//#region src/auth-channel.ts
|
|
6
7
|
var BroadcastAuthChannel = class {
|
|
7
8
|
supported;
|
|
@@ -498,6 +499,14 @@ function useSessionSource() {
|
|
|
498
499
|
} catch {
|
|
499
500
|
return null;
|
|
500
501
|
}
|
|
502
|
+
},
|
|
503
|
+
refresh: async () => {
|
|
504
|
+
try {
|
|
505
|
+
await engine.getAccessToken();
|
|
506
|
+
return true;
|
|
507
|
+
} catch {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
501
510
|
}
|
|
502
511
|
},
|
|
503
512
|
canSignIn: Boolean(controller)
|
|
@@ -518,12 +527,14 @@ function useAuth() {
|
|
|
518
527
|
}, [ctx]);
|
|
519
528
|
const signOut = useCallback((options) => source.signOut(options), [source]);
|
|
520
529
|
const getToken = useCallback(() => source.getToken(), [source]);
|
|
530
|
+
const refresh = useCallback(() => source.refresh(), [source]);
|
|
521
531
|
return {
|
|
522
532
|
isLoaded: state.status !== "resolving",
|
|
523
533
|
isSignedIn: state.status === "authenticated" || state.status === "refreshing",
|
|
524
534
|
signIn,
|
|
525
535
|
signOut,
|
|
526
|
-
getToken
|
|
536
|
+
getToken,
|
|
537
|
+
refresh
|
|
527
538
|
};
|
|
528
539
|
}
|
|
529
540
|
/** Run this on your callback route to complete a sign-in (see `<AuthCallback>`). */
|
|
@@ -629,7 +640,7 @@ function captureDeviceContext() {
|
|
|
629
640
|
}
|
|
630
641
|
//#endregion
|
|
631
642
|
//#region src/components/sign-in.tsx
|
|
632
|
-
const DEFAULT_LABELS$
|
|
643
|
+
const DEFAULT_LABELS$4 = {
|
|
633
644
|
heading: "Sign in",
|
|
634
645
|
subheading: "Enter your credentials to continue.",
|
|
635
646
|
tagline: "",
|
|
@@ -655,7 +666,8 @@ const DEFAULT_LABELS$3 = {
|
|
|
655
666
|
invalidCredentials: "Invalid email or password.",
|
|
656
667
|
noAccess: "You don't have access to this application.",
|
|
657
668
|
passwordChangeRequired: "You must reset your password before you can sign in.",
|
|
658
|
-
genericError: "Something went wrong. Please try again."
|
|
669
|
+
genericError: "Something went wrong. Please try again.",
|
|
670
|
+
verificationRequired: "Additional verification is required to sign in."
|
|
659
671
|
};
|
|
660
672
|
/** Mirrors poly-auth's own floor (Joi: min 6). */
|
|
661
673
|
const MIN_PASSWORD_LENGTH = 6;
|
|
@@ -744,7 +756,7 @@ function SignIn(props) {
|
|
|
744
756
|
const action = props.action ?? "/api/polyx/authenticate";
|
|
745
757
|
const variant = props.variant ?? "card";
|
|
746
758
|
const labels = {
|
|
747
|
-
...DEFAULT_LABELS$
|
|
759
|
+
...DEFAULT_LABELS$4,
|
|
748
760
|
...props.labels
|
|
749
761
|
};
|
|
750
762
|
const [email, setEmail] = useState("");
|
|
@@ -825,6 +837,9 @@ function SignIn(props) {
|
|
|
825
837
|
case "no_access":
|
|
826
838
|
setError(labels.noAccess);
|
|
827
839
|
return;
|
|
840
|
+
case "verification_required":
|
|
841
|
+
setError(labels.verificationRequired ?? labels.genericError);
|
|
842
|
+
return;
|
|
828
843
|
default:
|
|
829
844
|
setError(labels.genericError);
|
|
830
845
|
return;
|
|
@@ -1137,7 +1152,7 @@ function RecoveryStatus(props) {
|
|
|
1137
1152
|
}
|
|
1138
1153
|
//#endregion
|
|
1139
1154
|
//#region src/components/forgot-password.tsx
|
|
1140
|
-
const DEFAULT_LABELS$
|
|
1155
|
+
const DEFAULT_LABELS$3 = {
|
|
1141
1156
|
heading: "Reset your password",
|
|
1142
1157
|
subheading: "Enter your email and we'll send you a reset link.",
|
|
1143
1158
|
emailLabel: "Email",
|
|
@@ -1172,7 +1187,7 @@ function ForgotPassword(props) {
|
|
|
1172
1187
|
const action = props.action ?? "/api/polyx/forgot-password";
|
|
1173
1188
|
const signInPath = props.signInPath ?? "/sign-in";
|
|
1174
1189
|
const labels = {
|
|
1175
|
-
...DEFAULT_LABELS$
|
|
1190
|
+
...DEFAULT_LABELS$3,
|
|
1176
1191
|
...props.labels
|
|
1177
1192
|
};
|
|
1178
1193
|
const [email, setEmail] = useState("");
|
|
@@ -1300,7 +1315,7 @@ function ForgotPassword(props) {
|
|
|
1300
1315
|
}
|
|
1301
1316
|
//#endregion
|
|
1302
1317
|
//#region src/components/reset-password.tsx
|
|
1303
|
-
const DEFAULT_LABELS$
|
|
1318
|
+
const DEFAULT_LABELS$2 = {
|
|
1304
1319
|
heading: "Choose a new password",
|
|
1305
1320
|
subheading: "Enter and confirm your new password.",
|
|
1306
1321
|
newPasswordLabel: "New password",
|
|
@@ -1336,7 +1351,7 @@ function ResetPassword(props) {
|
|
|
1336
1351
|
const signInPath = props.signInPath ?? "/sign-in";
|
|
1337
1352
|
const forgotPasswordPath = props.forgotPasswordPath ?? "/forgot-password";
|
|
1338
1353
|
const labels = {
|
|
1339
|
-
...DEFAULT_LABELS$
|
|
1354
|
+
...DEFAULT_LABELS$2,
|
|
1340
1355
|
...props.labels
|
|
1341
1356
|
};
|
|
1342
1357
|
const [token] = useState(() => props.token ?? (typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("token") ?? "" : ""));
|
|
@@ -1619,6 +1634,511 @@ function UserAvatar({ src, name, size = 32, className }) {
|
|
|
1619
1634
|
});
|
|
1620
1635
|
}
|
|
1621
1636
|
//#endregion
|
|
1637
|
+
//#region src/components/user-profile.tsx
|
|
1638
|
+
const PROFILE_ENDPOINT = "/api/polyx/profile";
|
|
1639
|
+
const DEFAULT_LABELS$1 = {
|
|
1640
|
+
trigger: "Manage profile",
|
|
1641
|
+
title: "Your profile",
|
|
1642
|
+
description: "Update your personal details.",
|
|
1643
|
+
firstName: "First name",
|
|
1644
|
+
lastName: "Last name",
|
|
1645
|
+
phoneNumber: "Phone number",
|
|
1646
|
+
cnic: "National ID",
|
|
1647
|
+
gender: "Gender",
|
|
1648
|
+
photo: "Photo",
|
|
1649
|
+
changePhoto: "Change photo",
|
|
1650
|
+
optional: "optional",
|
|
1651
|
+
save: "Save changes",
|
|
1652
|
+
saving: "Saving…",
|
|
1653
|
+
cancel: "Cancel",
|
|
1654
|
+
close: "Close",
|
|
1655
|
+
loading: "Loading your profile…",
|
|
1656
|
+
loadFailed: "We couldn't load your profile. Close this and try again.",
|
|
1657
|
+
saveFailed: "We couldn't save your changes. Your edits are still here — try again.",
|
|
1658
|
+
saved: "Profile updated.",
|
|
1659
|
+
signedOut: "Your session ended. Sign in again to make changes.",
|
|
1660
|
+
genderUnset: "Prefer not to say",
|
|
1661
|
+
genderMale: "Male",
|
|
1662
|
+
genderFemale: "Female",
|
|
1663
|
+
genderOther: "Other"
|
|
1664
|
+
};
|
|
1665
|
+
/** Matches the console's options, so the same stored values round-trip. */
|
|
1666
|
+
const GENDERS = [
|
|
1667
|
+
"male",
|
|
1668
|
+
"female",
|
|
1669
|
+
"other"
|
|
1670
|
+
];
|
|
1671
|
+
const EMPTY = {
|
|
1672
|
+
firstName: "",
|
|
1673
|
+
lastName: "",
|
|
1674
|
+
phoneNumber: "",
|
|
1675
|
+
cnic: "",
|
|
1676
|
+
gender: ""
|
|
1677
|
+
};
|
|
1678
|
+
function readProfile(user) {
|
|
1679
|
+
const draft = { ...EMPTY };
|
|
1680
|
+
for (const field of PROFILE_FIELDS) {
|
|
1681
|
+
const value = user[field];
|
|
1682
|
+
if (typeof value === "string") draft[field] = value;
|
|
1683
|
+
}
|
|
1684
|
+
return draft;
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* The signed-in person's own profile, as a dialog (v0.4 / FR-PROF).
|
|
1688
|
+
*
|
|
1689
|
+
* Every application that wanted this had to build the same four things: a form, a validation
|
|
1690
|
+
* ruleset guessed at from error messages, a file upload, and — because the browser holds no
|
|
1691
|
+
* credential under BFF custody — a server route to carry the call. The last of those is
|
|
1692
|
+
* security-adjacent work that had no business being duplicated per app.
|
|
1693
|
+
*
|
|
1694
|
+
* The credential never comes near this component: it posts to the SDK's own route, which
|
|
1695
|
+
* attaches the session server-side and takes the identity from the sealed session rather than
|
|
1696
|
+
* from anything sent here. This surface cannot be aimed at another person's profile even if a
|
|
1697
|
+
* caller tries (FR-CUSTODY-4 / D24).
|
|
1698
|
+
*
|
|
1699
|
+
* ```tsx
|
|
1700
|
+
* <UserProfile onSaved={() => router.refresh()} />
|
|
1701
|
+
* ```
|
|
1702
|
+
*/
|
|
1703
|
+
function UserProfile(props) {
|
|
1704
|
+
const labels = {
|
|
1705
|
+
...DEFAULT_LABELS$1,
|
|
1706
|
+
...props.labels
|
|
1707
|
+
};
|
|
1708
|
+
const { isLoaded, isSignedIn, refresh } = useAuth();
|
|
1709
|
+
const controlled = props.open !== void 0;
|
|
1710
|
+
const [internalOpen, setInternalOpen] = useState(props.defaultOpen ?? false);
|
|
1711
|
+
const open = controlled ? props.open === true : internalOpen;
|
|
1712
|
+
const [status, setStatus] = useState("loading");
|
|
1713
|
+
const [stored, setStored] = useState(EMPTY);
|
|
1714
|
+
const [draft, setDraft] = useState(EMPTY);
|
|
1715
|
+
const [errors, setErrors] = useState({});
|
|
1716
|
+
const [photo, setPhoto] = useState(null);
|
|
1717
|
+
const [photoError, setPhotoError] = useState(null);
|
|
1718
|
+
const [storedPhoto, setStoredPhoto] = useState(void 0);
|
|
1719
|
+
const [saving, setSaving] = useState(false);
|
|
1720
|
+
const [feedback, setFeedback] = useState(null);
|
|
1721
|
+
const [portalHost, setPortalHost] = useState(null);
|
|
1722
|
+
useEffect(() => {
|
|
1723
|
+
const host = document.createElement("div");
|
|
1724
|
+
host.setAttribute("data-polyx-userprofile-portal", "");
|
|
1725
|
+
document.body.appendChild(host);
|
|
1726
|
+
setPortalHost(host);
|
|
1727
|
+
return () => host.remove();
|
|
1728
|
+
}, []);
|
|
1729
|
+
const triggerRef = useRef(null);
|
|
1730
|
+
const dialogRef = useRef(null);
|
|
1731
|
+
const fileRef = useRef(null);
|
|
1732
|
+
const titleId = useId();
|
|
1733
|
+
const fieldId = useId();
|
|
1734
|
+
const onOpenChange = props.onOpenChange;
|
|
1735
|
+
const setOpen = useCallback((next) => {
|
|
1736
|
+
if (!controlled) setInternalOpen(next);
|
|
1737
|
+
onOpenChange?.(next);
|
|
1738
|
+
}, [controlled, onOpenChange]);
|
|
1739
|
+
const polyx = useContext(PolyXContext);
|
|
1740
|
+
const [branding, setBranding] = useState({});
|
|
1741
|
+
useEffect(() => {
|
|
1742
|
+
if (!polyx || !open) return;
|
|
1743
|
+
let active = true;
|
|
1744
|
+
polyx.authClient.fetchBranding({}).then((tokens) => {
|
|
1745
|
+
if (active) setBranding(tokens);
|
|
1746
|
+
});
|
|
1747
|
+
return () => {
|
|
1748
|
+
active = false;
|
|
1749
|
+
};
|
|
1750
|
+
}, [polyx, open]);
|
|
1751
|
+
const brandingStyle = {};
|
|
1752
|
+
if (branding.primaryColor) brandingStyle["--polyx-brand"] = branding.primaryColor;
|
|
1753
|
+
/** Load on open. Never leaves blank fields looking like a stored (empty) profile — EC-PROF-1. */
|
|
1754
|
+
useEffect(() => {
|
|
1755
|
+
if (!open) return;
|
|
1756
|
+
let active = true;
|
|
1757
|
+
setStatus("loading");
|
|
1758
|
+
setFeedback(null);
|
|
1759
|
+
(async () => {
|
|
1760
|
+
try {
|
|
1761
|
+
const response = await fetch(PROFILE_ENDPOINT, { headers: { Accept: "application/json" } });
|
|
1762
|
+
const body = await response.json().catch(() => ({}));
|
|
1763
|
+
if (!active) return;
|
|
1764
|
+
if (!response.ok || !body.user) {
|
|
1765
|
+
setStatus("load_failed");
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
const loaded = readProfile(body.user);
|
|
1769
|
+
setStored(loaded);
|
|
1770
|
+
setDraft(loaded);
|
|
1771
|
+
setStoredPhoto(typeof body.user.profilePicture === "string" ? body.user.profilePicture : void 0);
|
|
1772
|
+
setErrors({});
|
|
1773
|
+
setStatus("ready");
|
|
1774
|
+
} catch {
|
|
1775
|
+
if (active) setStatus("load_failed");
|
|
1776
|
+
}
|
|
1777
|
+
})();
|
|
1778
|
+
return () => {
|
|
1779
|
+
active = false;
|
|
1780
|
+
};
|
|
1781
|
+
}, [open]);
|
|
1782
|
+
useEffect(() => {
|
|
1783
|
+
const preview = photo?.preview;
|
|
1784
|
+
return () => {
|
|
1785
|
+
if (preview) URL.revokeObjectURL(preview);
|
|
1786
|
+
};
|
|
1787
|
+
}, [photo]);
|
|
1788
|
+
/**
|
|
1789
|
+
* Hide the rest of the page from assistive technology while the dialog is open.
|
|
1790
|
+
*
|
|
1791
|
+
* `aria-modal="true"` alone is not enough — screen-reader support for it is inconsistent, so a
|
|
1792
|
+
* virtual cursor can still read and act on everything behind the dialog. The keyboard focus trap
|
|
1793
|
+
* does not cover this either: it constrains Tab, not the virtual cursor. Without both, the dialog
|
|
1794
|
+
* announces itself as modal and then does not behave modally.
|
|
1795
|
+
*
|
|
1796
|
+
* Every direct child of `<body>` except the dialog's own portal is marked, and only attributes
|
|
1797
|
+
* this effect added are removed again — a host that set its own `aria-hidden` keeps it.
|
|
1798
|
+
*/
|
|
1799
|
+
useEffect(() => {
|
|
1800
|
+
if (!open || !portalHost) return;
|
|
1801
|
+
const marked = [];
|
|
1802
|
+
for (const node of Array.from(document.body.children)) {
|
|
1803
|
+
if (node === portalHost || !(node instanceof HTMLElement)) continue;
|
|
1804
|
+
if (node.hasAttribute("aria-hidden")) continue;
|
|
1805
|
+
node.setAttribute("aria-hidden", "true");
|
|
1806
|
+
node.setAttribute("inert", "");
|
|
1807
|
+
marked.push(node);
|
|
1808
|
+
}
|
|
1809
|
+
return () => {
|
|
1810
|
+
for (const node of marked) {
|
|
1811
|
+
node.removeAttribute("aria-hidden");
|
|
1812
|
+
node.removeAttribute("inert");
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
}, [open, portalHost]);
|
|
1816
|
+
/**
|
|
1817
|
+
* Move focus into the dialog once there is something to focus (FR-A11Y-1).
|
|
1818
|
+
*
|
|
1819
|
+
* Deliberately keyed on `status`, not just `open`: the dialog opens in its loading state with
|
|
1820
|
+
* no focusable content, so placing focus at open time silently leaves it on the page behind —
|
|
1821
|
+
* which is exactly nowhere for anyone navigating by keyboard or screen reader.
|
|
1822
|
+
*/
|
|
1823
|
+
useEffect(() => {
|
|
1824
|
+
if (!open || status === "loading") return;
|
|
1825
|
+
dialogRef.current?.querySelector("input, select, button")?.focus();
|
|
1826
|
+
}, [open, status]);
|
|
1827
|
+
/** Escape closes; focus is trapped while open (FR-A11Y-2/3). */
|
|
1828
|
+
useEffect(() => {
|
|
1829
|
+
if (!open) return;
|
|
1830
|
+
const node = dialogRef.current;
|
|
1831
|
+
const previous = document.activeElement;
|
|
1832
|
+
const onKeyDown = (event) => {
|
|
1833
|
+
if (event.key === "Escape") {
|
|
1834
|
+
setOpen(false);
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
if (event.key !== "Tab" || !node) return;
|
|
1838
|
+
const focusable = [...node.querySelectorAll("input, select, button, a[href]")].filter((element) => !element.hasAttribute("disabled"));
|
|
1839
|
+
if (focusable.length === 0) return;
|
|
1840
|
+
const first = focusable[0];
|
|
1841
|
+
const last = focusable[focusable.length - 1];
|
|
1842
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
1843
|
+
event.preventDefault();
|
|
1844
|
+
last.focus();
|
|
1845
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
1846
|
+
event.preventDefault();
|
|
1847
|
+
first.focus();
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1851
|
+
return () => {
|
|
1852
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
1853
|
+
(triggerRef.current ?? previous)?.focus();
|
|
1854
|
+
};
|
|
1855
|
+
}, [open, setOpen]);
|
|
1856
|
+
function set(field, value) {
|
|
1857
|
+
setDraft((current) => ({
|
|
1858
|
+
...current,
|
|
1859
|
+
[field]: value
|
|
1860
|
+
}));
|
|
1861
|
+
setErrors((current) => ({
|
|
1862
|
+
...current,
|
|
1863
|
+
[field]: void 0
|
|
1864
|
+
}));
|
|
1865
|
+
setFeedback(null);
|
|
1866
|
+
}
|
|
1867
|
+
/** Validate on leaving a field, so a mistake is caught next to where it was made (FR-VALID-8). */
|
|
1868
|
+
function blur(field) {
|
|
1869
|
+
const found = validateProfileDraft(draft)[field];
|
|
1870
|
+
setErrors((current) => ({
|
|
1871
|
+
...current,
|
|
1872
|
+
[field]: found
|
|
1873
|
+
}));
|
|
1874
|
+
}
|
|
1875
|
+
function choosePhoto(file) {
|
|
1876
|
+
if (!file) return;
|
|
1877
|
+
const invalid = validateProfileImage(file);
|
|
1878
|
+
if (invalid) {
|
|
1879
|
+
setPhotoError(invalid);
|
|
1880
|
+
setPhoto(null);
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
setPhotoError(null);
|
|
1884
|
+
setPhoto({
|
|
1885
|
+
file,
|
|
1886
|
+
preview: URL.createObjectURL(file)
|
|
1887
|
+
});
|
|
1888
|
+
setFeedback(null);
|
|
1889
|
+
}
|
|
1890
|
+
async function save() {
|
|
1891
|
+
if (saving) return;
|
|
1892
|
+
const found = validateProfileDraft(draft);
|
|
1893
|
+
if (Object.keys(found).length > 0) {
|
|
1894
|
+
setErrors(found);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
const changed = buildProfileUpdate(draft, stored);
|
|
1898
|
+
if (Object.keys(changed).length === 0 && !photo) {
|
|
1899
|
+
setOpen(false);
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
const body = new FormData();
|
|
1903
|
+
for (const [field, value] of Object.entries(changed)) body.append(field, value);
|
|
1904
|
+
if (photo) body.append("image", photo.file);
|
|
1905
|
+
setSaving(true);
|
|
1906
|
+
setFeedback(null);
|
|
1907
|
+
try {
|
|
1908
|
+
const response = await fetch(PROFILE_ENDPOINT, {
|
|
1909
|
+
method: "POST",
|
|
1910
|
+
body
|
|
1911
|
+
});
|
|
1912
|
+
const payload = await response.json().catch(() => ({}));
|
|
1913
|
+
if (response.status === 401) {
|
|
1914
|
+
setFeedback({
|
|
1915
|
+
kind: "error",
|
|
1916
|
+
message: labels.signedOut
|
|
1917
|
+
});
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
if (!response.ok) {
|
|
1921
|
+
const field = conflictField(payload.message);
|
|
1922
|
+
if (field) {
|
|
1923
|
+
setErrors((current) => ({
|
|
1924
|
+
...current,
|
|
1925
|
+
[field]: payload.message
|
|
1926
|
+
}));
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
setFeedback({
|
|
1930
|
+
kind: "error",
|
|
1931
|
+
message: payload.message ?? labels.saveFailed
|
|
1932
|
+
});
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
const saved = payload.user ?? {};
|
|
1936
|
+
setStored(readProfile(saved));
|
|
1937
|
+
if (typeof saved.profilePicture === "string") setStoredPhoto(saved.profilePicture);
|
|
1938
|
+
setPhoto(null);
|
|
1939
|
+
setFeedback({
|
|
1940
|
+
kind: "success",
|
|
1941
|
+
message: labels.saved
|
|
1942
|
+
});
|
|
1943
|
+
await refresh().catch(() => false);
|
|
1944
|
+
props.onSaved?.(saved);
|
|
1945
|
+
setOpen(false);
|
|
1946
|
+
} catch {
|
|
1947
|
+
setFeedback({
|
|
1948
|
+
kind: "error",
|
|
1949
|
+
message: labels.saveFailed
|
|
1950
|
+
});
|
|
1951
|
+
} finally {
|
|
1952
|
+
setSaving(false);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
if (!isLoaded || !isSignedIn) return null;
|
|
1956
|
+
const rootClass = [
|
|
1957
|
+
"polyx-signin",
|
|
1958
|
+
"polyx-userprofile",
|
|
1959
|
+
props.className
|
|
1960
|
+
].filter(Boolean).join(" ");
|
|
1961
|
+
const displayName = [draft.firstName, draft.lastName].filter(Boolean).join(" ");
|
|
1962
|
+
function field(name, label, extra = {}) {
|
|
1963
|
+
const id = `${fieldId}-${name}`;
|
|
1964
|
+
const error = errors[name];
|
|
1965
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1966
|
+
className: "polyx-signin__field",
|
|
1967
|
+
children: [
|
|
1968
|
+
/* @__PURE__ */ jsxs("label", {
|
|
1969
|
+
className: "polyx-signin__label",
|
|
1970
|
+
htmlFor: id,
|
|
1971
|
+
children: [label, extra.optional ? /* @__PURE__ */ jsxs("span", {
|
|
1972
|
+
className: "polyx-userprofile__optional",
|
|
1973
|
+
children: [
|
|
1974
|
+
" (",
|
|
1975
|
+
labels.optional,
|
|
1976
|
+
")"
|
|
1977
|
+
]
|
|
1978
|
+
}) : null]
|
|
1979
|
+
}),
|
|
1980
|
+
/* @__PURE__ */ jsx("input", {
|
|
1981
|
+
id,
|
|
1982
|
+
className: "polyx-signin__input",
|
|
1983
|
+
type: extra.type ?? "text",
|
|
1984
|
+
value: draft[name] ?? "",
|
|
1985
|
+
placeholder: extra.placeholder,
|
|
1986
|
+
disabled: saving,
|
|
1987
|
+
"aria-required": extra.optional ? void 0 : true,
|
|
1988
|
+
"aria-invalid": error ? true : void 0,
|
|
1989
|
+
"aria-describedby": error ? `${id}-error` : void 0,
|
|
1990
|
+
onChange: (event) => set(name, event.target.value),
|
|
1991
|
+
onBlur: () => blur(name)
|
|
1992
|
+
}),
|
|
1993
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
1994
|
+
id: `${id}-error`,
|
|
1995
|
+
className: "polyx-userprofile__field-error",
|
|
1996
|
+
children: error
|
|
1997
|
+
}) : null
|
|
1998
|
+
]
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2002
|
+
className: rootClass,
|
|
2003
|
+
style: brandingStyle,
|
|
2004
|
+
children: [props.open === void 0 ? /* @__PURE__ */ jsx("button", {
|
|
2005
|
+
ref: triggerRef,
|
|
2006
|
+
type: "button",
|
|
2007
|
+
className: ["polyx-userprofile__trigger", props.triggerClassName].filter(Boolean).join(" "),
|
|
2008
|
+
onClick: () => setOpen(true),
|
|
2009
|
+
children: props.trigger ?? labels.trigger
|
|
2010
|
+
}) : null, open && portalHost ? createPortal(/* @__PURE__ */ jsx("div", {
|
|
2011
|
+
className: "polyx-signin polyx-userprofile polyx-userprofile__overlay",
|
|
2012
|
+
style: brandingStyle,
|
|
2013
|
+
onMouseDown: () => setOpen(false),
|
|
2014
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2015
|
+
ref: dialogRef,
|
|
2016
|
+
role: "dialog",
|
|
2017
|
+
"aria-modal": "true",
|
|
2018
|
+
"aria-labelledby": titleId,
|
|
2019
|
+
className: ["polyx-userprofile__dialog", props.dialogClassName].filter(Boolean).join(" "),
|
|
2020
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
2021
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2022
|
+
className: "polyx-userprofile__header",
|
|
2023
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
2024
|
+
id: titleId,
|
|
2025
|
+
className: "polyx-userprofile__title",
|
|
2026
|
+
children: labels.title
|
|
2027
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2028
|
+
className: "polyx-userprofile__description",
|
|
2029
|
+
children: labels.description
|
|
2030
|
+
})]
|
|
2031
|
+
}), status === "loading" ? /* @__PURE__ */ jsx("p", {
|
|
2032
|
+
className: "polyx-userprofile__status",
|
|
2033
|
+
role: "status",
|
|
2034
|
+
children: labels.loading
|
|
2035
|
+
}) : status === "load_failed" ? /* @__PURE__ */ jsx("p", {
|
|
2036
|
+
className: "polyx-signin__error",
|
|
2037
|
+
role: "alert",
|
|
2038
|
+
children: labels.loadFailed
|
|
2039
|
+
}) : /* @__PURE__ */ jsxs("form", {
|
|
2040
|
+
className: "polyx-signin__form",
|
|
2041
|
+
onSubmit: (event) => {
|
|
2042
|
+
event.preventDefault();
|
|
2043
|
+
save();
|
|
2044
|
+
},
|
|
2045
|
+
children: [
|
|
2046
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2047
|
+
className: "polyx-userprofile__photo",
|
|
2048
|
+
children: [/* @__PURE__ */ jsx(UserAvatar, {
|
|
2049
|
+
src: photo?.preview ?? storedPhoto,
|
|
2050
|
+
name: displayName,
|
|
2051
|
+
size: 56
|
|
2052
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2053
|
+
className: "polyx-userprofile__photo-actions",
|
|
2054
|
+
children: [
|
|
2055
|
+
/* @__PURE__ */ jsx("button", {
|
|
2056
|
+
type: "button",
|
|
2057
|
+
className: "polyx-userprofile__photo-button",
|
|
2058
|
+
disabled: saving,
|
|
2059
|
+
onClick: () => fileRef.current?.click(),
|
|
2060
|
+
children: labels.changePhoto
|
|
2061
|
+
}),
|
|
2062
|
+
/* @__PURE__ */ jsx("input", {
|
|
2063
|
+
ref: fileRef,
|
|
2064
|
+
type: "file",
|
|
2065
|
+
accept: "image/*",
|
|
2066
|
+
className: "polyx-userprofile__file",
|
|
2067
|
+
"aria-label": labels.photo,
|
|
2068
|
+
onChange: (event) => choosePhoto(event.target.files?.[0])
|
|
2069
|
+
}),
|
|
2070
|
+
photoError ? /* @__PURE__ */ jsx("p", {
|
|
2071
|
+
className: "polyx-userprofile__field-error",
|
|
2072
|
+
children: photoError
|
|
2073
|
+
}) : null
|
|
2074
|
+
]
|
|
2075
|
+
})]
|
|
2076
|
+
}),
|
|
2077
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2078
|
+
className: "polyx-userprofile__row",
|
|
2079
|
+
children: [field("firstName", labels.firstName), field("lastName", labels.lastName)]
|
|
2080
|
+
}),
|
|
2081
|
+
field("phoneNumber", labels.phoneNumber, {
|
|
2082
|
+
optional: true,
|
|
2083
|
+
type: "tel",
|
|
2084
|
+
placeholder: "+14155552671"
|
|
2085
|
+
}),
|
|
2086
|
+
field("cnic", labels.cnic, { optional: true }),
|
|
2087
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2088
|
+
className: "polyx-signin__field",
|
|
2089
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
2090
|
+
className: "polyx-signin__label",
|
|
2091
|
+
htmlFor: `${fieldId}-gender`,
|
|
2092
|
+
children: [labels.gender, /* @__PURE__ */ jsxs("span", {
|
|
2093
|
+
className: "polyx-userprofile__optional",
|
|
2094
|
+
children: [
|
|
2095
|
+
" (",
|
|
2096
|
+
labels.optional,
|
|
2097
|
+
")"
|
|
2098
|
+
]
|
|
2099
|
+
})]
|
|
2100
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
2101
|
+
id: `${fieldId}-gender`,
|
|
2102
|
+
className: "polyx-signin__input",
|
|
2103
|
+
value: draft.gender ?? "",
|
|
2104
|
+
disabled: saving,
|
|
2105
|
+
onChange: (event) => set("gender", event.target.value),
|
|
2106
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
2107
|
+
value: "",
|
|
2108
|
+
children: labels.genderUnset
|
|
2109
|
+
}), GENDERS.map((value) => /* @__PURE__ */ jsx("option", {
|
|
2110
|
+
value,
|
|
2111
|
+
children: value === "male" ? labels.genderMale : value === "female" ? labels.genderFemale : labels.genderOther
|
|
2112
|
+
}, value))]
|
|
2113
|
+
})]
|
|
2114
|
+
}),
|
|
2115
|
+
feedback ? /* @__PURE__ */ jsx("p", {
|
|
2116
|
+
className: feedback.kind === "error" ? "polyx-signin__error" : "polyx-userprofile__success",
|
|
2117
|
+
role: feedback.kind === "error" ? "alert" : "status",
|
|
2118
|
+
children: feedback.message
|
|
2119
|
+
}) : null,
|
|
2120
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2121
|
+
className: "polyx-userprofile__actions",
|
|
2122
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
2123
|
+
type: "button",
|
|
2124
|
+
className: "polyx-userprofile__cancel",
|
|
2125
|
+
disabled: saving,
|
|
2126
|
+
onClick: () => setOpen(false),
|
|
2127
|
+
children: labels.cancel
|
|
2128
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
2129
|
+
type: "submit",
|
|
2130
|
+
className: "polyx-signin__submit",
|
|
2131
|
+
disabled: saving,
|
|
2132
|
+
children: saving ? labels.saving : labels.save
|
|
2133
|
+
})]
|
|
2134
|
+
})
|
|
2135
|
+
]
|
|
2136
|
+
})]
|
|
2137
|
+
})
|
|
2138
|
+
}), portalHost) : null]
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
//#endregion
|
|
1622
2142
|
//#region src/components/user-button.tsx
|
|
1623
2143
|
const DEFAULT_LABELS = {
|
|
1624
2144
|
trigger: "Account menu",
|
|
@@ -1725,6 +2245,7 @@ function UserButtonRoot(props) {
|
|
|
1725
2245
|
const { isLoaded, isSignedIn, signOut } = useAuth();
|
|
1726
2246
|
const user = useUser();
|
|
1727
2247
|
const [open, setOpen] = useState(props.defaultOpen ?? false);
|
|
2248
|
+
const [profileOpen, setProfileOpen] = useState(false);
|
|
1728
2249
|
const [focused, setFocused] = useState(0);
|
|
1729
2250
|
const [side, setSide] = useState(props.side && props.side !== "auto" ? props.side : "bottom");
|
|
1730
2251
|
const triggerRef = useRef(null);
|
|
@@ -1801,6 +2322,12 @@ function UserButtonRoot(props) {
|
|
|
1801
2322
|
labelIcon: /* @__PURE__ */ jsx(GearIcon, {}),
|
|
1802
2323
|
href: props.manageAccountUrl
|
|
1803
2324
|
});
|
|
2325
|
+
else if (props.showUserProfile) builtIns.set("manageAccount", {
|
|
2326
|
+
key: "manageAccount",
|
|
2327
|
+
label: labels.manageAccount,
|
|
2328
|
+
labelIcon: /* @__PURE__ */ jsx(GearIcon, {}),
|
|
2329
|
+
onClick: () => setProfileOpen(true)
|
|
2330
|
+
});
|
|
1804
2331
|
builtIns.set("signOut", {
|
|
1805
2332
|
key: "signOut",
|
|
1806
2333
|
label: labels.signOut,
|
|
@@ -1840,81 +2367,89 @@ function UserButtonRoot(props) {
|
|
|
1840
2367
|
props.className
|
|
1841
2368
|
].filter(Boolean).join(" "),
|
|
1842
2369
|
"data-polyx-userbutton": open ? "open" : "closed",
|
|
1843
|
-
children: [
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
name,
|
|
1858
|
-
size: props.avatarSize
|
|
1859
|
-
}), props.showName ? /* @__PURE__ */ jsx("span", {
|
|
1860
|
-
className: "polyx-userbutton__trigger-name",
|
|
1861
|
-
children: name
|
|
1862
|
-
}) : null] })
|
|
1863
|
-
}), open ? /* @__PURE__ */ jsxs("div", {
|
|
1864
|
-
ref: menuRef,
|
|
1865
|
-
id: menuId,
|
|
1866
|
-
role: "menu",
|
|
1867
|
-
className: "polyx-userbutton__menu",
|
|
1868
|
-
"data-polyx-side": side,
|
|
1869
|
-
"data-polyx-align": align,
|
|
1870
|
-
onKeyDown: onMenuKeyDown,
|
|
1871
|
-
children: [customHeader ?? /* @__PURE__ */ jsxs("div", {
|
|
1872
|
-
className: "polyx-userbutton__identity",
|
|
1873
|
-
children: [/* @__PURE__ */ jsx(UserAvatar, {
|
|
2370
|
+
children: [
|
|
2371
|
+
/* @__PURE__ */ jsx("button", {
|
|
2372
|
+
ref: triggerRef,
|
|
2373
|
+
type: "button",
|
|
2374
|
+
className: ["polyx-userbutton__trigger", props.triggerClassName].filter(Boolean).join(" "),
|
|
2375
|
+
"aria-haspopup": "menu",
|
|
2376
|
+
"aria-expanded": open,
|
|
2377
|
+
"aria-controls": open ? menuId : void 0,
|
|
2378
|
+
"aria-label": labels.trigger,
|
|
2379
|
+
onClick: () => {
|
|
2380
|
+
setFocused(0);
|
|
2381
|
+
setOpen((current) => !current);
|
|
2382
|
+
},
|
|
2383
|
+
children: props.trigger ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(UserAvatar, {
|
|
1874
2384
|
src: user.avatarUrl,
|
|
1875
2385
|
name,
|
|
1876
|
-
size:
|
|
1877
|
-
}), /* @__PURE__ */
|
|
1878
|
-
className: "polyx-
|
|
1879
|
-
children:
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2386
|
+
size: props.avatarSize
|
|
2387
|
+
}), props.showName ? /* @__PURE__ */ jsx("span", {
|
|
2388
|
+
className: "polyx-userbutton__trigger-name",
|
|
2389
|
+
children: name
|
|
2390
|
+
}) : null] })
|
|
2391
|
+
}),
|
|
2392
|
+
open ? /* @__PURE__ */ jsxs("div", {
|
|
2393
|
+
ref: menuRef,
|
|
2394
|
+
id: menuId,
|
|
2395
|
+
role: "menu",
|
|
2396
|
+
className: "polyx-userbutton__menu",
|
|
2397
|
+
"data-polyx-side": side,
|
|
2398
|
+
"data-polyx-align": align,
|
|
2399
|
+
onKeyDown: onMenuKeyDown,
|
|
2400
|
+
children: [customHeader ?? /* @__PURE__ */ jsxs("div", {
|
|
2401
|
+
className: "polyx-userbutton__identity",
|
|
2402
|
+
children: [/* @__PURE__ */ jsx(UserAvatar, {
|
|
2403
|
+
src: user.avatarUrl,
|
|
2404
|
+
name,
|
|
2405
|
+
size: 36
|
|
2406
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2407
|
+
className: "polyx-userbutton__identity-text",
|
|
2408
|
+
children: [user.displayName ? /* @__PURE__ */ jsx("span", {
|
|
2409
|
+
className: "polyx-userbutton__name",
|
|
2410
|
+
children: user.displayName
|
|
2411
|
+
}) : null, user.email ? /* @__PURE__ */ jsx("span", {
|
|
2412
|
+
className: "polyx-userbutton__email",
|
|
2413
|
+
children: user.email
|
|
2414
|
+
}) : null]
|
|
2415
|
+
})]
|
|
2416
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
2417
|
+
className: "polyx-userbutton__items",
|
|
2418
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx("li", { children: item.custom ? /* @__PURE__ */ jsx("div", {
|
|
2419
|
+
className: "polyx-userbutton__custom",
|
|
2420
|
+
children: item.custom
|
|
2421
|
+
}) : item.href ? /* @__PURE__ */ jsxs("a", {
|
|
2422
|
+
ref: (node) => {
|
|
2423
|
+
itemRefs.current[index] = node;
|
|
2424
|
+
},
|
|
2425
|
+
role: "menuitem",
|
|
2426
|
+
tabIndex: index === focused ? 0 : -1,
|
|
2427
|
+
className: "polyx-userbutton__item",
|
|
2428
|
+
href: item.href,
|
|
2429
|
+
onClick: () => close(false),
|
|
2430
|
+
children: [item.labelIcon, item.label]
|
|
2431
|
+
}) : /* @__PURE__ */ jsxs("button", {
|
|
2432
|
+
ref: (node) => {
|
|
2433
|
+
itemRefs.current[index] = node;
|
|
2434
|
+
},
|
|
2435
|
+
role: "menuitem",
|
|
2436
|
+
tabIndex: index === focused ? 0 : -1,
|
|
2437
|
+
type: "button",
|
|
2438
|
+
className: "polyx-userbutton__item",
|
|
2439
|
+
onClick: () => {
|
|
2440
|
+
close(false);
|
|
2441
|
+
item.onClick?.();
|
|
2442
|
+
},
|
|
2443
|
+
children: [item.labelIcon, item.label]
|
|
2444
|
+
}) }, item.key))
|
|
1886
2445
|
})]
|
|
1887
|
-
})
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
itemRefs.current[index] = node;
|
|
1895
|
-
},
|
|
1896
|
-
role: "menuitem",
|
|
1897
|
-
tabIndex: index === focused ? 0 : -1,
|
|
1898
|
-
className: "polyx-userbutton__item",
|
|
1899
|
-
href: item.href,
|
|
1900
|
-
onClick: () => close(false),
|
|
1901
|
-
children: [item.labelIcon, item.label]
|
|
1902
|
-
}) : /* @__PURE__ */ jsxs("button", {
|
|
1903
|
-
ref: (node) => {
|
|
1904
|
-
itemRefs.current[index] = node;
|
|
1905
|
-
},
|
|
1906
|
-
role: "menuitem",
|
|
1907
|
-
tabIndex: index === focused ? 0 : -1,
|
|
1908
|
-
type: "button",
|
|
1909
|
-
className: "polyx-userbutton__item",
|
|
1910
|
-
onClick: () => {
|
|
1911
|
-
close(false);
|
|
1912
|
-
item.onClick?.();
|
|
1913
|
-
},
|
|
1914
|
-
children: [item.labelIcon, item.label]
|
|
1915
|
-
}) }, item.key))
|
|
1916
|
-
})]
|
|
1917
|
-
}) : null]
|
|
2446
|
+
}) : null,
|
|
2447
|
+
props.showUserProfile && !props.manageAccountUrl ? /* @__PURE__ */ jsx(UserProfile, {
|
|
2448
|
+
...props.userProfileProps,
|
|
2449
|
+
open: profileOpen,
|
|
2450
|
+
onOpenChange: setProfileOpen
|
|
2451
|
+
}) : null
|
|
2452
|
+
]
|
|
1918
2453
|
});
|
|
1919
2454
|
}
|
|
1920
2455
|
function GearIcon() {
|
|
@@ -1979,6 +2514,33 @@ const UserButton = Object.assign(UserButtonRoot, {
|
|
|
1979
2514
|
Header
|
|
1980
2515
|
});
|
|
1981
2516
|
//#endregion
|
|
2517
|
+
//#region src/auth-event.ts
|
|
2518
|
+
/**
|
|
2519
|
+
* Read a one-shot auth event published by the BFF (F017 / FR-CLIENT-5).
|
|
2520
|
+
*
|
|
2521
|
+
* Sign-in and sign-out are full-page navigations, so an app cannot simply react in the
|
|
2522
|
+
* handler that triggered them — the page is gone. The BFF leaves a short-lived, readable
|
|
2523
|
+
* cookie; the destination page consumes it once.
|
|
2524
|
+
*/
|
|
2525
|
+
/** Must match `AUTH_EVENT_COOKIE` in `@poly-x/next`. */
|
|
2526
|
+
const AUTH_EVENT_COOKIE = "polyx_auth_event";
|
|
2527
|
+
function readCookie(name) {
|
|
2528
|
+
if (typeof document === "undefined") return void 0;
|
|
2529
|
+
return document.cookie.split(";").map((part) => part.trim()).find((part) => part.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
2530
|
+
}
|
|
2531
|
+
/**
|
|
2532
|
+
* Read the pending auth event and clear it, so it fires exactly once.
|
|
2533
|
+
*
|
|
2534
|
+
* Returns `null` when there is nothing pending — which is the normal case on almost every
|
|
2535
|
+
* page load. Safe to call during render or in an effect; it is a no-op on the server.
|
|
2536
|
+
*/
|
|
2537
|
+
function consumeAuthEvent() {
|
|
2538
|
+
const value = readCookie(AUTH_EVENT_COOKIE);
|
|
2539
|
+
if (value !== "signed-in" && value !== "signed-out") return null;
|
|
2540
|
+
document.cookie = `${AUTH_EVENT_COOKIE}=; Path=/; Max-Age=0; SameSite=Lax`;
|
|
2541
|
+
return value;
|
|
2542
|
+
}
|
|
2543
|
+
//#endregion
|
|
1982
2544
|
//#region src/live-authz.ts
|
|
1983
2545
|
const DEFAULT_POLL_MS = 6e4;
|
|
1984
2546
|
const DEFAULT_FOCUS_THROTTLE_MS = 5e3;
|
|
@@ -2117,6 +2679,7 @@ function useAuthzChanged(callback) {
|
|
|
2117
2679
|
* F006; this entry is the state foundation (provider, hooks, browser seams).
|
|
2118
2680
|
*/
|
|
2119
2681
|
const PACKAGE_NAME = "@poly-x/react";
|
|
2120
|
-
|
|
2682
|
+
/** The published version of this package, injected at build time. */
|
|
2683
|
+
const version = "0.2.0";
|
|
2121
2684
|
//#endregion
|
|
2122
|
-
export { AuthCallback, BroadcastAuthChannel, BroadcastChannelSessionChannel, ForgotPassword, PACKAGE_NAME, PolyXContext, PolyXProvider, PopupCancelledError, Protected, ResetPassword, SessionStoragePkceStore, SignIn, UserAvatar, UserButton, WebLocksLock, WindowBrowserBridge, capturePreciseLocation, createLoginController, initialsFrom, resolveSide, startLiveAuthz, useAuth, useAuthCallback, useAuthzChanged, useLiveAuthz, useSession, useUser, version };
|
|
2685
|
+
export { AuthCallback, BroadcastAuthChannel, BroadcastChannelSessionChannel, ForgotPassword, PACKAGE_NAME, PolyXContext, PolyXProvider, PopupCancelledError, Protected, ResetPassword, SessionStoragePkceStore, SignIn, UserAvatar, UserButton, UserProfile, WebLocksLock, WindowBrowserBridge, capturePreciseLocation, consumeAuthEvent, createLoginController, initialsFrom, resolveSide, startLiveAuthz, useAuth, useAuthCallback, useAuthzChanged, useLiveAuthz, useSession, useUser, version };
|