@nocios/crudify-ui 3.0.60 → 3.0.64

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.d.mts CHANGED
@@ -163,15 +163,24 @@ type TokenData = {
163
163
  expiresAt: number;
164
164
  refreshExpiresAt: number;
165
165
  };
166
- type StorageType = 'localStorage' | 'sessionStorage' | 'none';
166
+ type StorageType = "localStorage" | "sessionStorage" | "none";
167
167
  declare class TokenStorage {
168
168
  private static readonly TOKEN_KEY;
169
- private static readonly ENCRYPTION_KEY;
169
+ private static readonly ENCRYPTION_KEY_STORAGE;
170
+ private static encryptionKey;
170
171
  private static storageType;
171
172
  /**
172
173
  * Configurar tipo de almacenamiento
173
174
  */
174
175
  static setStorageType(type: StorageType): void;
176
+ /**
177
+ * Generar clave de encriptación única y persistente
178
+ */
179
+ private static generateEncryptionKey;
180
+ /**
181
+ * Obtener o generar clave de encriptación
182
+ */
183
+ private static getEncryptionKey;
175
184
  /**
176
185
  * Verificar si el storage está disponible
177
186
  */
@@ -200,6 +209,10 @@ declare class TokenStorage {
200
209
  * Limpiar tokens almacenados
201
210
  */
202
211
  static clearTokens(): void;
212
+ /**
213
+ * Rotar clave de encriptación (limpia tokens existentes por seguridad)
214
+ */
215
+ static rotateEncryptionKey(): void;
203
216
  /**
204
217
  * Verificar si hay tokens válidos guardados
205
218
  */
@@ -399,6 +412,7 @@ type NotificationOptions = {
399
412
  vertical: "top" | "bottom";
400
413
  horizontal: "left" | "center" | "right";
401
414
  };
415
+ allowHtml?: boolean;
402
416
  };
403
417
  type SessionProviderProps = {
404
418
  children: ReactNode;
@@ -771,6 +785,7 @@ interface GlobalNotificationProviderProps {
771
785
  horizontal: "left" | "center" | "right";
772
786
  };
773
787
  enabled?: boolean;
788
+ allowHtml?: boolean;
774
789
  }
775
790
  declare const GlobalNotificationProvider: React.FC<GlobalNotificationProviderProps>;
776
791
  declare const useGlobalNotification: () => GlobalNotificationContextValue;
package/dist/index.d.ts CHANGED
@@ -163,15 +163,24 @@ type TokenData = {
163
163
  expiresAt: number;
164
164
  refreshExpiresAt: number;
165
165
  };
166
- type StorageType = 'localStorage' | 'sessionStorage' | 'none';
166
+ type StorageType = "localStorage" | "sessionStorage" | "none";
167
167
  declare class TokenStorage {
168
168
  private static readonly TOKEN_KEY;
169
- private static readonly ENCRYPTION_KEY;
169
+ private static readonly ENCRYPTION_KEY_STORAGE;
170
+ private static encryptionKey;
170
171
  private static storageType;
171
172
  /**
172
173
  * Configurar tipo de almacenamiento
173
174
  */
174
175
  static setStorageType(type: StorageType): void;
176
+ /**
177
+ * Generar clave de encriptación única y persistente
178
+ */
179
+ private static generateEncryptionKey;
180
+ /**
181
+ * Obtener o generar clave de encriptación
182
+ */
183
+ private static getEncryptionKey;
175
184
  /**
176
185
  * Verificar si el storage está disponible
177
186
  */
@@ -200,6 +209,10 @@ declare class TokenStorage {
200
209
  * Limpiar tokens almacenados
201
210
  */
202
211
  static clearTokens(): void;
212
+ /**
213
+ * Rotar clave de encriptación (limpia tokens existentes por seguridad)
214
+ */
215
+ static rotateEncryptionKey(): void;
203
216
  /**
204
217
  * Verificar si hay tokens válidos guardados
205
218
  */
@@ -399,6 +412,7 @@ type NotificationOptions = {
399
412
  vertical: "top" | "bottom";
400
413
  horizontal: "left" | "center" | "right";
401
414
  };
415
+ allowHtml?: boolean;
402
416
  };
403
417
  type SessionProviderProps = {
404
418
  children: ReactNode;
@@ -771,6 +785,7 @@ interface GlobalNotificationProviderProps {
771
785
  horizontal: "left" | "center" | "right";
772
786
  };
773
787
  enabled?: boolean;
788
+ allowHtml?: boolean;
774
789
  }
775
790
  declare const GlobalNotificationProvider: React.FC<GlobalNotificationProviderProps>;
776
791
  declare const useGlobalNotification: () => GlobalNotificationContextValue;
package/dist/index.js CHANGED
@@ -504,6 +504,47 @@ var _TokenStorage = class _TokenStorage {
504
504
  static setStorageType(type) {
505
505
  _TokenStorage.storageType = type;
506
506
  }
507
+ /**
508
+ * Generar clave de encriptación única y persistente
509
+ */
510
+ static generateEncryptionKey() {
511
+ const browserInfo = [
512
+ navigator.userAgent,
513
+ navigator.language,
514
+ navigator.platform,
515
+ screen.width,
516
+ screen.height,
517
+ Date.now().toString(),
518
+ Math.random().toString(36)
519
+ ].join("|");
520
+ return import_crypto_js.default.SHA256(browserInfo).toString();
521
+ }
522
+ /**
523
+ * Obtener o generar clave de encriptación
524
+ */
525
+ static getEncryptionKey() {
526
+ if (_TokenStorage.encryptionKey) {
527
+ return _TokenStorage.encryptionKey;
528
+ }
529
+ const storage = window.localStorage;
530
+ if (!storage) {
531
+ _TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
532
+ return _TokenStorage.encryptionKey;
533
+ }
534
+ try {
535
+ let existingKey = storage.getItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
536
+ if (!existingKey || existingKey.length < 32) {
537
+ existingKey = _TokenStorage.generateEncryptionKey();
538
+ storage.setItem(_TokenStorage.ENCRYPTION_KEY_STORAGE, existingKey);
539
+ }
540
+ _TokenStorage.encryptionKey = existingKey;
541
+ return existingKey;
542
+ } catch (error) {
543
+ console.warn("Crudify: Cannot persist encryption key, using temporary key");
544
+ _TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
545
+ return _TokenStorage.encryptionKey;
546
+ }
547
+ }
507
548
  /**
508
549
  * Verificar si el storage está disponible
509
550
  */
@@ -534,7 +575,8 @@ var _TokenStorage = class _TokenStorage {
534
575
  */
535
576
  static encrypt(data) {
536
577
  try {
537
- return import_crypto_js.default.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
578
+ const encryptionKey = _TokenStorage.getEncryptionKey();
579
+ return import_crypto_js.default.AES.encrypt(data, encryptionKey).toString();
538
580
  } catch (error) {
539
581
  console.error("Crudify: Encryption failed", error);
540
582
  return data;
@@ -545,7 +587,8 @@ var _TokenStorage = class _TokenStorage {
545
587
  */
546
588
  static decrypt(encryptedData) {
547
589
  try {
548
- const bytes = import_crypto_js.default.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
590
+ const encryptionKey = _TokenStorage.getEncryptionKey();
591
+ const bytes = import_crypto_js.default.AES.decrypt(encryptedData, encryptionKey);
549
592
  const decrypted = bytes.toString(import_crypto_js.default.enc.Utf8);
550
593
  return decrypted || encryptedData;
551
594
  } catch (error) {
@@ -620,6 +663,22 @@ var _TokenStorage = class _TokenStorage {
620
663
  console.error("Crudify: Failed to clear tokens", error);
621
664
  }
622
665
  }
666
+ /**
667
+ * Rotar clave de encriptación (limpia tokens existentes por seguridad)
668
+ */
669
+ static rotateEncryptionKey() {
670
+ try {
671
+ _TokenStorage.clearTokens();
672
+ _TokenStorage.encryptionKey = null;
673
+ const storage = window.localStorage;
674
+ if (storage) {
675
+ storage.removeItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
676
+ }
677
+ console.info("Crudify: Encryption key rotated successfully");
678
+ } catch (error) {
679
+ console.error("Crudify: Failed to rotate encryption key", error);
680
+ }
681
+ }
623
682
  /**
624
683
  * Verificar si hay tokens válidos guardados
625
684
  */
@@ -658,7 +717,8 @@ var _TokenStorage = class _TokenStorage {
658
717
  }
659
718
  };
660
719
  _TokenStorage.TOKEN_KEY = "crudify_tokens";
661
- _TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
720
+ _TokenStorage.ENCRYPTION_KEY_STORAGE = "crudify_enc_key";
721
+ _TokenStorage.encryptionKey = null;
662
722
  _TokenStorage.storageType = "localStorage";
663
723
  var TokenStorage = _TokenStorage;
664
724
 
@@ -1424,8 +1484,10 @@ var GlobalNotificationProvider = ({
1424
1484
  maxNotifications = 5,
1425
1485
  defaultAutoHideDuration = 6e3,
1426
1486
  position = { vertical: "top", horizontal: "right" },
1427
- enabled = false
1487
+ enabled = false,
1428
1488
  // ✅ Por defecto DESACTIVADO
1489
+ allowHtml = false
1490
+ // Por defecto no permitir HTML
1429
1491
  }) => {
1430
1492
  const [notifications, setNotifications] = (0, import_react6.useState)([]);
1431
1493
  const showNotification = (0, import_react6.useCallback)(
@@ -1448,8 +1510,8 @@ var GlobalNotificationProvider = ({
1448
1510
  severity,
1449
1511
  autoHideDuration: options?.autoHideDuration ?? defaultAutoHideDuration,
1450
1512
  persistent: options?.persistent ?? false,
1451
- allowHtml: options?.allowHtml ?? false
1452
- // Por defecto NO permitir HTML
1513
+ allowHtml: options?.allowHtml ?? allowHtml
1514
+ // Usar valor del provider por defecto
1453
1515
  };
1454
1516
  setNotifications((prev) => {
1455
1517
  const updatedNotifications = prev.length >= maxNotifications ? prev.slice(-(maxNotifications - 1)) : prev;
@@ -1457,7 +1519,7 @@ var GlobalNotificationProvider = ({
1457
1519
  });
1458
1520
  return id;
1459
1521
  },
1460
- [maxNotifications, defaultAutoHideDuration, enabled]
1522
+ [maxNotifications, defaultAutoHideDuration, enabled, allowHtml]
1461
1523
  );
1462
1524
  const hideNotification = (0, import_react6.useCallback)((id) => {
1463
1525
  setNotifications((prev) => prev.filter((notification) => notification.id !== id));
@@ -1569,15 +1631,17 @@ function InnerSessionProvider({
1569
1631
  showNotificationFn = showNotification;
1570
1632
  } catch {
1571
1633
  }
1572
- const enhancedOptions = import_react7.default.useMemo(() => ({
1573
- ...options,
1574
- showNotification: showNotificationFn,
1575
- // TODO: Agregar translateFn cuando esté disponible
1576
- onSessionExpired: () => {
1577
- console.log("\u{1F6A8} SessionProvider - Session expired callback triggered");
1578
- options.onSessionExpired?.();
1579
- }
1580
- }), [options, showNotificationFn]);
1634
+ const enhancedOptions = import_react7.default.useMemo(
1635
+ () => ({
1636
+ ...options,
1637
+ showNotification: showNotificationFn,
1638
+ // TODO: Agregar translateFn cuando esté disponible
1639
+ onSessionExpired: () => {
1640
+ options.onSessionExpired?.();
1641
+ }
1642
+ }),
1643
+ [options, showNotificationFn]
1644
+ );
1581
1645
  const sessionHook = useSession(enhancedOptions);
1582
1646
  const resolvedConfig = (0, import_react7.useMemo)(() => {
1583
1647
  let publicApiKey;
@@ -1626,12 +1690,6 @@ function InnerSessionProvider({
1626
1690
  loginActions = decodedActions.split(",").map((s) => s.trim()).filter(Boolean);
1627
1691
  }
1628
1692
  }
1629
- console.log("\u{1F50D} SessionProvider - Configuration Detection:");
1630
- console.log(" - Config source:", configSource);
1631
- console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
1632
- console.log(" - Environment:", env);
1633
- console.log(" - App name:", appName);
1634
- console.log(" - Login actions:", loginActions);
1635
1693
  return {
1636
1694
  publicApiKey,
1637
1695
  env,
@@ -1683,7 +1741,9 @@ function SessionProvider(props) {
1683
1741
  enabled: props.showNotifications,
1684
1742
  maxNotifications: props.notificationOptions?.maxNotifications || 5,
1685
1743
  defaultAutoHideDuration: props.notificationOptions?.defaultAutoHideDuration || 6e3,
1686
- position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" }
1744
+ position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" },
1745
+ allowHtml: props.notificationOptions?.allowHtml || false
1746
+ // Pasar allowHtml desde notificationOptions
1687
1747
  };
1688
1748
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GlobalNotificationProvider, { ...notificationConfig, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InnerSessionProvider, { ...props }) });
1689
1749
  }
@@ -2057,11 +2117,13 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
2057
2117
  const { crudify: crudify7 } = useCrudify();
2058
2118
  const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
2059
2119
  const { login: sessionLogin } = useSessionContext();
2060
- const { t, i18n } = useTranslation();
2120
+ const translationContext = useTranslation();
2121
+ const { t } = translationContext;
2122
+ const i18n = translationContext.i18n;
2061
2123
  const usernameInputRef = (0, import_react8.useRef)(null);
2062
2124
  const errorTranslator = createErrorTranslator(t, {
2063
2125
  currentLanguage: i18n?.language,
2064
- enableDebug: process.env.NODE_ENV === "development"
2126
+ enableDebug: false
2065
2127
  });
2066
2128
  const getRedirectUrl = () => {
2067
2129
  if (state.searchParams.redirect) {
@@ -4035,7 +4097,6 @@ var useUserData = (options = {}) => {
4035
4097
  }, []);
4036
4098
  const refreshProfile = (0, import_react18.useCallback)(async () => {
4037
4099
  const userEmail = getUserEmail();
4038
- console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
4039
4100
  if (!userEmail) {
4040
4101
  if (mountedRef.current) {
4041
4102
  setError("No user email available from session data");
@@ -4061,91 +4122,59 @@ var useUserData = (options = {}) => {
4061
4122
  setLoading(true);
4062
4123
  setError(null);
4063
4124
  }
4064
- console.log("\u{1F464} useUserData - Fetching profile data from database");
4065
4125
  const response = await import_crudify_browser4.default.readItems("users", {
4066
4126
  filter: { email: userEmail },
4067
4127
  pagination: { limit: 1 }
4068
4128
  });
4069
- console.log("\u{1F464} useUserData - Database response:", response);
4070
- console.log("\u{1F464} useUserData - response.data:", response.data);
4071
- console.log("\u{1F464} useUserData - response.data type:", typeof response.data);
4072
4129
  if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
4073
4130
  let userData2 = null;
4074
4131
  if (response.success) {
4075
- console.log("\u{1F464} useUserData - Processing successful response:", {
4076
- dataType: typeof response.data,
4077
- isArray: Array.isArray(response.data),
4078
- hasResponse: !!response.data?.response,
4079
- hasResponseData: !!response.data?.response?.data,
4080
- responseDataType: typeof response.data?.response?.data
4081
- });
4082
4132
  if (Array.isArray(response.data) && response.data.length > 0) {
4083
- console.log("\u{1F464} useUserData - Found direct array format");
4084
4133
  userData2 = response.data[0];
4085
4134
  } else if (response.data?.response?.data) {
4086
- console.log("\u{1F464} useUserData - Found nested response.data format");
4087
4135
  try {
4088
4136
  const rawData = response.data.response.data;
4089
- console.log("\u{1F464} useUserData - Raw nested data:", rawData);
4090
- console.log("\u{1F464} useUserData - Raw data type:", typeof rawData);
4091
4137
  const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
4092
- console.log("\u{1F464} useUserData - Parsed nested data:", parsedData);
4093
4138
  if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
4094
4139
  userData2 = parsedData.items[0];
4095
- console.log("\u{1F464} useUserData - Extracted user from nested items:", userData2);
4096
4140
  } else {
4097
- console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
4098
4141
  }
4099
4142
  } catch (parseError) {
4100
- console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
4101
4143
  }
4102
4144
  } else if (response.data && typeof response.data === "object") {
4103
- console.log("\u{1F464} useUserData - Found object format, checking for items");
4104
4145
  if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
4105
- console.log("\u{1F464} useUserData - Found items in object format");
4106
4146
  userData2 = response.data.items[0];
4107
4147
  } else {
4108
- console.log("\u{1F464} useUserData - No items found in object format");
4109
4148
  }
4110
4149
  } else if (response.data?.data?.response?.data) {
4111
- console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
4112
4150
  try {
4113
4151
  const rawData = response.data.data.response.data;
4114
- console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
4115
4152
  const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
4116
- console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
4117
4153
  if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
4118
4154
  userData2 = parsedData.items[0];
4119
- console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
4120
4155
  }
4121
4156
  } catch (parseError) {
4122
- console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
4123
4157
  }
4124
4158
  }
4125
4159
  }
4126
4160
  if (userData2) {
4127
- console.log("\u{1F464} useUserData - User data found:", userData2);
4128
4161
  setUserData(userData2);
4129
4162
  setError(null);
4130
4163
  retryCountRef.current = 0;
4131
- console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
4132
4164
  } else {
4133
4165
  setError("User profile not found in database");
4134
4166
  setUserData(null);
4135
- console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
4136
4167
  }
4137
4168
  }
4138
4169
  } catch (err) {
4139
4170
  if (currentRequestId === requestIdRef.current && mountedRef.current) {
4140
4171
  const error2 = err;
4141
- console.error("\u{1F464} useUserData - Error fetching profile:", error2);
4142
4172
  if (error2.name === "AbortError") {
4143
4173
  return;
4144
4174
  }
4145
4175
  const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
4146
4176
  if (shouldRetry) {
4147
4177
  retryCountRef.current++;
4148
- console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
4149
4178
  setTimeout(() => {
4150
4179
  if (mountedRef.current) {
4151
4180
  refreshProfile();
package/dist/index.mjs CHANGED
@@ -431,6 +431,47 @@ var _TokenStorage = class _TokenStorage {
431
431
  static setStorageType(type) {
432
432
  _TokenStorage.storageType = type;
433
433
  }
434
+ /**
435
+ * Generar clave de encriptación única y persistente
436
+ */
437
+ static generateEncryptionKey() {
438
+ const browserInfo = [
439
+ navigator.userAgent,
440
+ navigator.language,
441
+ navigator.platform,
442
+ screen.width,
443
+ screen.height,
444
+ Date.now().toString(),
445
+ Math.random().toString(36)
446
+ ].join("|");
447
+ return CryptoJS.SHA256(browserInfo).toString();
448
+ }
449
+ /**
450
+ * Obtener o generar clave de encriptación
451
+ */
452
+ static getEncryptionKey() {
453
+ if (_TokenStorage.encryptionKey) {
454
+ return _TokenStorage.encryptionKey;
455
+ }
456
+ const storage = window.localStorage;
457
+ if (!storage) {
458
+ _TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
459
+ return _TokenStorage.encryptionKey;
460
+ }
461
+ try {
462
+ let existingKey = storage.getItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
463
+ if (!existingKey || existingKey.length < 32) {
464
+ existingKey = _TokenStorage.generateEncryptionKey();
465
+ storage.setItem(_TokenStorage.ENCRYPTION_KEY_STORAGE, existingKey);
466
+ }
467
+ _TokenStorage.encryptionKey = existingKey;
468
+ return existingKey;
469
+ } catch (error) {
470
+ console.warn("Crudify: Cannot persist encryption key, using temporary key");
471
+ _TokenStorage.encryptionKey = _TokenStorage.generateEncryptionKey();
472
+ return _TokenStorage.encryptionKey;
473
+ }
474
+ }
434
475
  /**
435
476
  * Verificar si el storage está disponible
436
477
  */
@@ -461,7 +502,8 @@ var _TokenStorage = class _TokenStorage {
461
502
  */
462
503
  static encrypt(data) {
463
504
  try {
464
- return CryptoJS.AES.encrypt(data, _TokenStorage.ENCRYPTION_KEY).toString();
505
+ const encryptionKey = _TokenStorage.getEncryptionKey();
506
+ return CryptoJS.AES.encrypt(data, encryptionKey).toString();
465
507
  } catch (error) {
466
508
  console.error("Crudify: Encryption failed", error);
467
509
  return data;
@@ -472,7 +514,8 @@ var _TokenStorage = class _TokenStorage {
472
514
  */
473
515
  static decrypt(encryptedData) {
474
516
  try {
475
- const bytes = CryptoJS.AES.decrypt(encryptedData, _TokenStorage.ENCRYPTION_KEY);
517
+ const encryptionKey = _TokenStorage.getEncryptionKey();
518
+ const bytes = CryptoJS.AES.decrypt(encryptedData, encryptionKey);
476
519
  const decrypted = bytes.toString(CryptoJS.enc.Utf8);
477
520
  return decrypted || encryptedData;
478
521
  } catch (error) {
@@ -547,6 +590,22 @@ var _TokenStorage = class _TokenStorage {
547
590
  console.error("Crudify: Failed to clear tokens", error);
548
591
  }
549
592
  }
593
+ /**
594
+ * Rotar clave de encriptación (limpia tokens existentes por seguridad)
595
+ */
596
+ static rotateEncryptionKey() {
597
+ try {
598
+ _TokenStorage.clearTokens();
599
+ _TokenStorage.encryptionKey = null;
600
+ const storage = window.localStorage;
601
+ if (storage) {
602
+ storage.removeItem(_TokenStorage.ENCRYPTION_KEY_STORAGE);
603
+ }
604
+ console.info("Crudify: Encryption key rotated successfully");
605
+ } catch (error) {
606
+ console.error("Crudify: Failed to rotate encryption key", error);
607
+ }
608
+ }
550
609
  /**
551
610
  * Verificar si hay tokens válidos guardados
552
611
  */
@@ -585,7 +644,8 @@ var _TokenStorage = class _TokenStorage {
585
644
  }
586
645
  };
587
646
  _TokenStorage.TOKEN_KEY = "crudify_tokens";
588
- _TokenStorage.ENCRYPTION_KEY = "crudify_secure_key_v1";
647
+ _TokenStorage.ENCRYPTION_KEY_STORAGE = "crudify_enc_key";
648
+ _TokenStorage.encryptionKey = null;
589
649
  _TokenStorage.storageType = "localStorage";
590
650
  var TokenStorage = _TokenStorage;
591
651
 
@@ -1351,8 +1411,10 @@ var GlobalNotificationProvider = ({
1351
1411
  maxNotifications = 5,
1352
1412
  defaultAutoHideDuration = 6e3,
1353
1413
  position = { vertical: "top", horizontal: "right" },
1354
- enabled = false
1414
+ enabled = false,
1355
1415
  // ✅ Por defecto DESACTIVADO
1416
+ allowHtml = false
1417
+ // Por defecto no permitir HTML
1356
1418
  }) => {
1357
1419
  const [notifications, setNotifications] = useState4([]);
1358
1420
  const showNotification = useCallback2(
@@ -1375,8 +1437,8 @@ var GlobalNotificationProvider = ({
1375
1437
  severity,
1376
1438
  autoHideDuration: options?.autoHideDuration ?? defaultAutoHideDuration,
1377
1439
  persistent: options?.persistent ?? false,
1378
- allowHtml: options?.allowHtml ?? false
1379
- // Por defecto NO permitir HTML
1440
+ allowHtml: options?.allowHtml ?? allowHtml
1441
+ // Usar valor del provider por defecto
1380
1442
  };
1381
1443
  setNotifications((prev) => {
1382
1444
  const updatedNotifications = prev.length >= maxNotifications ? prev.slice(-(maxNotifications - 1)) : prev;
@@ -1384,7 +1446,7 @@ var GlobalNotificationProvider = ({
1384
1446
  });
1385
1447
  return id;
1386
1448
  },
1387
- [maxNotifications, defaultAutoHideDuration, enabled]
1449
+ [maxNotifications, defaultAutoHideDuration, enabled, allowHtml]
1388
1450
  );
1389
1451
  const hideNotification = useCallback2((id) => {
1390
1452
  setNotifications((prev) => prev.filter((notification) => notification.id !== id));
@@ -1496,15 +1558,17 @@ function InnerSessionProvider({
1496
1558
  showNotificationFn = showNotification;
1497
1559
  } catch {
1498
1560
  }
1499
- const enhancedOptions = React5.useMemo(() => ({
1500
- ...options,
1501
- showNotification: showNotificationFn,
1502
- // TODO: Agregar translateFn cuando esté disponible
1503
- onSessionExpired: () => {
1504
- console.log("\u{1F6A8} SessionProvider - Session expired callback triggered");
1505
- options.onSessionExpired?.();
1506
- }
1507
- }), [options, showNotificationFn]);
1561
+ const enhancedOptions = React5.useMemo(
1562
+ () => ({
1563
+ ...options,
1564
+ showNotification: showNotificationFn,
1565
+ // TODO: Agregar translateFn cuando esté disponible
1566
+ onSessionExpired: () => {
1567
+ options.onSessionExpired?.();
1568
+ }
1569
+ }),
1570
+ [options, showNotificationFn]
1571
+ );
1508
1572
  const sessionHook = useSession(enhancedOptions);
1509
1573
  const resolvedConfig = useMemo2(() => {
1510
1574
  let publicApiKey;
@@ -1553,12 +1617,6 @@ function InnerSessionProvider({
1553
1617
  loginActions = decodedActions.split(",").map((s) => s.trim()).filter(Boolean);
1554
1618
  }
1555
1619
  }
1556
- console.log("\u{1F50D} SessionProvider - Configuration Detection:");
1557
- console.log(" - Config source:", configSource);
1558
- console.log(" - PublicApiKey:", publicApiKey ? publicApiKey.substring(0, 15) + "..." : "undefined");
1559
- console.log(" - Environment:", env);
1560
- console.log(" - App name:", appName);
1561
- console.log(" - Login actions:", loginActions);
1562
1620
  return {
1563
1621
  publicApiKey,
1564
1622
  env,
@@ -1610,7 +1668,9 @@ function SessionProvider(props) {
1610
1668
  enabled: props.showNotifications,
1611
1669
  maxNotifications: props.notificationOptions?.maxNotifications || 5,
1612
1670
  defaultAutoHideDuration: props.notificationOptions?.defaultAutoHideDuration || 6e3,
1613
- position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" }
1671
+ position: props.notificationOptions?.position || { vertical: "top", horizontal: "right" },
1672
+ allowHtml: props.notificationOptions?.allowHtml || false
1673
+ // Pasar allowHtml desde notificationOptions
1614
1674
  };
1615
1675
  return /* @__PURE__ */ jsx5(GlobalNotificationProvider, { ...notificationConfig, children: /* @__PURE__ */ jsx5(InnerSessionProvider, { ...props }) });
1616
1676
  }
@@ -1984,11 +2044,13 @@ var LoginForm = ({ onScreenChange, onExternalNavigate, onLoginSuccess, onError,
1984
2044
  const { crudify: crudify7 } = useCrudify();
1985
2045
  const { state, updateFormData, setFieldError, clearErrors, setLoading } = useLoginState();
1986
2046
  const { login: sessionLogin } = useSessionContext();
1987
- const { t, i18n } = useTranslation();
2047
+ const translationContext = useTranslation();
2048
+ const { t } = translationContext;
2049
+ const i18n = translationContext.i18n;
1988
2050
  const usernameInputRef = useRef(null);
1989
2051
  const errorTranslator = createErrorTranslator(t, {
1990
2052
  currentLanguage: i18n?.language,
1991
- enableDebug: process.env.NODE_ENV === "development"
2053
+ enableDebug: false
1992
2054
  });
1993
2055
  const getRedirectUrl = () => {
1994
2056
  if (state.searchParams.redirect) {
@@ -4021,7 +4083,6 @@ var useUserData = (options = {}) => {
4021
4083
  }, []);
4022
4084
  const refreshProfile = useCallback4(async () => {
4023
4085
  const userEmail = getUserEmail();
4024
- console.log("\u{1F464} useUserData - Refreshing profile for:", userEmail);
4025
4086
  if (!userEmail) {
4026
4087
  if (mountedRef.current) {
4027
4088
  setError("No user email available from session data");
@@ -4047,91 +4108,59 @@ var useUserData = (options = {}) => {
4047
4108
  setLoading(true);
4048
4109
  setError(null);
4049
4110
  }
4050
- console.log("\u{1F464} useUserData - Fetching profile data from database");
4051
4111
  const response = await crudify4.readItems("users", {
4052
4112
  filter: { email: userEmail },
4053
4113
  pagination: { limit: 1 }
4054
4114
  });
4055
- console.log("\u{1F464} useUserData - Database response:", response);
4056
- console.log("\u{1F464} useUserData - response.data:", response.data);
4057
- console.log("\u{1F464} useUserData - response.data type:", typeof response.data);
4058
4115
  if (currentRequestId === requestIdRef.current && mountedRef.current && !abortController.signal.aborted) {
4059
4116
  let userData2 = null;
4060
4117
  if (response.success) {
4061
- console.log("\u{1F464} useUserData - Processing successful response:", {
4062
- dataType: typeof response.data,
4063
- isArray: Array.isArray(response.data),
4064
- hasResponse: !!response.data?.response,
4065
- hasResponseData: !!response.data?.response?.data,
4066
- responseDataType: typeof response.data?.response?.data
4067
- });
4068
4118
  if (Array.isArray(response.data) && response.data.length > 0) {
4069
- console.log("\u{1F464} useUserData - Found direct array format");
4070
4119
  userData2 = response.data[0];
4071
4120
  } else if (response.data?.response?.data) {
4072
- console.log("\u{1F464} useUserData - Found nested response.data format");
4073
4121
  try {
4074
4122
  const rawData = response.data.response.data;
4075
- console.log("\u{1F464} useUserData - Raw nested data:", rawData);
4076
- console.log("\u{1F464} useUserData - Raw data type:", typeof rawData);
4077
4123
  const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
4078
- console.log("\u{1F464} useUserData - Parsed nested data:", parsedData);
4079
4124
  if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
4080
4125
  userData2 = parsedData.items[0];
4081
- console.log("\u{1F464} useUserData - Extracted user from nested items:", userData2);
4082
4126
  } else {
4083
- console.log("\u{1F464} useUserData - No items found in parsed data or items array is empty");
4084
4127
  }
4085
4128
  } catch (parseError) {
4086
- console.error("\u{1F464} useUserData - Error parsing nested response data:", parseError);
4087
4129
  }
4088
4130
  } else if (response.data && typeof response.data === "object") {
4089
- console.log("\u{1F464} useUserData - Found object format, checking for items");
4090
4131
  if (response.data.items && Array.isArray(response.data.items) && response.data.items.length > 0) {
4091
- console.log("\u{1F464} useUserData - Found items in object format");
4092
4132
  userData2 = response.data.items[0];
4093
4133
  } else {
4094
- console.log("\u{1F464} useUserData - No items found in object format");
4095
4134
  }
4096
4135
  } else if (response.data?.data?.response?.data) {
4097
- console.log("\u{1F464} useUserData - Found double-nested data.data.response.data format");
4098
4136
  try {
4099
4137
  const rawData = response.data.data.response.data;
4100
- console.log("\u{1F464} useUserData - Raw double-nested data:", rawData);
4101
4138
  const parsedData = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
4102
- console.log("\u{1F464} useUserData - Parsed double-nested data:", parsedData);
4103
4139
  if (parsedData && parsedData.items && Array.isArray(parsedData.items) && parsedData.items.length > 0) {
4104
4140
  userData2 = parsedData.items[0];
4105
- console.log("\u{1F464} useUserData - Extracted user from double-nested items:", userData2);
4106
4141
  }
4107
4142
  } catch (parseError) {
4108
- console.error("\u{1F464} useUserData - Error parsing double-nested response data:", parseError);
4109
4143
  }
4110
4144
  }
4111
4145
  }
4112
4146
  if (userData2) {
4113
- console.log("\u{1F464} useUserData - User data found:", userData2);
4114
4147
  setUserData(userData2);
4115
4148
  setError(null);
4116
4149
  retryCountRef.current = 0;
4117
- console.log("\u{1F464} useUserData - Profile loaded successfully:", userData2);
4118
4150
  } else {
4119
4151
  setError("User profile not found in database");
4120
4152
  setUserData(null);
4121
- console.warn("\u{1F464} useUserData - User not found for email:", userEmail);
4122
4153
  }
4123
4154
  }
4124
4155
  } catch (err) {
4125
4156
  if (currentRequestId === requestIdRef.current && mountedRef.current) {
4126
4157
  const error2 = err;
4127
- console.error("\u{1F464} useUserData - Error fetching profile:", error2);
4128
4158
  if (error2.name === "AbortError") {
4129
4159
  return;
4130
4160
  }
4131
4161
  const shouldRetry = retryOnError && retryCountRef.current < maxRetries && (error2.message?.includes("Network Error") || error2.message?.includes("Failed to fetch"));
4132
4162
  if (shouldRetry) {
4133
4163
  retryCountRef.current++;
4134
- console.log(`\u{1F464} useUserData - Retrying profile fetch (${retryCountRef.current}/${maxRetries})`);
4135
4164
  setTimeout(() => {
4136
4165
  if (mountedRef.current) {
4137
4166
  refreshProfile();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocios/crudify-ui",
3
- "version": "3.0.60",
3
+ "version": "3.0.64",
4
4
  "description": "Biblioteca de componentes UI para Crudify",
5
5
  "author": "Nocios",
6
6
  "license": "MIT",
@@ -25,7 +25,7 @@
25
25
  "@mui/icons-material": "^7.1.0",
26
26
  "@mui/material": "^7.1.0",
27
27
  "@mui/x-data-grid": "^8.5.1",
28
- "@nocios/crudify-browser": "^2.0.6",
28
+ "@nocios/crudify-browser": "^2.0.62",
29
29
  "@types/dompurify": "^3.0.5",
30
30
  "@types/uuid": "^10.0.0",
31
31
  "crypto-js": "^4.2.0",