@riligar/auth-react 1.3.0 → 1.5.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.esm.js +157 -70
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +156 -68
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React$1, {
|
|
1
|
+
import React$1, { useMemo, useEffect, createContext, useState, useRef } from 'react';
|
|
2
2
|
import { Navigate, Outlet } from 'react-router-dom';
|
|
3
3
|
import { Paper, Stack, Image, Title, Text, TextInput, PasswordInput, Anchor, Button, Divider, Group, Center, Loader } from '@mantine/core';
|
|
4
4
|
import { useForm } from '@mantine/form';
|
|
@@ -58,10 +58,18 @@ async function api(route, opts = {}) {
|
|
|
58
58
|
|
|
59
59
|
// Converte JSON automaticamente e lança erro legível
|
|
60
60
|
const data = res.status !== 204 ? await res.json().catch(() => ({})) : null;
|
|
61
|
-
if (!res.ok)
|
|
62
|
-
res
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
let errorMessage = data?.message || data?.error || data?.detail || res.statusText;
|
|
63
|
+
|
|
64
|
+
// Se a mensagem for um objeto, tenta extrair string ou stringify
|
|
65
|
+
if (typeof errorMessage === 'object') {
|
|
66
|
+
errorMessage = errorMessage.message || JSON.stringify(errorMessage);
|
|
67
|
+
}
|
|
68
|
+
throw Object.assign(new Error(errorMessage), {
|
|
69
|
+
res,
|
|
70
|
+
data
|
|
71
|
+
});
|
|
72
|
+
}
|
|
65
73
|
return data;
|
|
66
74
|
}
|
|
67
75
|
|
|
@@ -672,6 +680,68 @@ const useAuthStore = create((set, get) => ({
|
|
|
672
680
|
})
|
|
673
681
|
}));
|
|
674
682
|
|
|
683
|
+
const isIterable = (obj) => Symbol.iterator in obj;
|
|
684
|
+
const hasIterableEntries = (value) => (
|
|
685
|
+
// HACK: avoid checking entries type
|
|
686
|
+
"entries" in value
|
|
687
|
+
);
|
|
688
|
+
const compareEntries = (valueA, valueB) => {
|
|
689
|
+
const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());
|
|
690
|
+
const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());
|
|
691
|
+
if (mapA.size !== mapB.size) {
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
for (const [key, value] of mapA) {
|
|
695
|
+
if (!mapB.has(key) || !Object.is(value, mapB.get(key))) {
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return true;
|
|
700
|
+
};
|
|
701
|
+
const compareIterables = (valueA, valueB) => {
|
|
702
|
+
const iteratorA = valueA[Symbol.iterator]();
|
|
703
|
+
const iteratorB = valueB[Symbol.iterator]();
|
|
704
|
+
let nextA = iteratorA.next();
|
|
705
|
+
let nextB = iteratorB.next();
|
|
706
|
+
while (!nextA.done && !nextB.done) {
|
|
707
|
+
if (!Object.is(nextA.value, nextB.value)) {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
nextA = iteratorA.next();
|
|
711
|
+
nextB = iteratorB.next();
|
|
712
|
+
}
|
|
713
|
+
return !!nextA.done && !!nextB.done;
|
|
714
|
+
};
|
|
715
|
+
function shallow(valueA, valueB) {
|
|
716
|
+
if (Object.is(valueA, valueB)) {
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
if (typeof valueA !== "object" || valueA === null || typeof valueB !== "object" || valueB === null) {
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
if (isIterable(valueA) && isIterable(valueB)) {
|
|
726
|
+
if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {
|
|
727
|
+
return compareEntries(valueA, valueB);
|
|
728
|
+
}
|
|
729
|
+
return compareIterables(valueA, valueB);
|
|
730
|
+
}
|
|
731
|
+
return compareEntries(
|
|
732
|
+
{ entries: () => Object.entries(valueA) },
|
|
733
|
+
{ entries: () => Object.entries(valueB) }
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function useShallow(selector) {
|
|
738
|
+
const prev = React$1.useRef(void 0);
|
|
739
|
+
return (state) => {
|
|
740
|
+
const next = selector(state);
|
|
741
|
+
return shallow(prev.current, next) ? prev.current : prev.current = next;
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
675
745
|
const AuthContext = /*#__PURE__*/createContext(); // só para ter o Provider em JSX
|
|
676
746
|
|
|
677
747
|
function AuthProvider({
|
|
@@ -694,7 +764,9 @@ function AuthProvider({
|
|
|
694
764
|
const checkTokenValidity = useAuthStore(s => s.checkTokenValidity);
|
|
695
765
|
|
|
696
766
|
// Configura SDK com apiUrl e apiKey
|
|
697
|
-
|
|
767
|
+
// Configura SDK com apiUrl e apiKey
|
|
768
|
+
// Usamos useMemo para garantir que a configuração ocorra ANTES dos efeitos dos componentes filhos
|
|
769
|
+
useMemo(() => {
|
|
698
770
|
configure({
|
|
699
771
|
apiUrl,
|
|
700
772
|
apiKey
|
|
@@ -741,12 +813,12 @@ function AuthProvider({
|
|
|
741
813
|
}
|
|
742
814
|
|
|
743
815
|
/* Hooks "facade" que a app vai usar */
|
|
744
|
-
const useAuth = () => useAuthStore(s => ({
|
|
816
|
+
const useAuth = () => useAuthStore(useShallow(s => ({
|
|
745
817
|
user: s.user,
|
|
746
818
|
loading: s.loading,
|
|
747
819
|
error: s.error,
|
|
748
820
|
isAuthenticated: s.user !== null
|
|
749
|
-
}));
|
|
821
|
+
})));
|
|
750
822
|
|
|
751
823
|
// Auth Actions
|
|
752
824
|
const useSignIn = () => useAuthStore(s => s.signIn);
|
|
@@ -755,62 +827,37 @@ const useSignOut = () => useAuthStore(s => s.signOut);
|
|
|
755
827
|
const useCheckToken = () => useAuthStore(s => s.checkTokenValidity);
|
|
756
828
|
|
|
757
829
|
// Magic Link Hook
|
|
758
|
-
const useMagicLink = () => {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
sendMagicLink,
|
|
766
|
-
verifyMagicLink,
|
|
767
|
-
loading,
|
|
768
|
-
verifying,
|
|
769
|
-
error
|
|
770
|
-
};
|
|
771
|
-
};
|
|
830
|
+
const useMagicLink = () => useAuthStore(useShallow(s => ({
|
|
831
|
+
sendMagicLink: s.sendMagicLink,
|
|
832
|
+
verifyMagicLink: s.verifyMagicLink,
|
|
833
|
+
loading: s.loadingStates.magicLink,
|
|
834
|
+
verifying: s.loadingStates.verifyMagicLink,
|
|
835
|
+
error: s.error
|
|
836
|
+
})));
|
|
772
837
|
|
|
773
838
|
// Password Reset Hook
|
|
774
|
-
const usePasswordReset = () => {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
forgotPassword,
|
|
781
|
-
resetPassword,
|
|
782
|
-
loading,
|
|
783
|
-
error
|
|
784
|
-
};
|
|
785
|
-
};
|
|
839
|
+
const usePasswordReset = () => useAuthStore(useShallow(s => ({
|
|
840
|
+
forgotPassword: s.forgotPassword,
|
|
841
|
+
resetPassword: s.resetPassword,
|
|
842
|
+
loading: s.loadingStates.resetPassword,
|
|
843
|
+
error: s.error
|
|
844
|
+
})));
|
|
786
845
|
|
|
787
846
|
// Email Verification Hook
|
|
788
|
-
const useEmailVerification = () => {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
verifyEmail,
|
|
796
|
-
resendVerification,
|
|
797
|
-
loading,
|
|
798
|
-
resending,
|
|
799
|
-
error
|
|
800
|
-
};
|
|
801
|
-
};
|
|
847
|
+
const useEmailVerification = () => useAuthStore(useShallow(s => ({
|
|
848
|
+
verifyEmail: s.verifyEmail,
|
|
849
|
+
resendVerification: s.resendVerification,
|
|
850
|
+
loading: s.loadingStates.verifyEmail,
|
|
851
|
+
resending: s.loadingStates.resendVerification,
|
|
852
|
+
error: s.error
|
|
853
|
+
})));
|
|
802
854
|
|
|
803
855
|
// Session Hook
|
|
804
|
-
const useSession = () => {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
getSession,
|
|
810
|
-
user,
|
|
811
|
-
setUser
|
|
812
|
-
};
|
|
813
|
-
};
|
|
856
|
+
const useSession = () => useAuthStore(useShallow(s => ({
|
|
857
|
+
getSession: s.getSession,
|
|
858
|
+
user: s.user,
|
|
859
|
+
setUser: s.setUser
|
|
860
|
+
})));
|
|
814
861
|
|
|
815
862
|
// Loading States Hook
|
|
816
863
|
const useAuthLoading = () => useAuthStore(s => s.loadingStates);
|
|
@@ -1237,6 +1284,7 @@ function MagicLinkVerify({
|
|
|
1237
1284
|
const [status, setStatus] = useState('verifying'); // verifying, success, error
|
|
1238
1285
|
const [errorMessage, setErrorMessage] = useState('');
|
|
1239
1286
|
const verifyMagicLink = useAuthStore(s => s.verifyMagicLink);
|
|
1287
|
+
const verifyingTokenRef = useRef(null);
|
|
1240
1288
|
useEffect(() => {
|
|
1241
1289
|
const verify = async () => {
|
|
1242
1290
|
// Pega token da prop ou da URL
|
|
@@ -1251,6 +1299,10 @@ function MagicLinkVerify({
|
|
|
1251
1299
|
onError?.(new Error('No token'));
|
|
1252
1300
|
return;
|
|
1253
1301
|
}
|
|
1302
|
+
|
|
1303
|
+
// Evita verificar o mesmo token duas vezes (React Strict Mode ou re-renders)
|
|
1304
|
+
if (verifyingTokenRef.current === token) return;
|
|
1305
|
+
verifyingTokenRef.current = token;
|
|
1254
1306
|
try {
|
|
1255
1307
|
const result = await verifyMagicLink(token);
|
|
1256
1308
|
setStatus('success');
|
|
@@ -1267,7 +1319,8 @@ function MagicLinkVerify({
|
|
|
1267
1319
|
}
|
|
1268
1320
|
};
|
|
1269
1321
|
verify();
|
|
1270
|
-
|
|
1322
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1323
|
+
}, [propToken, redirectTo, redirectDelay]);
|
|
1271
1324
|
return /*#__PURE__*/React.createElement(AuthCard, _extends({
|
|
1272
1325
|
logo: logo
|
|
1273
1326
|
}, cardProps), status === 'verifying' && /*#__PURE__*/React.createElement(Stack, {
|
|
@@ -1522,9 +1575,13 @@ function VerifyEmailCard({
|
|
|
1522
1575
|
}) {
|
|
1523
1576
|
const [status, setStatus] = useState('verifying'); // verifying, success, error
|
|
1524
1577
|
const [errorMessage, setErrorMessage] = useState('');
|
|
1578
|
+
const [tokenEmail, setTokenEmail] = useState(null); // Novo estado para email do token
|
|
1579
|
+
|
|
1525
1580
|
const verifyEmail = useAuthStore(s => s.verifyEmail);
|
|
1526
1581
|
const resendVerification = useAuthStore(s => s.resendVerification);
|
|
1527
1582
|
const loadingResend = useAuthStore(s => s.loadingStates.resendVerification);
|
|
1583
|
+
const user = useAuthStore(s => s.user);
|
|
1584
|
+
const verifyingTokenRef = useRef(null);
|
|
1528
1585
|
useEffect(() => {
|
|
1529
1586
|
const verify = async () => {
|
|
1530
1587
|
// Pega token da prop ou da URL
|
|
@@ -1534,11 +1591,25 @@ function VerifyEmailCard({
|
|
|
1534
1591
|
token = urlParams.get('token');
|
|
1535
1592
|
}
|
|
1536
1593
|
if (!token) {
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1594
|
+
// Se já mostramos o erro de token faltando, não precisa fazer nada
|
|
1595
|
+
if (status === 'missing_token') return;
|
|
1596
|
+
setStatus('missing_token');
|
|
1540
1597
|
return;
|
|
1541
1598
|
}
|
|
1599
|
+
|
|
1600
|
+
// Tenta extrair email do token (mesmo expirado) para permitir reenvio
|
|
1601
|
+
try {
|
|
1602
|
+
const payload = decodeJWT(token);
|
|
1603
|
+
if (payload?.email) {
|
|
1604
|
+
setTokenEmail(payload.email);
|
|
1605
|
+
}
|
|
1606
|
+
} catch (e) {
|
|
1607
|
+
// ignore jwt error
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// Evita verificar o mesmo token duas vezes (React Strict Mode ou re-renders)
|
|
1611
|
+
if (verifyingTokenRef.current === token) return;
|
|
1612
|
+
verifyingTokenRef.current = token;
|
|
1542
1613
|
try {
|
|
1543
1614
|
const result = await verifyEmail(token);
|
|
1544
1615
|
setStatus('success');
|
|
@@ -1549,18 +1620,22 @@ function VerifyEmailCard({
|
|
|
1549
1620
|
}, redirectDelay);
|
|
1550
1621
|
}
|
|
1551
1622
|
} catch (error) {
|
|
1623
|
+
// Se der erro, permitimos tentar novamente apenas se o usuário recarregar
|
|
1624
|
+
// ou se implementarmos um botão de retry que limpe o ref
|
|
1552
1625
|
setStatus('error');
|
|
1553
1626
|
setErrorMessage(error.message || labels.verificationFailed || 'Verificação falhou');
|
|
1554
1627
|
onError?.(error);
|
|
1555
1628
|
}
|
|
1556
1629
|
};
|
|
1557
1630
|
verify();
|
|
1558
|
-
|
|
1631
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1632
|
+
}, [propToken, redirectTo, redirectDelay]);
|
|
1559
1633
|
const handleResend = async () => {
|
|
1560
|
-
|
|
1634
|
+
const targetEmail = email || user?.email || tokenEmail;
|
|
1635
|
+
if (!targetEmail) return;
|
|
1561
1636
|
try {
|
|
1562
|
-
await resendVerification(
|
|
1563
|
-
onResent?.(
|
|
1637
|
+
await resendVerification(targetEmail);
|
|
1638
|
+
onResent?.(targetEmail);
|
|
1564
1639
|
} catch (error) {
|
|
1565
1640
|
onError?.(error);
|
|
1566
1641
|
}
|
|
@@ -1578,7 +1653,19 @@ function VerifyEmailCard({
|
|
|
1578
1653
|
size: "sm",
|
|
1579
1654
|
c: "dimmed",
|
|
1580
1655
|
ta: "center"
|
|
1581
|
-
}, labels.pleaseWait || 'Aguarde enquanto verificamos seu email...')), status === '
|
|
1656
|
+
}, labels.pleaseWait || 'Aguarde enquanto verificamos seu email...')), status === 'missing_token' && /*#__PURE__*/React.createElement(Stack, {
|
|
1657
|
+
align: "center",
|
|
1658
|
+
gap: "sm"
|
|
1659
|
+
}, /*#__PURE__*/React.createElement(IconMail, {
|
|
1660
|
+
size: 48,
|
|
1661
|
+
color: "var(--mantine-color-blue-6)"
|
|
1662
|
+
}), /*#__PURE__*/React.createElement(Title, {
|
|
1663
|
+
order: 4
|
|
1664
|
+
}, labels.verification || 'Verificação de Email'), /*#__PURE__*/React.createElement(Text, {
|
|
1665
|
+
size: "sm",
|
|
1666
|
+
c: "dimmed",
|
|
1667
|
+
ta: "center"
|
|
1668
|
+
}, labels.missingToken || 'Para verificar seu conta, clique no link enviado para seu email.')), status === 'success' && /*#__PURE__*/React.createElement(Stack, {
|
|
1582
1669
|
align: "center",
|
|
1583
1670
|
gap: "sm"
|
|
1584
1671
|
}, /*#__PURE__*/React.createElement(IconCheck, {
|
|
@@ -1602,7 +1689,7 @@ function VerifyEmailCard({
|
|
|
1602
1689
|
size: "sm",
|
|
1603
1690
|
c: "dimmed",
|
|
1604
1691
|
ta: "center"
|
|
1605
|
-
}, errorMessage || labels.invalidToken || 'O link é inválido ou expirou.'), email && /*#__PURE__*/React.createElement(Button, {
|
|
1692
|
+
}, errorMessage || labels.invalidToken || 'O link é inválido ou expirou.'), (email || user?.email || tokenEmail) && errorMessage?.toLowerCase().includes('expired') && /*#__PURE__*/React.createElement(Button, {
|
|
1606
1693
|
variant: "light",
|
|
1607
1694
|
leftSection: /*#__PURE__*/React.createElement(IconRefresh, {
|
|
1608
1695
|
size: 16
|
|
@@ -1610,8 +1697,8 @@ function VerifyEmailCard({
|
|
|
1610
1697
|
onClick: handleResend,
|
|
1611
1698
|
loading: loadingResend,
|
|
1612
1699
|
fullWidth: true
|
|
1613
|
-
}, labels.resend || '
|
|
1700
|
+
}, labels.resend || 'Solicitar novo link')));
|
|
1614
1701
|
}
|
|
1615
1702
|
|
|
1616
|
-
export { AuthCard, AuthProvider, ForgotPasswordForm, MagicLinkForm, MagicLinkVerify, ProtectedRoute, ResetPasswordForm, SignInForm, SignUpForm, VerifyEmailCard, configure, forgotPassword, getCurrentUser, getSession, isAuthenticated, refreshToken, resendVerification, resetPassword, sendMagicLink, signIn, signOut, signUp, socialRedirect, useAuth, useAuthLoading, useAuthStore, useCheckToken, useEmailVerification, useMagicLink, usePasswordReset, useSession, useSignIn, useSignOut, useSignUp, verifyEmail, verifyMagicLink };
|
|
1703
|
+
export { AuthCard, AuthProvider, ForgotPasswordForm, MagicLinkForm, MagicLinkVerify, ProtectedRoute, ResetPasswordForm, SignInForm, SignUpForm, VerifyEmailCard, configure, decodeJWT, forgotPassword, getCurrentUser, getSession, isAuthenticated, refreshToken, resendVerification, resetPassword, sendMagicLink, signIn, signOut, signUp, socialRedirect, useAuth, useAuthLoading, useAuthStore, useCheckToken, useEmailVerification, useMagicLink, usePasswordReset, useSession, useSignIn, useSignOut, useSignUp, verifyEmail, verifyMagicLink };
|
|
1617
1704
|
//# sourceMappingURL=index.esm.js.map
|