@nocios/crudify-ui 1.2.36 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -704,7 +704,7 @@ var useCrudifyAuth = () => {
704
704
  // src/components/CrudifyLogin/Forms/LoginForm.tsx
705
705
  import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
706
706
  var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError, redirectUrl = "/" }) => {
707
- const { crudify: crudify6 } = useCrudify();
707
+ const { crudify: crudify9 } = useCrudify();
708
708
  const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
709
709
  const { setToken } = useCrudifyAuth();
710
710
  const { t } = useTranslation();
@@ -756,10 +756,10 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
756
756
  clearErrors();
757
757
  setLoading(true);
758
758
  try {
759
- if (!crudify6) {
759
+ if (!crudify9) {
760
760
  throw new Error("Crudify not initialized");
761
761
  }
762
- const response = await crudify6.login(state.formData.username, state.formData.password);
762
+ const response = await crudify9.login(state.formData.username, state.formData.password);
763
763
  setLoading(false);
764
764
  if (response.success) {
765
765
  console.log("\u{1F510} LoginForm - Login successful, setting tokens");
@@ -922,7 +922,7 @@ import { useState as useState3 } from "react";
922
922
  import { Typography as Typography2, TextField as TextField2, Button as Button2, Box as Box2, CircularProgress as CircularProgress2, Alert as Alert2, Link as Link2 } from "@mui/material";
923
923
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
924
924
  var ForgotPasswordForm = ({ onScreenChange, onError }) => {
925
- const { crudify: crudify6 } = useCrudify();
925
+ const { crudify: crudify9 } = useCrudify();
926
926
  const [email, setEmail] = useState3("");
927
927
  const [loading, setLoading] = useState3(false);
928
928
  const [errors, setErrors] = useState3([]);
@@ -951,7 +951,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
951
951
  return emailRegex.test(email2);
952
952
  };
953
953
  const handleSubmit = async () => {
954
- if (loading || !crudify6) return;
954
+ if (loading || !crudify9) return;
955
955
  setErrors([]);
956
956
  setHelperTextEmail(null);
957
957
  if (!email) {
@@ -965,7 +965,7 @@ var ForgotPasswordForm = ({ onScreenChange, onError }) => {
965
965
  setLoading(true);
966
966
  try {
967
967
  const data = [{ operation: "requestPasswordReset", data: { email } }];
968
- const response = await crudify6.transaction(data);
968
+ const response = await crudify9.transaction(data);
969
969
  if (response.success) {
970
970
  if (response.data && response.data.existingCodeValid) {
971
971
  setCodeAlreadyExists(true);
@@ -1068,7 +1068,7 @@ import { useState as useState4, useEffect as useEffect5 } from "react";
1068
1068
  import { Typography as Typography3, TextField as TextField3, Button as Button3, Box as Box3, CircularProgress as CircularProgress3, Alert as Alert3, Link as Link3 } from "@mui/material";
1069
1069
  import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1070
1070
  var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess }) => {
1071
- const { crudify: crudify6 } = useCrudify();
1071
+ const { crudify: crudify9 } = useCrudify();
1072
1072
  const [newPassword, setNewPassword] = useState4("");
1073
1073
  const [confirmPassword, setConfirmPassword] = useState4("");
1074
1074
  const [loading, setLoading] = useState4(false);
@@ -1148,9 +1148,9 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1148
1148
  setErrors([t("resetPassword.invalidCode")]);
1149
1149
  setValidatingCode(false);
1150
1150
  setTimeout(() => onScreenChange?.("forgotPassword"), 3e3);
1151
- }, [searchParams, crudify6, t, onScreenChange]);
1151
+ }, [searchParams, crudify9, t, onScreenChange]);
1152
1152
  useEffect5(() => {
1153
- if (crudify6 && pendingValidation && !isValidating) {
1153
+ if (crudify9 && pendingValidation && !isValidating) {
1154
1154
  setIsValidating(true);
1155
1155
  const validateCode = async (emailToValidate, codeToValidate) => {
1156
1156
  try {
@@ -1160,7 +1160,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1160
1160
  data: { email: emailToValidate, codePassword: codeToValidate }
1161
1161
  }
1162
1162
  ];
1163
- const response = await crudify6.transaction(data);
1163
+ const response = await crudify9.transaction(data);
1164
1164
  if (response.data && Array.isArray(response.data)) {
1165
1165
  const validationResult = response.data[0];
1166
1166
  if (validationResult && validationResult.response && validationResult.response.status === "OK") {
@@ -1189,7 +1189,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1189
1189
  };
1190
1190
  validateCode(pendingValidation.email, pendingValidation.code);
1191
1191
  }
1192
- }, [crudify6, pendingValidation, t, onScreenChange]);
1192
+ }, [crudify9, pendingValidation, t, onScreenChange]);
1193
1193
  const validatePassword = (password) => {
1194
1194
  if (password.length < 8) {
1195
1195
  return t("resetPassword.passwordTooShort");
@@ -1197,7 +1197,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1197
1197
  return null;
1198
1198
  };
1199
1199
  const handleSubmit = async () => {
1200
- if (loading || !crudify6) return;
1200
+ if (loading || !crudify9) return;
1201
1201
  setErrors([]);
1202
1202
  setHelperTextNewPassword(null);
1203
1203
  setHelperTextConfirmPassword(null);
@@ -1228,7 +1228,7 @@ var ResetPasswordForm = ({ onScreenChange, onError, searchParams, onResetSuccess
1228
1228
  data: { email, codePassword: code, newPassword }
1229
1229
  }
1230
1230
  ];
1231
- const response = await crudify6.transaction(data);
1231
+ const response = await crudify9.transaction(data);
1232
1232
  if (response.success) {
1233
1233
  setErrors([]);
1234
1234
  setTimeout(() => {
@@ -1339,7 +1339,7 @@ import { useState as useState5, useEffect as useEffect6 } from "react";
1339
1339
  import { Typography as Typography4, TextField as TextField4, Button as Button4, Box as Box4, CircularProgress as CircularProgress4, Alert as Alert4, Link as Link4 } from "@mui/material";
1340
1340
  import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1341
1341
  var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1342
- const { crudify: crudify6 } = useCrudify();
1342
+ const { crudify: crudify9 } = useCrudify();
1343
1343
  const [code, setCode] = useState5("");
1344
1344
  const [loading, setLoading] = useState5(false);
1345
1345
  const [errors, setErrors] = useState5([]);
@@ -1378,7 +1378,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1378
1378
  }
1379
1379
  }, [searchParams, onScreenChange]);
1380
1380
  const handleSubmit = async () => {
1381
- if (loading || !crudify6) return;
1381
+ if (loading || !crudify9) return;
1382
1382
  setErrors([]);
1383
1383
  setHelperTextCode(null);
1384
1384
  if (!code) {
@@ -1397,7 +1397,7 @@ var CheckCodeForm = ({ onScreenChange, onError, searchParams }) => {
1397
1397
  data: { email, codePassword: code }
1398
1398
  }
1399
1399
  ];
1400
- const response = await crudify6.transaction(data);
1400
+ const response = await crudify9.transaction(data);
1401
1401
  if (response.success) {
1402
1402
  onScreenChange?.("resetPassword", { email, code, fromCodeVerification: "true" });
1403
1403
  } else {
@@ -2581,11 +2581,1204 @@ var getCrudifyInstanceSync = async () => {
2581
2581
  }
2582
2582
  return crudify5;
2583
2583
  };
2584
+
2585
+ // src/core/SessionManager.ts
2586
+ import crudify6 from "@nocios/crudify-browser";
2587
+
2588
+ // src/utils/tokenStorage.ts
2589
+ import CryptoJS from "crypto-js";
2590
+ var _TokenStorage = class _TokenStorage {
2591
+ /**
2592
+ * Configurar tipo de almacenamiento
2593
+ */
2594
+ static setStorageType(type) {
2595
+ _TokenStorage.storageType = type;
2596
+ }
2597
+ /**
2598
+ * Verificar si el storage está disponible
2599
+ */
2600
+ static isStorageAvailable(type) {
2601
+ try {
2602
+ const storage = window[type];
2603
+ const testKey = "__storage_test__";
2604
+ storage.setItem(testKey, "test");
2605
+ storage.removeItem(testKey);
2606
+ return true;
2607
+ } catch {
2608
+ return false;
2609
+ }
2610
+ }
2611
+ /**
2612
+ * Obtener instancia de storage
2613
+ */
2614
+ static getStorage() {
2615
+ if (_TokenStorage.storageType === "none") return null;
2616
+ if (!_TokenStorage.isStorageAvailable(_TokenStorage.storageType)) {
2617
+ console.warn(`Crudify: ${_TokenStorage.storageType} not available, tokens won't persist`);
2618
+ return null;
2619
+ }
2620
+ return window[_TokenStorage.storageType];
2621
+ }
2622
+ /**
2623
+ * Encriptar datos sensibles
2624
+ */
2625
+ static encrypt(data) {
2626
+ try {
2627
+ return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
2628
+ } catch (error) {
2629
+ console.error("Crudify: Encryption failed", error);
2630
+ return data;
2631
+ }
2632
+ }
2633
+ /**
2634
+ * Desencriptar datos
2635
+ */
2636
+ static decrypt(encryptedData) {
2637
+ try {
2638
+ const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
2639
+ const decrypted = bytes.toString(CryptoJS.enc.Utf8);
2640
+ return decrypted || encryptedData;
2641
+ } catch (error) {
2642
+ console.error("Crudify: Decryption failed", error);
2643
+ return encryptedData;
2644
+ }
2645
+ }
2646
+ /**
2647
+ * Guardar tokens de forma segura
2648
+ */
2649
+ static saveTokens(tokens) {
2650
+ const storage = _TokenStorage.getStorage();
2651
+ if (!storage) return;
2652
+ try {
2653
+ const tokenData = {
2654
+ accessToken: tokens.accessToken,
2655
+ refreshToken: tokens.refreshToken,
2656
+ expiresAt: tokens.expiresAt,
2657
+ refreshExpiresAt: tokens.refreshExpiresAt,
2658
+ savedAt: Date.now()
2659
+ };
2660
+ const encrypted = _TokenStorage.encrypt(JSON.stringify(tokenData));
2661
+ storage.setItem(_TokenStorage.TOKEN_KEY, encrypted);
2662
+ console.debug("Crudify: Tokens saved successfully");
2663
+ } catch (error) {
2664
+ console.error("Crudify: Failed to save tokens", error);
2665
+ }
2666
+ }
2667
+ /**
2668
+ * Obtener tokens guardados
2669
+ */
2670
+ static getTokens() {
2671
+ const storage = _TokenStorage.getStorage();
2672
+ if (!storage) return null;
2673
+ try {
2674
+ const encrypted = storage.getItem(_TokenStorage.TOKEN_KEY);
2675
+ if (!encrypted) return null;
2676
+ const decrypted = _TokenStorage.decrypt(encrypted);
2677
+ const tokenData = JSON.parse(decrypted);
2678
+ if (!tokenData.accessToken || !tokenData.refreshToken || !tokenData.expiresAt || !tokenData.refreshExpiresAt) {
2679
+ console.warn("Crudify: Incomplete token data found, clearing storage");
2680
+ _TokenStorage.clearTokens();
2681
+ return null;
2682
+ }
2683
+ if (Date.now() >= tokenData.refreshExpiresAt) {
2684
+ console.info("Crudify: Refresh token expired, clearing storage");
2685
+ _TokenStorage.clearTokens();
2686
+ return null;
2687
+ }
2688
+ return {
2689
+ accessToken: tokenData.accessToken,
2690
+ refreshToken: tokenData.refreshToken,
2691
+ expiresAt: tokenData.expiresAt,
2692
+ refreshExpiresAt: tokenData.refreshExpiresAt
2693
+ };
2694
+ } catch (error) {
2695
+ console.error("Crudify: Failed to retrieve tokens", error);
2696
+ _TokenStorage.clearTokens();
2697
+ return null;
2698
+ }
2699
+ }
2700
+ /**
2701
+ * Limpiar tokens almacenados
2702
+ */
2703
+ static clearTokens() {
2704
+ const storage = _TokenStorage.getStorage();
2705
+ if (!storage) return;
2706
+ try {
2707
+ storage.removeItem(_TokenStorage.TOKEN_KEY);
2708
+ console.debug("Crudify: Tokens cleared from storage");
2709
+ } catch (error) {
2710
+ console.error("Crudify: Failed to clear tokens", error);
2711
+ }
2712
+ }
2713
+ /**
2714
+ * Verificar si hay tokens válidos guardados
2715
+ */
2716
+ static hasValidTokens() {
2717
+ const tokens = _TokenStorage.getTokens();
2718
+ return tokens !== null;
2719
+ }
2720
+ /**
2721
+ * Obtener información de expiración
2722
+ */
2723
+ static getExpirationInfo() {
2724
+ const tokens = _TokenStorage.getTokens();
2725
+ if (!tokens) return null;
2726
+ const now = Date.now();
2727
+ return {
2728
+ accessExpired: now >= tokens.expiresAt,
2729
+ refreshExpired: now >= tokens.refreshExpiresAt,
2730
+ accessExpiresIn: Math.max(0, tokens.expiresAt - now),
2731
+ refreshExpiresIn: Math.max(0, tokens.refreshExpiresAt - now)
2732
+ };
2733
+ }
2734
+ /**
2735
+ * Actualizar solo el access token (después de refresh)
2736
+ */
2737
+ static updateAccessToken(newAccessToken, newExpiresAt) {
2738
+ const existingTokens = _TokenStorage.getTokens();
2739
+ if (!existingTokens) {
2740
+ console.warn("Crudify: Cannot update access token, no existing tokens found");
2741
+ return;
2742
+ }
2743
+ _TokenStorage.saveTokens({
2744
+ ...existingTokens,
2745
+ accessToken: newAccessToken,
2746
+ expiresAt: newExpiresAt
2747
+ });
2748
+ }
2749
+ };
2750
+ _TokenStorage.TOKEN_KEY = "crudify_tokens";
2751
+ _TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
2752
+ _TokenStorage.storageType = "localStorage";
2753
+ var TokenStorage = _TokenStorage;
2754
+
2755
+ // src/core/SessionManager.ts
2756
+ var SessionManager = class _SessionManager {
2757
+ constructor() {
2758
+ this.config = {};
2759
+ this.initialized = false;
2760
+ }
2761
+ static getInstance() {
2762
+ if (!_SessionManager.instance) {
2763
+ _SessionManager.instance = new _SessionManager();
2764
+ }
2765
+ return _SessionManager.instance;
2766
+ }
2767
+ /**
2768
+ * Inicializar el SessionManager
2769
+ */
2770
+ async initialize(config = {}) {
2771
+ if (this.initialized) {
2772
+ console.warn("SessionManager: Already initialized");
2773
+ return;
2774
+ }
2775
+ this.config = {
2776
+ storageType: "localStorage",
2777
+ autoRestore: true,
2778
+ enableLogging: false,
2779
+ ...config
2780
+ };
2781
+ TokenStorage.setStorageType(this.config.storageType || "localStorage");
2782
+ if (this.config.enableLogging) {
2783
+ }
2784
+ if (this.config.autoRestore) {
2785
+ await this.restoreSession();
2786
+ }
2787
+ this.initialized = true;
2788
+ this.log("SessionManager initialized successfully");
2789
+ }
2790
+ /**
2791
+ * Login con persistencia automática
2792
+ */
2793
+ async login(email, password) {
2794
+ try {
2795
+ this.log("Attempting login...");
2796
+ const response = await crudify6.login(email, password);
2797
+ if (!response.success) {
2798
+ this.log("Login failed:", response.errors);
2799
+ return {
2800
+ success: false,
2801
+ error: this.formatError(response.errors)
2802
+ };
2803
+ }
2804
+ const tokens = {
2805
+ accessToken: response.data.token,
2806
+ refreshToken: response.data.refreshToken,
2807
+ expiresAt: response.data.expiresAt,
2808
+ refreshExpiresAt: response.data.refreshExpiresAt
2809
+ };
2810
+ TokenStorage.saveTokens(tokens);
2811
+ this.log("Login successful, tokens saved");
2812
+ this.config.onLoginSuccess?.(tokens);
2813
+ return {
2814
+ success: true,
2815
+ tokens
2816
+ };
2817
+ } catch (error) {
2818
+ this.log("Login error:", error);
2819
+ return {
2820
+ success: false,
2821
+ error: error instanceof Error ? error.message : "Unknown error"
2822
+ };
2823
+ }
2824
+ }
2825
+ /**
2826
+ * Logout con limpieza de tokens
2827
+ */
2828
+ async logout() {
2829
+ try {
2830
+ this.log("Logging out...");
2831
+ await crudify6.logout();
2832
+ TokenStorage.clearTokens();
2833
+ this.log("Logout successful");
2834
+ this.config.onLogout?.();
2835
+ } catch (error) {
2836
+ this.log("Logout error:", error);
2837
+ TokenStorage.clearTokens();
2838
+ }
2839
+ }
2840
+ /**
2841
+ * Restaurar sesión desde storage
2842
+ */
2843
+ async restoreSession() {
2844
+ try {
2845
+ this.log("Attempting to restore session...");
2846
+ const savedTokens = TokenStorage.getTokens();
2847
+ if (!savedTokens) {
2848
+ this.log("No valid tokens found in storage");
2849
+ return false;
2850
+ }
2851
+ crudify6.setTokens({
2852
+ accessToken: savedTokens.accessToken,
2853
+ refreshToken: savedTokens.refreshToken,
2854
+ expiresAt: savedTokens.expiresAt,
2855
+ refreshExpiresAt: savedTokens.refreshExpiresAt
2856
+ });
2857
+ this.log("Session restored successfully");
2858
+ this.config.onSessionRestored?.(savedTokens);
2859
+ return true;
2860
+ } catch (error) {
2861
+ this.log("Session restore error:", error);
2862
+ TokenStorage.clearTokens();
2863
+ return false;
2864
+ }
2865
+ }
2866
+ /**
2867
+ * Verificar si el usuario está autenticado
2868
+ */
2869
+ isAuthenticated() {
2870
+ return crudify6.isLogin() || TokenStorage.hasValidTokens();
2871
+ }
2872
+ /**
2873
+ * Obtener información de tokens actuales
2874
+ */
2875
+ getTokenInfo() {
2876
+ const crudifyTokens = crudify6.getTokenData();
2877
+ const storageInfo = TokenStorage.getExpirationInfo();
2878
+ return {
2879
+ isLoggedIn: this.isAuthenticated(),
2880
+ crudifyTokens,
2881
+ storageInfo,
2882
+ hasValidTokens: TokenStorage.hasValidTokens()
2883
+ };
2884
+ }
2885
+ /**
2886
+ * Refrescar tokens manualmente
2887
+ */
2888
+ async refreshTokens() {
2889
+ try {
2890
+ this.log("Manually refreshing tokens...");
2891
+ const response = await crudify6.refreshAccessToken();
2892
+ if (!response.success) {
2893
+ this.log("Token refresh failed:", response.errors);
2894
+ TokenStorage.clearTokens();
2895
+ this.config.onSessionExpired?.();
2896
+ return false;
2897
+ }
2898
+ const newTokens = {
2899
+ accessToken: response.data.token,
2900
+ refreshToken: response.data.refreshToken,
2901
+ expiresAt: response.data.expiresAt,
2902
+ refreshExpiresAt: response.data.refreshExpiresAt
2903
+ };
2904
+ TokenStorage.saveTokens(newTokens);
2905
+ this.log("Tokens refreshed and saved successfully");
2906
+ return true;
2907
+ } catch (error) {
2908
+ this.log("Token refresh error:", error);
2909
+ TokenStorage.clearTokens();
2910
+ this.config.onSessionExpired?.();
2911
+ return false;
2912
+ }
2913
+ }
2914
+ /**
2915
+ * Configurar interceptor de respuesta para manejo automático de errores
2916
+ */
2917
+ setupResponseInterceptor() {
2918
+ crudify6.setResponseInterceptor(async (response) => {
2919
+ if (response.errors) {
2920
+ const hasAuthError = response.errors.some(
2921
+ (error) => error.message?.includes("Unauthorized") || error.message?.includes("Token") || error.extensions?.code === "UNAUTHENTICATED"
2922
+ );
2923
+ if (hasAuthError && TokenStorage.hasValidTokens()) {
2924
+ this.log("Auth error detected, attempting token refresh...");
2925
+ const refreshSuccess = await this.refreshTokens();
2926
+ if (!refreshSuccess) {
2927
+ this.log("Session expired, triggering callback");
2928
+ this.config.onSessionExpired?.();
2929
+ }
2930
+ }
2931
+ }
2932
+ return response;
2933
+ });
2934
+ this.log("Response interceptor configured");
2935
+ }
2936
+ /**
2937
+ * Limpiar sesión completamente
2938
+ */
2939
+ clearSession() {
2940
+ TokenStorage.clearTokens();
2941
+ crudify6.logout();
2942
+ this.log("Session cleared completely");
2943
+ }
2944
+ // Métodos privados
2945
+ log(message, ...args) {
2946
+ if (this.config.enableLogging) {
2947
+ console.log(`[SessionManager] ${message}`, ...args);
2948
+ }
2949
+ }
2950
+ formatError(errors) {
2951
+ if (!errors) return "Unknown error";
2952
+ if (typeof errors === "string") return errors;
2953
+ if (typeof errors === "object") {
2954
+ const errorMessages = Object.values(errors).flat();
2955
+ return errorMessages.join(", ");
2956
+ }
2957
+ return "Authentication failed";
2958
+ }
2959
+ };
2960
+
2961
+ // src/hooks/useSession.ts
2962
+ import { useState as useState9, useEffect as useEffect9, useCallback as useCallback5 } from "react";
2963
+ function useSession(options = {}) {
2964
+ const [state, setState] = useState9({
2965
+ isAuthenticated: false,
2966
+ isLoading: true,
2967
+ isInitialized: false,
2968
+ tokens: null,
2969
+ error: null
2970
+ });
2971
+ const sessionManager = SessionManager.getInstance();
2972
+ const initialize = useCallback5(async () => {
2973
+ try {
2974
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
2975
+ const config = {
2976
+ autoRestore: options.autoRestore ?? true,
2977
+ enableLogging: options.enableLogging ?? false,
2978
+ onSessionExpired: () => {
2979
+ setState((prev) => ({
2980
+ ...prev,
2981
+ isAuthenticated: false,
2982
+ tokens: null,
2983
+ error: "Session expired"
2984
+ }));
2985
+ options.onSessionExpired?.();
2986
+ },
2987
+ onSessionRestored: (tokens) => {
2988
+ setState((prev) => ({
2989
+ ...prev,
2990
+ isAuthenticated: true,
2991
+ tokens,
2992
+ error: null
2993
+ }));
2994
+ options.onSessionRestored?.(tokens);
2995
+ },
2996
+ onLoginSuccess: (tokens) => {
2997
+ setState((prev) => ({
2998
+ ...prev,
2999
+ isAuthenticated: true,
3000
+ tokens,
3001
+ error: null
3002
+ }));
3003
+ },
3004
+ onLogout: () => {
3005
+ setState((prev) => ({
3006
+ ...prev,
3007
+ isAuthenticated: false,
3008
+ tokens: null,
3009
+ error: null
3010
+ }));
3011
+ }
3012
+ };
3013
+ await sessionManager.initialize(config);
3014
+ sessionManager.setupResponseInterceptor();
3015
+ const isAuth = sessionManager.isAuthenticated();
3016
+ const tokenInfo = sessionManager.getTokenInfo();
3017
+ setState((prev) => ({
3018
+ ...prev,
3019
+ isAuthenticated: isAuth,
3020
+ isInitialized: true,
3021
+ isLoading: false,
3022
+ tokens: tokenInfo.crudifyTokens.accessToken ? {
3023
+ accessToken: tokenInfo.crudifyTokens.accessToken,
3024
+ refreshToken: tokenInfo.crudifyTokens.refreshToken,
3025
+ expiresAt: tokenInfo.crudifyTokens.expiresAt,
3026
+ refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
3027
+ } : null
3028
+ }));
3029
+ } catch (error) {
3030
+ setState((prev) => ({
3031
+ ...prev,
3032
+ isLoading: false,
3033
+ isInitialized: true,
3034
+ error: error instanceof Error ? error.message : "Initialization failed"
3035
+ }));
3036
+ }
3037
+ }, [options.autoRestore, options.enableLogging, options.onSessionExpired, options.onSessionRestored]);
3038
+ const login = useCallback5(async (email, password) => {
3039
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3040
+ try {
3041
+ const result = await sessionManager.login(email, password);
3042
+ if (result.success && result.tokens) {
3043
+ setState((prev) => ({
3044
+ ...prev,
3045
+ isAuthenticated: true,
3046
+ tokens: result.tokens,
3047
+ isLoading: false,
3048
+ error: null
3049
+ }));
3050
+ } else {
3051
+ setState((prev) => ({
3052
+ ...prev,
3053
+ isAuthenticated: false,
3054
+ tokens: null,
3055
+ isLoading: false,
3056
+ error: result.error || "Login failed"
3057
+ }));
3058
+ }
3059
+ return result;
3060
+ } catch (error) {
3061
+ const errorMsg = error instanceof Error ? error.message : "Login failed";
3062
+ setState((prev) => ({
3063
+ ...prev,
3064
+ isAuthenticated: false,
3065
+ tokens: null,
3066
+ isLoading: false,
3067
+ error: errorMsg
3068
+ }));
3069
+ return {
3070
+ success: false,
3071
+ error: errorMsg
3072
+ };
3073
+ }
3074
+ }, [sessionManager]);
3075
+ const logout = useCallback5(async () => {
3076
+ setState((prev) => ({ ...prev, isLoading: true }));
3077
+ try {
3078
+ await sessionManager.logout();
3079
+ setState((prev) => ({
3080
+ ...prev,
3081
+ isAuthenticated: false,
3082
+ tokens: null,
3083
+ isLoading: false,
3084
+ error: null
3085
+ }));
3086
+ } catch (error) {
3087
+ setState((prev) => ({
3088
+ ...prev,
3089
+ isAuthenticated: false,
3090
+ tokens: null,
3091
+ isLoading: false,
3092
+ error: error instanceof Error ? error.message : "Logout error"
3093
+ }));
3094
+ }
3095
+ }, [sessionManager]);
3096
+ const refreshTokens = useCallback5(async () => {
3097
+ try {
3098
+ const success = await sessionManager.refreshTokens();
3099
+ if (success) {
3100
+ const tokenInfo = sessionManager.getTokenInfo();
3101
+ setState((prev) => ({
3102
+ ...prev,
3103
+ tokens: tokenInfo.crudifyTokens.accessToken ? {
3104
+ accessToken: tokenInfo.crudifyTokens.accessToken,
3105
+ refreshToken: tokenInfo.crudifyTokens.refreshToken,
3106
+ expiresAt: tokenInfo.crudifyTokens.expiresAt,
3107
+ refreshExpiresAt: tokenInfo.crudifyTokens.refreshExpiresAt
3108
+ } : null,
3109
+ error: null
3110
+ }));
3111
+ } else {
3112
+ setState((prev) => ({
3113
+ ...prev,
3114
+ isAuthenticated: false,
3115
+ tokens: null,
3116
+ error: "Token refresh failed"
3117
+ }));
3118
+ }
3119
+ return success;
3120
+ } catch (error) {
3121
+ setState((prev) => ({
3122
+ ...prev,
3123
+ isAuthenticated: false,
3124
+ tokens: null,
3125
+ error: error instanceof Error ? error.message : "Token refresh failed"
3126
+ }));
3127
+ return false;
3128
+ }
3129
+ }, [sessionManager]);
3130
+ const clearError = useCallback5(() => {
3131
+ setState((prev) => ({ ...prev, error: null }));
3132
+ }, []);
3133
+ const getTokenInfo = useCallback5(() => {
3134
+ return sessionManager.getTokenInfo();
3135
+ }, [sessionManager]);
3136
+ useEffect9(() => {
3137
+ initialize();
3138
+ }, [initialize]);
3139
+ return {
3140
+ // Estado
3141
+ ...state,
3142
+ // Acciones
3143
+ login,
3144
+ logout,
3145
+ refreshTokens,
3146
+ clearError,
3147
+ getTokenInfo,
3148
+ // Utilidades
3149
+ isExpiringSoon: state.tokens ? state.tokens.expiresAt - Date.now() < 5 * 60 * 1e3 : false,
3150
+ // 5 minutos
3151
+ expiresIn: state.tokens ? Math.max(0, state.tokens.expiresAt - Date.now()) : 0,
3152
+ refreshExpiresIn: state.tokens ? Math.max(0, state.tokens.refreshExpiresAt - Date.now()) : 0
3153
+ };
3154
+ }
3155
+
3156
+ // src/providers/SessionProvider.tsx
3157
+ import { createContext as createContext4, useContext as useContext4, useMemo as useMemo3 } from "react";
3158
+ import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
3159
+ var SessionContext = createContext4(void 0);
3160
+ function SessionProvider({ children, options = {} }) {
3161
+ const sessionHook = useSession(options);
3162
+ const sessionData = useMemo3(() => {
3163
+ if (!sessionHook.tokens?.accessToken || !sessionHook.isAuthenticated) {
3164
+ return null;
3165
+ }
3166
+ try {
3167
+ const decoded = decodeJwtSafely(sessionHook.tokens.accessToken);
3168
+ if (decoded && decoded.sub && decoded.email && decoded.subscriber) {
3169
+ const result = {
3170
+ _id: decoded.sub,
3171
+ email: decoded.email,
3172
+ subscriberKey: decoded.subscriber
3173
+ };
3174
+ Object.keys(decoded).forEach((key) => {
3175
+ if (!["sub", "email", "subscriber"].includes(key)) {
3176
+ result[key] = decoded[key];
3177
+ }
3178
+ });
3179
+ return result;
3180
+ }
3181
+ } catch (error) {
3182
+ console.error("Error decoding JWT token for sessionData:", error);
3183
+ }
3184
+ return null;
3185
+ }, [sessionHook.tokens?.accessToken, sessionHook.isAuthenticated]);
3186
+ const contextValue = {
3187
+ ...sessionHook,
3188
+ sessionData
3189
+ };
3190
+ return /* @__PURE__ */ jsx11(SessionContext.Provider, { value: contextValue, children });
3191
+ }
3192
+ function useSessionContext() {
3193
+ const context = useContext4(SessionContext);
3194
+ if (context === void 0) {
3195
+ throw new Error("useSessionContext must be used within a SessionProvider");
3196
+ }
3197
+ return context;
3198
+ }
3199
+ function ProtectedRoute({
3200
+ children,
3201
+ fallback = /* @__PURE__ */ jsx11("div", { children: "Please log in to access this content" }),
3202
+ redirectTo
3203
+ }) {
3204
+ const { isAuthenticated, isLoading, isInitialized } = useSessionContext();
3205
+ if (!isInitialized || isLoading) {
3206
+ return /* @__PURE__ */ jsx11("div", { children: "Loading..." });
3207
+ }
3208
+ if (!isAuthenticated) {
3209
+ if (redirectTo) {
3210
+ redirectTo();
3211
+ return null;
3212
+ }
3213
+ return /* @__PURE__ */ jsx11(Fragment7, { children: fallback });
3214
+ }
3215
+ return /* @__PURE__ */ jsx11(Fragment7, { children });
3216
+ }
3217
+ function SessionDebugInfo() {
3218
+ const session = useSessionContext();
3219
+ if (!session.isInitialized) {
3220
+ return /* @__PURE__ */ jsx11("div", { children: "Session not initialized" });
3221
+ }
3222
+ return /* @__PURE__ */ jsxs8("div", { style: {
3223
+ padding: "10px",
3224
+ margin: "10px",
3225
+ border: "1px solid #ccc",
3226
+ borderRadius: "4px",
3227
+ fontSize: "12px",
3228
+ fontFamily: "monospace"
3229
+ }, children: [
3230
+ /* @__PURE__ */ jsx11("h4", { children: "Session Debug Info" }),
3231
+ /* @__PURE__ */ jsxs8("div", { children: [
3232
+ /* @__PURE__ */ jsx11("strong", { children: "Authenticated:" }),
3233
+ " ",
3234
+ session.isAuthenticated ? "Yes" : "No"
3235
+ ] }),
3236
+ /* @__PURE__ */ jsxs8("div", { children: [
3237
+ /* @__PURE__ */ jsx11("strong", { children: "Loading:" }),
3238
+ " ",
3239
+ session.isLoading ? "Yes" : "No"
3240
+ ] }),
3241
+ /* @__PURE__ */ jsxs8("div", { children: [
3242
+ /* @__PURE__ */ jsx11("strong", { children: "Error:" }),
3243
+ " ",
3244
+ session.error || "None"
3245
+ ] }),
3246
+ session.tokens && /* @__PURE__ */ jsxs8(Fragment7, { children: [
3247
+ /* @__PURE__ */ jsxs8("div", { children: [
3248
+ /* @__PURE__ */ jsx11("strong", { children: "Access Token:" }),
3249
+ " ",
3250
+ session.tokens.accessToken.substring(0, 20),
3251
+ "..."
3252
+ ] }),
3253
+ /* @__PURE__ */ jsxs8("div", { children: [
3254
+ /* @__PURE__ */ jsx11("strong", { children: "Refresh Token:" }),
3255
+ " ",
3256
+ session.tokens.refreshToken.substring(0, 20),
3257
+ "..."
3258
+ ] }),
3259
+ /* @__PURE__ */ jsxs8("div", { children: [
3260
+ /* @__PURE__ */ jsx11("strong", { children: "Access Expires In:" }),
3261
+ " ",
3262
+ Math.round(session.expiresIn / 1e3 / 60),
3263
+ " minutes"
3264
+ ] }),
3265
+ /* @__PURE__ */ jsxs8("div", { children: [
3266
+ /* @__PURE__ */ jsx11("strong", { children: "Refresh Expires In:" }),
3267
+ " ",
3268
+ Math.round(session.refreshExpiresIn / 1e3 / 60 / 60),
3269
+ " hours"
3270
+ ] }),
3271
+ /* @__PURE__ */ jsxs8("div", { children: [
3272
+ /* @__PURE__ */ jsx11("strong", { children: "Expiring Soon:" }),
3273
+ " ",
3274
+ session.isExpiringSoon ? "Yes" : "No"
3275
+ ] })
3276
+ ] })
3277
+ ] });
3278
+ }
3279
+
3280
+ // src/components/LoginComponent.tsx
3281
+ import { useState as useState10 } from "react";
3282
+ import { Button as Button5, TextField as TextField5, Box as Box8, Alert as Alert7, Typography as Typography8, CircularProgress as CircularProgress7 } from "@mui/material";
3283
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
3284
+ function LoginComponent() {
3285
+ const [email, setEmail] = useState10("");
3286
+ const [password, setPassword] = useState10("");
3287
+ const [showForm, setShowForm] = useState10(false);
3288
+ const {
3289
+ isAuthenticated,
3290
+ isLoading,
3291
+ error,
3292
+ login,
3293
+ logout,
3294
+ refreshTokens,
3295
+ clearError,
3296
+ isExpiringSoon,
3297
+ expiresIn
3298
+ } = useSessionContext();
3299
+ const handleLogin = async (e) => {
3300
+ e.preventDefault();
3301
+ if (!email || !password) {
3302
+ return;
3303
+ }
3304
+ const result = await login(email, password);
3305
+ if (result.success) {
3306
+ setEmail("");
3307
+ setPassword("");
3308
+ setShowForm(false);
3309
+ }
3310
+ };
3311
+ const handleLogout = async () => {
3312
+ await logout();
3313
+ };
3314
+ const handleRefreshTokens = async () => {
3315
+ await refreshTokens();
3316
+ };
3317
+ if (isAuthenticated) {
3318
+ return /* @__PURE__ */ jsxs9(Box8, { sx: { maxWidth: 600, mx: "auto", p: 3 }, children: [
3319
+ /* @__PURE__ */ jsx12(Typography8, { variant: "h4", gutterBottom: true, children: "Welcome! \u{1F389}" }),
3320
+ /* @__PURE__ */ jsx12(Alert7, { severity: "success", sx: { mb: 3 }, children: "You are successfully logged in with Refresh Token Pattern enabled" }),
3321
+ /* @__PURE__ */ jsxs9(Box8, { sx: { mb: 3, p: 2, bgcolor: "background.paper", border: 1, borderColor: "divider", borderRadius: 1 }, children: [
3322
+ /* @__PURE__ */ jsx12(Typography8, { variant: "h6", gutterBottom: true, children: "Token Status" }),
3323
+ /* @__PURE__ */ jsxs9(Typography8, { variant: "body2", color: "text.secondary", children: [
3324
+ "Access Token expires in: ",
3325
+ Math.round(expiresIn / 1e3 / 60),
3326
+ " minutes"
3327
+ ] }),
3328
+ isExpiringSoon && /* @__PURE__ */ jsx12(Alert7, { severity: "warning", sx: { mt: 1 }, children: "Token expires soon - automatic refresh will happen" })
3329
+ ] }),
3330
+ /* @__PURE__ */ jsxs9(Box8, { sx: { display: "flex", gap: 2, flexWrap: "wrap" }, children: [
3331
+ /* @__PURE__ */ jsx12(
3332
+ Button5,
3333
+ {
3334
+ variant: "contained",
3335
+ onClick: handleRefreshTokens,
3336
+ disabled: isLoading,
3337
+ startIcon: isLoading ? /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }) : null,
3338
+ children: "Refresh Tokens"
3339
+ }
3340
+ ),
3341
+ /* @__PURE__ */ jsx12(
3342
+ Button5,
3343
+ {
3344
+ variant: "outlined",
3345
+ color: "error",
3346
+ onClick: handleLogout,
3347
+ disabled: isLoading,
3348
+ children: "Logout"
3349
+ }
3350
+ )
3351
+ ] }),
3352
+ error && /* @__PURE__ */ jsx12(Alert7, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
3353
+ ] });
3354
+ }
3355
+ return /* @__PURE__ */ jsxs9(Box8, { sx: { maxWidth: 400, mx: "auto", p: 3 }, children: [
3356
+ /* @__PURE__ */ jsx12(Typography8, { variant: "h4", gutterBottom: true, align: "center", children: "Login with Refresh Tokens" }),
3357
+ /* @__PURE__ */ jsx12(Alert7, { severity: "info", sx: { mb: 3 }, children: "This demo shows the new Refresh Token Pattern with automatic session management" }),
3358
+ !showForm ? /* @__PURE__ */ jsx12(
3359
+ Button5,
3360
+ {
3361
+ fullWidth: true,
3362
+ variant: "contained",
3363
+ size: "large",
3364
+ onClick: () => setShowForm(true),
3365
+ sx: { mt: 2 },
3366
+ children: "Show Login Form"
3367
+ }
3368
+ ) : /* @__PURE__ */ jsxs9("form", { onSubmit: handleLogin, children: [
3369
+ /* @__PURE__ */ jsx12(
3370
+ TextField5,
3371
+ {
3372
+ fullWidth: true,
3373
+ label: "Email",
3374
+ type: "email",
3375
+ value: email,
3376
+ onChange: (e) => setEmail(e.target.value),
3377
+ margin: "normal",
3378
+ required: true,
3379
+ autoComplete: "email"
3380
+ }
3381
+ ),
3382
+ /* @__PURE__ */ jsx12(
3383
+ TextField5,
3384
+ {
3385
+ fullWidth: true,
3386
+ label: "Password",
3387
+ type: "password",
3388
+ value: password,
3389
+ onChange: (e) => setPassword(e.target.value),
3390
+ margin: "normal",
3391
+ required: true,
3392
+ autoComplete: "current-password"
3393
+ }
3394
+ ),
3395
+ /* @__PURE__ */ jsx12(
3396
+ Button5,
3397
+ {
3398
+ type: "submit",
3399
+ fullWidth: true,
3400
+ variant: "contained",
3401
+ size: "large",
3402
+ disabled: isLoading,
3403
+ startIcon: isLoading ? /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }) : null,
3404
+ sx: { mt: 3, mb: 2 },
3405
+ children: isLoading ? "Logging in..." : "Login"
3406
+ }
3407
+ )
3408
+ ] }),
3409
+ error && /* @__PURE__ */ jsx12(Alert7, { severity: "error", sx: { mt: 2 }, onClose: clearError, children: error })
3410
+ ] });
3411
+ }
3412
+ function SessionStatus() {
3413
+ const { isAuthenticated, isLoading, isExpiringSoon, expiresIn } = useSessionContext();
3414
+ if (isLoading) {
3415
+ return /* @__PURE__ */ jsxs9(Box8, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
3416
+ /* @__PURE__ */ jsx12(CircularProgress7, { size: 16 }),
3417
+ /* @__PURE__ */ jsx12(Typography8, { variant: "caption", children: "Loading session..." })
3418
+ ] });
3419
+ }
3420
+ if (!isAuthenticated) {
3421
+ return /* @__PURE__ */ jsx12(Typography8, { variant: "caption", color: "text.secondary", children: "Not logged in" });
3422
+ }
3423
+ return /* @__PURE__ */ jsxs9(Box8, { children: [
3424
+ /* @__PURE__ */ jsx12(Typography8, { variant: "caption", color: "success.main", children: "\u2713 Authenticated" }),
3425
+ isExpiringSoon && /* @__PURE__ */ jsxs9(Typography8, { variant: "caption", color: "warning.main", display: "block", children: [
3426
+ "\u26A0 Token expires in ",
3427
+ Math.round(expiresIn / 1e3 / 60),
3428
+ " min"
3429
+ ] })
3430
+ ] });
3431
+ }
3432
+
3433
+ // src/hooks/useUserData.ts
3434
+ import { useState as useState11, useEffect as useEffect10, useCallback as useCallback6, useRef as useRef4 } from "react";
3435
+ import crudify7 from "@nocios/crudify-browser";
3436
+ var useUserData = (options = {}) => {
3437
+ const { autoFetch = true, retryOnError = false, maxRetries = 3 } = options;
3438
+ const { isAuthenticated, isInitialized, sessionData, tokens } = useSessionContext();
3439
+ const [userData, setUserData] = useState11(null);
3440
+ const [loading, setLoading] = useState11(false);
3441
+ const [error, setError] = useState11(null);
3442
+ const abortControllerRef = useRef4(null);
3443
+ const mountedRef = useRef4(true);
3444
+ const requestIdRef = useRef4(0);
3445
+ const retryCountRef = useRef4(0);
3446
+ const getUserEmail = useCallback6(() => {
3447
+ if (!sessionData) return null;
3448
+ return sessionData.email || sessionData["cognito:username"] || null;
3449
+ }, [sessionData]);
3450
+ const clearProfile = useCallback6(() => {
3451
+ setUserData(null);
3452
+ setError(null);
3453
+ setLoading(false);
3454
+ retryCountRef.current = 0;
3455
+ }, []);
3456
+ const refreshProfile = useCallback6(async () => {
3457
+ const userEmail = getUserEmail();
3458
+ console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
3459
+ if (!userEmail) {
3460
+ if (mountedRef.current) {
3461
+ setError("No user email available from session data");
3462
+ setLoading(false);
3463
+ }
3464
+ return;
3465
+ }
3466
+ if (!isInitialized) {
3467
+ if (mountedRef.current) {
3468
+ setError("Session not initialized");
3469
+ setLoading(false);
3470
+ }
3471
+ return;
3472
+ }
3473
+ if (abortControllerRef.current) {
3474
+ abortControllerRef.current.abort();
3475
+ }
3476
+ const abortController = new AbortController();
3477
+ abortControllerRef.current = abortController;
3478
+ const currentRequestId = ++requestIdRef.current;
3479
+ try {
3480
+ if (mountedRef.current) {
3481
+ setLoading(true);
3482
+ setError(null);
3483
+ }
3484
+ console.log("\u{1F464} useUserData - Fetching profile data from database");
3485
+ const response = await crudify7.readItems("users", {
3486
+ filter: { email: userEmail },
3487
+ pagination: { limit: 1 }
3488
+ });
3489
+ console.log("\u{1F464} useUserData - Database response:", response);
3490
+ console.log("\u{1F464} useUserData - response.data:", response.data);
3491
+ console.log("\u{1F464} useUserData - response.data type:", typeof response.data);
3492
+ if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
3493
+ let userData2 = null;
3494
+ if (response.success) {
3495
+ console.log("\u{1F464} useUserData - Processing successful response:", {
3496
+ dataType: typeof response.data,
3497
+ isArray: Array.isArray(response.data),
3498
+ hasResponse: !!response.data?.response,
3499
+ hasResponseData: !!response.data?.response?.data,
3500
+ responseDataType: typeof response.data?.response?.data
3501
+ });
3502
+ if (Array.isArray(response.data) && response.data.length > 0) {
3503
+ console.log("\u{1F464} useUserData - Found direct array format");
3504
+ userData2 = response.data[0];
3505
+ } else if (response.data?.response?.data) {
3506
+ console.log("\u{1F464} useUserData - Found nested response.data format");
3507
+ try {
3508
+ const rawData = response.data.response.data;
3509
+ console.log("\u{1F464} useUserData - Raw nested data:", rawData);
3510
+ console.log("\u{1F464} useUserData - Raw data type:", typeof rawData);
3511
+ const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
3512
+ console.log("\u{1F464} useUserData - Parsed nested data:", parsedData);
3513
+ if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
3514
+ userData2 = parsedData.items[0];
3515
+ console.log("\u{1F464} useUserData - Extracted user from nested items:", userData2);
3516
+ } else {
3517
+ console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
3518
+ }
3519
+ } catch (parseError) {
3520
+ console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
3521
+ }
3522
+ } else if (response.data && typeof response.data === "object") {
3523
+ console.log("\u{1F464} useUserData - Found object format, checking for items");
3524
+ if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
3525
+ console.log("\u{1F464} useUserData - Found items in object format");
3526
+ userData2 = response.data.items[0];
3527
+ } else {
3528
+ console.log("\u{1F464} useUserData - No items found in object format");
3529
+ }
3530
+ } else if (response.data?.data?.response?.data) {
3531
+ console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
3532
+ try {
3533
+ const rawData = response.data.data.response.data;
3534
+ console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
3535
+ const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
3536
+ console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
3537
+ if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
3538
+ userData2 = parsedData.items[0];
3539
+ console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
3540
+ }
3541
+ } catch (parseError) {
3542
+ console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
3543
+ }
3544
+ }
3545
+ }
3546
+ if (userData2) {
3547
+ console.log("\u{1F464} useUserData - User data found:", userData2);
3548
+ setUserData(userData2);
3549
+ setError(null);
3550
+ retryCountRef.current = 0;
3551
+ console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
3552
+ } else {
3553
+ setError("User profile not found in database");
3554
+ setUserData(null);
3555
+ console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
3556
+ }
3557
+ }
3558
+ } catch (err) {
3559
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
3560
+ const error2 = err;
3561
+ console.error("\u{1F464} useUserData - Error fetching profile:", error2);
3562
+ if (error2.name === "AbortError") {
3563
+ return;
3564
+ }
3565
+ const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
3566
+ if (shouldRetry) {
3567
+ retryCountRef.current++;
3568
+ console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
3569
+ setTimeout(() => {
3570
+ if (mountedRef.current) {
3571
+ refreshProfile();
3572
+ }
3573
+ }, 1e3 * retryCountRef.current);
3574
+ } else {
3575
+ setError("Failed to load user profile from database");
3576
+ setUserData(null);
3577
+ }
3578
+ }
3579
+ } finally {
3580
+ if (currentRequestId === requestIdRef.current && mountedRef.current) {
3581
+ setLoading(false);
3582
+ }
3583
+ if (abortControllerRef.current === abortController) {
3584
+ abortControllerRef.current = null;
3585
+ }
3586
+ }
3587
+ }, [isInitialized, getUserEmail, retryOnError, maxRetries]);
3588
+ useEffect10(() => {
3589
+ if (autoFetch && isAuthenticated && isInitialized) {
3590
+ refreshProfile();
3591
+ } else if (!isAuthenticated) {
3592
+ clearProfile();
3593
+ }
3594
+ }, [autoFetch, isAuthenticated, isInitialized, refreshProfile, clearProfile]);
3595
+ useEffect10(() => {
3596
+ mountedRef.current = true;
3597
+ return () => {
3598
+ mountedRef.current = false;
3599
+ if (abortControllerRef.current) {
3600
+ abortControllerRef.current.abort();
3601
+ abortControllerRef.current = null;
3602
+ }
3603
+ };
3604
+ }, []);
3605
+ const user = {
3606
+ session: sessionData,
3607
+ // Usar sessionData del nuevo sistema
3608
+ data: userData
3609
+ // Mantener userData del database igual que legacy
3610
+ };
3611
+ return {
3612
+ user,
3613
+ loading,
3614
+ error,
3615
+ refreshProfile,
3616
+ clearProfile
3617
+ };
3618
+ };
3619
+
3620
+ // src/hooks/useAuth.ts
3621
+ import { useCallback as useCallback7 } from "react";
3622
+ var useAuth = () => {
3623
+ const {
3624
+ isAuthenticated,
3625
+ isLoading,
3626
+ isInitialized,
3627
+ tokens,
3628
+ error,
3629
+ sessionData,
3630
+ login,
3631
+ logout,
3632
+ refreshTokens,
3633
+ clearError,
3634
+ getTokenInfo,
3635
+ isExpiringSoon,
3636
+ expiresIn,
3637
+ refreshExpiresIn
3638
+ } = useSessionContext();
3639
+ const setToken = useCallback7((token) => {
3640
+ if (token) {
3641
+ console.warn("useAuth.setToken() is deprecated. Use login() method instead for better security.");
3642
+ } else {
3643
+ logout();
3644
+ }
3645
+ }, [logout]);
3646
+ const tokenExpiration = tokens?.expiresAt ? new Date(tokens.expiresAt) : null;
3647
+ return {
3648
+ // Estado básico (compatible con legacy)
3649
+ isAuthenticated,
3650
+ loading: isLoading,
3651
+ // En el nuevo sistema se llama isLoading
3652
+ error,
3653
+ // Datos del token (compatible con legacy + mejorado)
3654
+ token: tokens?.accessToken || null,
3655
+ user: sessionData,
3656
+ // sessionData del nuevo sistema (más rico que legacy)
3657
+ tokenExpiration,
3658
+ // Acciones (compatible con legacy + mejorado)
3659
+ setToken,
3660
+ logout,
3661
+ refreshToken: refreshTokens,
3662
+ // Funcionalidad real en el nuevo sistema
3663
+ // Nuevas funcionalidades del sistema de refresh tokens
3664
+ login,
3665
+ isExpiringSoon,
3666
+ expiresIn,
3667
+ refreshExpiresIn,
3668
+ getTokenInfo,
3669
+ clearError
3670
+ };
3671
+ };
3672
+
3673
+ // src/hooks/useData.ts
3674
+ import { useCallback as useCallback8 } from "react";
3675
+ import crudify8 from "@nocios/crudify-browser";
3676
+ var useData = () => {
3677
+ const {
3678
+ isInitialized,
3679
+ isLoading,
3680
+ error,
3681
+ isAuthenticated,
3682
+ login: sessionLogin
3683
+ } = useSessionContext();
3684
+ const isReady = useCallback8(() => {
3685
+ return isInitialized && !isLoading && !error;
3686
+ }, [isInitialized, isLoading, error]);
3687
+ const waitForReady = useCallback8(async () => {
3688
+ return new Promise((resolve, reject) => {
3689
+ const checkReady = () => {
3690
+ if (isReady()) {
3691
+ resolve();
3692
+ } else if (error) {
3693
+ reject(new Error(error));
3694
+ } else {
3695
+ setTimeout(checkReady, 100);
3696
+ }
3697
+ };
3698
+ checkReady();
3699
+ });
3700
+ }, [isReady, error]);
3701
+ const ensureReady = useCallback8(async () => {
3702
+ if (!isReady()) {
3703
+ throw new Error("System not ready. Check isInitialized, isLoading, and error states.");
3704
+ }
3705
+ }, [isReady]);
3706
+ const readItems = useCallback8(async (moduleKey, filter, options) => {
3707
+ await ensureReady();
3708
+ return await crudify8.readItems(moduleKey, filter || {}, options);
3709
+ }, [ensureReady]);
3710
+ const readItem = useCallback8(async (moduleKey, filter, options) => {
3711
+ await ensureReady();
3712
+ return await crudify8.readItem(moduleKey, filter, options);
3713
+ }, [ensureReady]);
3714
+ const createItem = useCallback8(async (moduleKey, data, options) => {
3715
+ await ensureReady();
3716
+ return await crudify8.createItem(moduleKey, data, options);
3717
+ }, [ensureReady]);
3718
+ const updateItem = useCallback8(async (moduleKey, data, options) => {
3719
+ await ensureReady();
3720
+ return await crudify8.updateItem(moduleKey, data, options);
3721
+ }, [ensureReady]);
3722
+ const deleteItem = useCallback8(async (moduleKey, id, options) => {
3723
+ await ensureReady();
3724
+ return await crudify8.deleteItem(moduleKey, id, options);
3725
+ }, [ensureReady]);
3726
+ const transaction = useCallback8(async (operations, options) => {
3727
+ await ensureReady();
3728
+ return await crudify8.transaction(operations, options);
3729
+ }, [ensureReady]);
3730
+ const login = useCallback8(async (email, password) => {
3731
+ try {
3732
+ const result = await sessionLogin(email, password);
3733
+ if (result.success) {
3734
+ return {
3735
+ success: true,
3736
+ data: result.tokens
3737
+ };
3738
+ } else {
3739
+ return {
3740
+ success: false,
3741
+ errors: result.error || "Login failed"
3742
+ };
3743
+ }
3744
+ } catch (error2) {
3745
+ return {
3746
+ success: false,
3747
+ errors: error2 instanceof Error ? error2.message : "Login failed"
3748
+ };
3749
+ }
3750
+ }, [sessionLogin]);
3751
+ return {
3752
+ // CRUD operations
3753
+ readItems,
3754
+ readItem,
3755
+ createItem,
3756
+ updateItem,
3757
+ deleteItem,
3758
+ transaction,
3759
+ login,
3760
+ // Estado
3761
+ isInitialized,
3762
+ isInitializing: isLoading,
3763
+ // El nuevo sistema usa isLoading
3764
+ initializationError: error,
3765
+ // Utilidades
3766
+ isReady,
3767
+ waitForReady
3768
+ };
3769
+ };
2584
3770
  export {
2585
3771
  CrudifyDataProvider,
2586
3772
  CrudifyLogin_default as CrudifyLogin,
2587
3773
  ERROR_CODES,
2588
3774
  ERROR_SEVERITY_MAP,
3775
+ LoginComponent,
3776
+ ProtectedRoute,
3777
+ SessionDebugInfo,
3778
+ SessionManager,
3779
+ SessionProvider,
3780
+ SessionStatus,
3781
+ TokenStorage,
2589
3782
  UserProfileDisplay_default as UserProfileDisplay,
2590
3783
  configurationManager,
2591
3784
  default2 as crudify,
@@ -2604,6 +3797,7 @@ export {
2604
3797
  secureLocalStorage,
2605
3798
  secureSessionStorage,
2606
3799
  tokenManager,
3800
+ useAuth,
2607
3801
  useCrudifyAuth,
2608
3802
  useCrudifyConfig,
2609
3803
  useCrudifyData,
@@ -2611,5 +3805,9 @@ export {
2611
3805
  useCrudifyInstance,
2612
3806
  useCrudifyLogin,
2613
3807
  useCrudifyUser,
3808
+ useData,
3809
+ useSession,
3810
+ useSessionContext,
3811
+ useUserData,
2614
3812
  useUserProfile
2615
3813
  };