@bantis/local-cipher 2.2.0 → 2.3.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/angular.js CHANGED
@@ -23,6 +23,9 @@ const DEFAULT_CONFIG = {
23
23
  compressionThreshold: 1024,
24
24
  autoCleanup: true,
25
25
  cleanupInterval: 60000,
26
+ enableCache: true,
27
+ verifyIntegrityOnRead: false,
28
+ storageEngine: typeof window !== 'undefined' ? window.localStorage : {},
26
29
  },
27
30
  debug: {
28
31
  enabled: false,
@@ -50,6 +53,8 @@ class EncryptionHelper {
50
53
  this.baseKey = '';
51
54
  this.baseKeyPromise = null;
52
55
  this.keyVersion = 1;
56
+ // Cache para optimización de rendimiento
57
+ this.keyNameCache = new Map();
53
58
  this.config = { ...DEFAULT_CONFIG.encryption, ...config };
54
59
  // Load key version from storage
55
60
  const storedVersion = localStorage.getItem(EncryptionHelper.KEY_VERSION_KEY);
@@ -219,10 +224,16 @@ class EncryptionHelper {
219
224
  * @returns Nombre encriptado con prefijo __enc_
220
225
  */
221
226
  async encryptKey(keyName) {
227
+ // Optimización: devolver desde cache si existe
228
+ if (this.keyNameCache.has(keyName)) {
229
+ return this.keyNameCache.get(keyName);
230
+ }
222
231
  const baseKey = await this.generateBaseKey();
223
232
  const combined = keyName + baseKey;
224
233
  const hash = await this.hashString(combined);
225
- return `__enc_${hash.substring(0, 16)}`;
234
+ const encryptedKey = `__enc_${hash.substring(0, 16)}`;
235
+ this.keyNameCache.set(keyName, encryptedKey);
236
+ return encryptedKey;
226
237
  }
227
238
  /**
228
239
  * Limpia todos los datos encriptados del localStorage
@@ -240,10 +251,11 @@ class EncryptionHelper {
240
251
  keysToRemove.forEach(key => localStorage.removeItem(key));
241
252
  // Eliminar salt
242
253
  localStorage.removeItem(EncryptionHelper.SALT_STORAGE_KEY);
243
- // Resetear clave en memoria
254
+ // Resetear clave en memoria y cache
244
255
  this.key = null;
245
256
  this.baseKey = '';
246
257
  this.baseKeyPromise = null;
258
+ this.keyNameCache.clear();
247
259
  }
248
260
  /**
249
261
  * Verifica si el navegador soporta Web Crypto API
@@ -683,6 +695,7 @@ function shouldCompress(data, threshold = 1024) {
683
695
  class SecureStorage {
684
696
  constructor(config) {
685
697
  this.cleanupInterval = null;
698
+ this.memoryCache = new Map();
686
699
  // Merge config with defaults
687
700
  this.config = {
688
701
  encryption: { ...DEFAULT_CONFIG.encryption, ...config?.encryption },
@@ -764,8 +777,12 @@ class SecureStorage {
764
777
  const encryptedKey = await this.encryptionHelper.encryptKey(key);
765
778
  // Encrypt the value
766
779
  const encryptedValue = await this.encryptionHelper.encrypt(serialized);
767
- // Store in localStorage
768
- localStorage.setItem(encryptedKey, encryptedValue);
780
+ // Store in storage
781
+ this.config.storage.storageEngine.setItem(encryptedKey, encryptedValue);
782
+ // Update cache
783
+ if (this.config.storage.enableCache) {
784
+ this.memoryCache.set(key, { value });
785
+ }
769
786
  this.logger.verbose(`Stored key: ${key}, compressed: ${compressed}, size: ${encryptedValue.length}`);
770
787
  this.eventEmitter.emit('encrypted', { key, metadata: { compressed, size: encryptedValue.length } });
771
788
  this.logger.timeEnd(`setItem:${key}`);
@@ -773,7 +790,7 @@ class SecureStorage {
773
790
  catch (error) {
774
791
  this.logger.error(`Error al guardar dato encriptado para ${key}:`, error);
775
792
  this.eventEmitter.emit('error', { key, error: error });
776
- localStorage.setItem(key, value);
793
+ this.config.storage.storageEngine.setItem(key, value);
777
794
  }
778
795
  }
779
796
  /**
@@ -826,8 +843,12 @@ class SecureStorage {
826
843
  const encryptedKey = await this.encryptionHelper.encryptKey(key);
827
844
  // Encrypt the value
828
845
  const encryptedValue = await this.encryptionHelper.encrypt(serialized);
829
- // Store in localStorage
830
- localStorage.setItem(encryptedKey, encryptedValue);
846
+ // Store in storage
847
+ this.config.storage.storageEngine.setItem(encryptedKey, encryptedValue);
848
+ // Update cache
849
+ if (this.config.storage.enableCache) {
850
+ this.memoryCache.set(key, { value, expiresAt });
851
+ }
831
852
  this.logger.verbose(`Stored key with expiry: ${key}, expiresAt: ${expiresAt}`);
832
853
  this.eventEmitter.emit('encrypted', { key, metadata: { compressed, expiresAt } });
833
854
  this.logger.timeEnd(`setItemWithExpiry:${key}`);
@@ -844,16 +865,31 @@ class SecureStorage {
844
865
  async getItem(key) {
845
866
  this.logger.time(`getItem:${key}`);
846
867
  if (!EncryptionHelper.isSupported()) {
847
- return localStorage.getItem(key);
868
+ return this.config.storage.storageEngine.getItem(key);
848
869
  }
849
870
  try {
871
+ // Check memory cache first
872
+ if (this.config.storage.enableCache && this.memoryCache.has(key)) {
873
+ const cached = this.memoryCache.get(key);
874
+ if (cached.expiresAt && cached.expiresAt < Date.now()) {
875
+ this.logger.info(`Key expired in cache: ${key}`);
876
+ await this.removeItem(key);
877
+ this.eventEmitter.emit('expired', { key });
878
+ this.logger.timeEnd(`getItem:${key}`);
879
+ return null;
880
+ }
881
+ this.logger.debug(`Retrieved from cache: ${key}`);
882
+ this.eventEmitter.emit('decrypted', { key });
883
+ this.logger.timeEnd(`getItem:${key}`);
884
+ return cached.value;
885
+ }
850
886
  // Encrypt the key
851
887
  const encryptedKey = await this.encryptionHelper.encryptKey(key);
852
888
  // Get encrypted value
853
- let encryptedValue = localStorage.getItem(encryptedKey);
889
+ let encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
854
890
  // Backward compatibility: try with plain key
855
891
  if (!encryptedValue) {
856
- encryptedValue = localStorage.getItem(key);
892
+ encryptedValue = this.config.storage.storageEngine.getItem(key);
857
893
  if (!encryptedValue) {
858
894
  this.logger.timeEnd(`getItem:${key}`);
859
895
  return null;
@@ -889,8 +925,8 @@ class SecureStorage {
889
925
  this.logger.timeEnd(`getItem:${key}`);
890
926
  return null;
891
927
  }
892
- // Verify integrity if checksum exists
893
- if (storedValue.checksum) {
928
+ // Verify integrity if checksum exists and configured
929
+ if (storedValue.checksum && this.config.storage.verifyIntegrityOnRead) {
894
930
  const calculatedChecksum = await this.calculateChecksum(storedValue.value);
895
931
  if (calculatedChecksum !== storedValue.checksum) {
896
932
  this.logger.warn(`Integrity check failed for key: ${key}`);
@@ -908,6 +944,9 @@ class SecureStorage {
908
944
  finalValue = await decompress(new Uint8Array(compressedData));
909
945
  this.eventEmitter.emit('decompressed', { key });
910
946
  }
947
+ if (this.config.storage.enableCache) {
948
+ this.memoryCache.set(key, { value: finalValue, expiresAt: storedValue.expiresAt });
949
+ }
911
950
  this.eventEmitter.emit('decrypted', { key });
912
951
  this.logger.timeEnd(`getItem:${key}`);
913
952
  return finalValue;
@@ -916,7 +955,7 @@ class SecureStorage {
916
955
  this.logger.error(`Error al recuperar dato encriptado para ${key}:`, error);
917
956
  this.eventEmitter.emit('error', { key, error: error });
918
957
  // Fallback: try plain key
919
- const fallback = localStorage.getItem(key);
958
+ const fallback = this.config.storage.storageEngine.getItem(key);
920
959
  this.logger.timeEnd(`getItem:${key}`);
921
960
  return fallback;
922
961
  }
@@ -926,19 +965,20 @@ class SecureStorage {
926
965
  */
927
966
  async removeItem(key) {
928
967
  if (!EncryptionHelper.isSupported()) {
929
- localStorage.removeItem(key);
968
+ this.config.storage.storageEngine.removeItem(key);
930
969
  return;
931
970
  }
932
971
  try {
933
972
  const encryptedKey = await this.encryptionHelper.encryptKey(key);
934
- localStorage.removeItem(encryptedKey);
935
- localStorage.removeItem(key); // Remove both versions
973
+ this.config.storage.storageEngine.removeItem(encryptedKey);
974
+ this.config.storage.storageEngine.removeItem(key); // Remove both versions
975
+ this.memoryCache.delete(key);
936
976
  this.eventEmitter.emit('deleted', { key });
937
977
  this.logger.info(`Removed key: ${key}`);
938
978
  }
939
979
  catch (error) {
940
980
  this.logger.error(`Error al eliminar dato para ${key}:`, error);
941
- localStorage.removeItem(key);
981
+ this.config.storage.storageEngine.removeItem(key);
942
982
  }
943
983
  }
944
984
  /**
@@ -953,6 +993,7 @@ class SecureStorage {
953
993
  */
954
994
  clear() {
955
995
  this.encryptionHelper.clearEncryptedData();
996
+ this.memoryCache.clear();
956
997
  this.eventEmitter.emit('cleared', {});
957
998
  this.logger.info('All encrypted data cleared');
958
999
  }
@@ -963,21 +1004,28 @@ class SecureStorage {
963
1004
  this.logger.info('Starting cleanup of expired items...');
964
1005
  let cleanedCount = 0;
965
1006
  const keysToCheck = [];
966
- for (let i = 0; i < localStorage.length; i++) {
967
- const key = localStorage.key(i);
1007
+ for (let i = 0; i < this.config.storage.storageEngine.length; i++) {
1008
+ const key = this.config.storage.storageEngine.key(i);
968
1009
  if (key && key.startsWith('__enc_')) {
969
1010
  keysToCheck.push(key);
970
1011
  }
971
1012
  }
972
1013
  for (const encryptedKey of keysToCheck) {
973
1014
  try {
974
- const encryptedValue = localStorage.getItem(encryptedKey);
1015
+ const encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
975
1016
  if (!encryptedValue)
976
1017
  continue;
977
1018
  const decrypted = await this.encryptionHelper.decrypt(encryptedValue);
978
1019
  const storedValue = JSON.parse(decrypted);
979
1020
  if (storedValue.expiresAt && storedValue.expiresAt < Date.now()) {
980
- localStorage.removeItem(encryptedKey);
1021
+ this.config.storage.storageEngine.removeItem(encryptedKey);
1022
+ // Encontrar la clave original en el cache y eliminarla
1023
+ for (const [cacheKey] of Array.from(this.memoryCache.entries())) {
1024
+ this.encryptionHelper.encryptKey(cacheKey).then(enc => {
1025
+ if (enc === encryptedKey)
1026
+ this.memoryCache.delete(cacheKey);
1027
+ });
1028
+ }
981
1029
  cleanedCount++;
982
1030
  this.eventEmitter.emit('expired', { key: encryptedKey });
983
1031
  }
@@ -995,7 +1043,7 @@ class SecureStorage {
995
1043
  async verifyIntegrity(key) {
996
1044
  try {
997
1045
  const encryptedKey = await this.encryptionHelper.encryptKey(key);
998
- const encryptedValue = localStorage.getItem(encryptedKey);
1046
+ const encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
999
1047
  if (!encryptedValue)
1000
1048
  return false;
1001
1049
  const decrypted = await this.encryptionHelper.decrypt(encryptedValue);
@@ -1018,7 +1066,7 @@ class SecureStorage {
1018
1066
  async getIntegrityInfo(key) {
1019
1067
  try {
1020
1068
  const encryptedKey = await this.encryptionHelper.encryptKey(key);
1021
- const encryptedValue = localStorage.getItem(encryptedKey);
1069
+ const encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
1022
1070
  if (!encryptedValue)
1023
1071
  return null;
1024
1072
  const decrypted = await this.encryptionHelper.decrypt(encryptedValue);
@@ -1101,7 +1149,7 @@ class SecureStorage {
1101
1149
  this.logger.info(`Iniciando migración de ${keys.length} claves...`);
1102
1150
  for (const key of keys) {
1103
1151
  try {
1104
- const value = localStorage.getItem(key);
1152
+ const value = this.config.storage.storageEngine.getItem(key);
1105
1153
  if (value === null)
1106
1154
  continue;
1107
1155
  // Try to decrypt to check if already encrypted
@@ -1114,7 +1162,7 @@ class SecureStorage {
1114
1162
  // Not encrypted, proceed with migration
1115
1163
  }
1116
1164
  await this.setItem(key, value);
1117
- localStorage.removeItem(key);
1165
+ this.config.storage.storageEngine.removeItem(key);
1118
1166
  this.logger.info(`✓ ${key} migrado exitosamente`);
1119
1167
  }
1120
1168
  catch (error) {
@@ -1128,8 +1176,8 @@ class SecureStorage {
1128
1176
  */
1129
1177
  getDebugInfo() {
1130
1178
  const allKeys = [];
1131
- for (let i = 0; i < localStorage.length; i++) {
1132
- const key = localStorage.key(i);
1179
+ for (let i = 0; i < this.config.storage.storageEngine.length; i++) {
1180
+ const key = this.config.storage.storageEngine.key(i);
1133
1181
  if (key)
1134
1182
  allKeys.push(key);
1135
1183
  }
@@ -1168,6 +1216,194 @@ class SecureStorage {
1168
1216
  }
1169
1217
  SecureStorage.instance = null;
1170
1218
 
1219
+ /**
1220
+ * SecureCookie - API de alto nivel para almacenamiento en cookies cifradas
1221
+ * Soporta opciones de cookies incluyendo domininios/subdominios y compresión.
1222
+ */
1223
+ class SecureCookie {
1224
+ constructor(config) {
1225
+ this.config = {
1226
+ encryption: { ...DEFAULT_CONFIG.encryption, ...config?.encryption },
1227
+ cookieOptions: { path: '/', ...config?.cookieOptions },
1228
+ compression: config?.compression ?? true,
1229
+ debug: { ...DEFAULT_CONFIG.debug, ...config?.debug }
1230
+ };
1231
+ this.logger = new Logger(this.config.debug);
1232
+ this.encryptionHelper = new EncryptionHelper(this.config.encryption);
1233
+ this.logger.info('SecureCookie initialized', this.config);
1234
+ }
1235
+ /**
1236
+ * Obtiene la instancia singleton de SecureCookie
1237
+ */
1238
+ static getInstance(config) {
1239
+ if (!SecureCookie.instance) {
1240
+ SecureCookie.instance = new SecureCookie(config);
1241
+ }
1242
+ return SecureCookie.instance;
1243
+ }
1244
+ /**
1245
+ * Serializa las opciones de cookie en un string
1246
+ */
1247
+ serializeOptions(options) {
1248
+ let cookieString = '';
1249
+ if (options.expires) {
1250
+ cookieString += `; expires=${options.expires.toUTCString()}`;
1251
+ }
1252
+ if (options.maxAge) {
1253
+ cookieString += `; max-age=${options.maxAge}`;
1254
+ }
1255
+ if (options.domain) {
1256
+ cookieString += `; domain=${options.domain}`;
1257
+ }
1258
+ if (options.path) {
1259
+ cookieString += `; path=${options.path}`;
1260
+ }
1261
+ if (options.secure) {
1262
+ cookieString += `; secure`;
1263
+ }
1264
+ if (options.sameSite) {
1265
+ cookieString += `; samesite=${options.sameSite}`;
1266
+ }
1267
+ return cookieString;
1268
+ }
1269
+ /**
1270
+ * Guarda un valor encriptado en una cookie
1271
+ */
1272
+ async set(name, value, options) {
1273
+ this.logger.time(`cookie:set:${name}`);
1274
+ if (!EncryptionHelper.isSupported()) {
1275
+ this.logger.warn('Web Crypto API no soportada, guardando cookie sin cifrar');
1276
+ const mergedOptions = { ...this.config.cookieOptions, ...options };
1277
+ document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${this.serializeOptions(mergedOptions)}`;
1278
+ return;
1279
+ }
1280
+ try {
1281
+ // Check compression (aggressively compress cookies to save space since max is ~4KB)
1282
+ const shouldCompressData = this.config.compression && shouldCompress(value, 200);
1283
+ let processedValue = value;
1284
+ let metadataPrefix = 'P_'; // P_ = Plain
1285
+ if (shouldCompressData) {
1286
+ this.logger.debug(`Compressing cookie value for: ${name}`);
1287
+ const compressedData = await compress(value);
1288
+ processedValue = this.encryptionHelper['arrayBufferToBase64'](compressedData.buffer);
1289
+ metadataPrefix = 'C_'; // C_ = Compressed
1290
+ }
1291
+ // Encrypt key name
1292
+ const encryptedKey = await this.encryptionHelper.encryptKey(name);
1293
+ // Serialize and Encrypt Value
1294
+ // Adding metadata prefix to avoid bloating value with massive StoredValue JSON headers
1295
+ const encryptedData = await this.encryptionHelper.encrypt(processedValue);
1296
+ const finalValue = metadataPrefix + encryptedData;
1297
+ // Prepare Cookie string
1298
+ const mergedOptions = { ...this.config.cookieOptions, ...options };
1299
+ const cookieOptionsStr = this.serializeOptions(mergedOptions);
1300
+ const cookieString = `${encodeURIComponent(encryptedKey)}=${encodeURIComponent(finalValue)}${cookieOptionsStr}`;
1301
+ // Verificación de tamaño de cookie
1302
+ if (cookieString.length > 4096) {
1303
+ this.logger.warn(`Cookie '${name}' es muy grande (${cookieString.length} bytes). Puede ser rechazada por el navegador.`);
1304
+ }
1305
+ document.cookie = cookieString;
1306
+ this.logger.verbose(`Stored cookie: ${name}`);
1307
+ this.logger.timeEnd(`cookie:set:${name}`);
1308
+ }
1309
+ catch (error) {
1310
+ this.logger.error(`Error al guardar cookie encriptada para ${name}:`, error);
1311
+ // Fallback sin cifrar como último recurso
1312
+ const mergedOptions = { ...this.config.cookieOptions, ...options };
1313
+ document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${this.serializeOptions(mergedOptions)}`;
1314
+ }
1315
+ }
1316
+ /**
1317
+ * Pide una cookie por nombre
1318
+ * Retorna el string o null si no existe
1319
+ */
1320
+ getRawCookie(name) {
1321
+ const nameEQ = encodeURIComponent(name) + '=';
1322
+ const ca = document.cookie.split(';');
1323
+ for (let i = 0; i < ca.length; i++) {
1324
+ let c = ca[i];
1325
+ while (c.charAt(0) === ' ')
1326
+ c = c.substring(1, c.length);
1327
+ if (c.indexOf(nameEQ) === 0)
1328
+ return decodeURIComponent(c.substring(nameEQ.length, c.length));
1329
+ }
1330
+ return null;
1331
+ }
1332
+ /**
1333
+ * Recupera y desencripta un valor de cookie
1334
+ */
1335
+ async get(name) {
1336
+ this.logger.time(`cookie:get:${name}`);
1337
+ if (!EncryptionHelper.isSupported()) {
1338
+ return this.getRawCookie(name);
1339
+ }
1340
+ try {
1341
+ // Find encrypted key
1342
+ const encryptedKey = await this.encryptionHelper.encryptKey(name);
1343
+ let rawValue = this.getRawCookie(encryptedKey);
1344
+ if (!rawValue) {
1345
+ // Backward compatibility just in case fallback was used
1346
+ rawValue = this.getRawCookie(name);
1347
+ if (!rawValue) {
1348
+ this.logger.timeEnd(`cookie:get:${name}`);
1349
+ return null;
1350
+ }
1351
+ }
1352
+ // Check if it has our metadata prefixes
1353
+ if (!rawValue.startsWith('P_') && !rawValue.startsWith('C_')) {
1354
+ // Podría ser un fallback plano
1355
+ this.logger.timeEnd(`cookie:get:${name}`);
1356
+ return rawValue; // Asumimos que es plano
1357
+ }
1358
+ const isCompressed = rawValue.startsWith('C_');
1359
+ const encryptedData = rawValue.substring(2);
1360
+ // Decrypt
1361
+ const decryptedString = await this.encryptionHelper.decrypt(encryptedData);
1362
+ // Decompress if needed
1363
+ let finalValue = decryptedString;
1364
+ if (isCompressed) {
1365
+ const compressedBuffer = this.encryptionHelper['base64ToArrayBuffer'](decryptedString);
1366
+ finalValue = await decompress(new Uint8Array(compressedBuffer));
1367
+ }
1368
+ this.logger.timeEnd(`cookie:get:${name}`);
1369
+ return finalValue;
1370
+ }
1371
+ catch (error) {
1372
+ this.logger.error(`Error al recuperar cookie encriptada para ${name}:`, error);
1373
+ // Fallback
1374
+ return this.getRawCookie(name);
1375
+ }
1376
+ }
1377
+ /**
1378
+ * Elimina una cookie
1379
+ */
1380
+ async remove(name, options) {
1381
+ const mergedOptions = { ...this.config.cookieOptions, ...options };
1382
+ // Expirar la cookie configurándole una fecha del pasado
1383
+ const expireOptions = {
1384
+ ...mergedOptions,
1385
+ expires: new Date(0),
1386
+ maxAge: 0
1387
+ };
1388
+ if (!EncryptionHelper.isSupported()) {
1389
+ document.cookie = `${encodeURIComponent(name)}=${this.serializeOptions(expireOptions)}`;
1390
+ return;
1391
+ }
1392
+ const encryptedKey = await this.encryptionHelper.encryptKey(name);
1393
+ // Remove encrypted cookie
1394
+ document.cookie = `${encodeURIComponent(encryptedKey)}=${this.serializeOptions(expireOptions)}`;
1395
+ // Also remove plain version just in case
1396
+ document.cookie = `${encodeURIComponent(name)}=${this.serializeOptions(expireOptions)}`;
1397
+ }
1398
+ /**
1399
+ * Limpia la instancia actual (útil para testing o refresco)
1400
+ */
1401
+ destroy() {
1402
+ SecureCookie.instance = null;
1403
+ }
1404
+ }
1405
+ SecureCookie.instance = null;
1406
+
1171
1407
  const secureStorage$1 = SecureStorage.getInstance();
1172
1408
  /**
1173
1409
  * Función de debug para verificar el estado del sistema de encriptación
@@ -1220,6 +1456,202 @@ async function forceMigration(customKeys) {
1220
1456
  await debugEncryptionState();
1221
1457
  }
1222
1458
 
1459
+ /**
1460
+ * PlainStorage - Una interfaz consistente con SecureStorage pero sin cifrado.
1461
+ * Envuelve localStorage o sessionStorage con serialización JSON.
1462
+ */
1463
+ class PlainStorage {
1464
+ constructor(storageEngine = typeof window !== 'undefined' ? window.localStorage : {}) {
1465
+ this.storage = storageEngine;
1466
+ this.logger = new Logger({ prefix: 'PlainStorage' });
1467
+ }
1468
+ async set(key, value) {
1469
+ try {
1470
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
1471
+ this.storage.setItem(key, serialized);
1472
+ }
1473
+ catch (error) {
1474
+ this.logger.error(`Error saving unencrypted item ${key}:`, error);
1475
+ }
1476
+ }
1477
+ async get(key) {
1478
+ try {
1479
+ const value = this.storage.getItem(key);
1480
+ if (value === null)
1481
+ return null;
1482
+ try {
1483
+ return JSON.parse(value);
1484
+ }
1485
+ catch {
1486
+ return value; // Fallback for plain strings
1487
+ }
1488
+ }
1489
+ catch (error) {
1490
+ this.logger.error(`Error reading unencrypted item ${key}:`, error);
1491
+ return null;
1492
+ }
1493
+ }
1494
+ async remove(key) {
1495
+ this.storage.removeItem(key);
1496
+ }
1497
+ clear() {
1498
+ this.storage.clear();
1499
+ }
1500
+ }
1501
+
1502
+ /**
1503
+ * Crea una instancia de almacenamiento encriptado.
1504
+ * @param engine 'localStorage' o 'sessionStorage'
1505
+ * @param secretKey Llave secreta opcional para derivación de claves
1506
+ * @param config Configuración adicional opcional
1507
+ * @returns Instancia de SecureStorage
1508
+ */
1509
+ function createEncryptedStorage(engine = 'localStorage', secretKey, config) {
1510
+ const defaultEngine = typeof window !== 'undefined'
1511
+ ? (engine === 'sessionStorage' ? window.sessionStorage : window.localStorage)
1512
+ : {};
1513
+ const mergedConfig = {
1514
+ ...config,
1515
+ encryption: {
1516
+ ...config?.encryption,
1517
+ ...(secretKey ? { appIdentifier: secretKey } : {})
1518
+ },
1519
+ storage: {
1520
+ ...config?.storage,
1521
+ storageEngine: defaultEngine
1522
+ }
1523
+ };
1524
+ return SecureStorage.getInstance(mergedConfig);
1525
+ }
1526
+ /**
1527
+ * Crea una instancia de almacenamiento plano (sin cifrar).
1528
+ * @param engine 'localStorage' o 'sessionStorage'
1529
+ * @returns Instancia de PlainStorage
1530
+ */
1531
+ function createPlainStorage(engine = 'localStorage') {
1532
+ const defaultEngine = typeof window !== 'undefined'
1533
+ ? (engine === 'sessionStorage' ? window.sessionStorage : window.localStorage)
1534
+ : {};
1535
+ return new PlainStorage(defaultEngine);
1536
+ }
1537
+
1538
+ /**
1539
+ * PlainCookie - Una interfaz consistente con SecureCookie pero sin cifrado.
1540
+ */
1541
+ class PlainCookie {
1542
+ constructor(defaultOptions) {
1543
+ this.logger = new Logger({ prefix: 'PlainCookie' });
1544
+ this.defaultOptions = { path: '/', ...defaultOptions };
1545
+ }
1546
+ serializeOptions(options) {
1547
+ let cookieString = '';
1548
+ if (options.expires)
1549
+ cookieString += `; expires=${options.expires.toUTCString()}`;
1550
+ if (options.maxAge)
1551
+ cookieString += `; max-age=${options.maxAge}`;
1552
+ if (options.domain)
1553
+ cookieString += `; domain=${options.domain}`;
1554
+ if (options.path)
1555
+ cookieString += `; path=${options.path}`;
1556
+ if (options.secure)
1557
+ cookieString += `; secure`;
1558
+ if (options.sameSite)
1559
+ cookieString += `; samesite=${options.sameSite}`;
1560
+ return cookieString;
1561
+ }
1562
+ async set(name, value, options) {
1563
+ try {
1564
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
1565
+ const mergedOptions = { ...this.defaultOptions, ...options };
1566
+ document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(serialized)}${this.serializeOptions(mergedOptions)}`;
1567
+ }
1568
+ catch (error) {
1569
+ this.logger.error(`Error saving unencrypted cookie ${name}:`, error);
1570
+ }
1571
+ }
1572
+ async get(name) {
1573
+ try {
1574
+ const nameEQ = encodeURIComponent(name) + '=';
1575
+ const ca = document.cookie.split(';');
1576
+ for (let i = 0; i < ca.length; i++) {
1577
+ let c = ca[i];
1578
+ while (c.charAt(0) === ' ')
1579
+ c = c.substring(1, c.length);
1580
+ if (c.indexOf(nameEQ) === 0) {
1581
+ const decoded = decodeURIComponent(c.substring(nameEQ.length, c.length));
1582
+ try {
1583
+ return JSON.parse(decoded);
1584
+ }
1585
+ catch {
1586
+ return decoded;
1587
+ }
1588
+ }
1589
+ }
1590
+ return null;
1591
+ }
1592
+ catch (error) {
1593
+ this.logger.error(`Error reading unencrypted cookie ${name}:`, error);
1594
+ return null;
1595
+ }
1596
+ }
1597
+ async remove(name, options) {
1598
+ const mergedOptions = { ...this.defaultOptions, ...options };
1599
+ document.cookie = `${encodeURIComponent(name)}=${this.serializeOptions({ ...mergedOptions, expires: new Date(0), maxAge: 0 })}`;
1600
+ }
1601
+ clearAll() {
1602
+ const cookies = document.cookie.split(';');
1603
+ for (let i = 0; i < cookies.length; i++) {
1604
+ const cookie = cookies[i];
1605
+ const eqPos = cookie.indexOf('=');
1606
+ const name = eqPos > -1 ? cookie.substring(0, eqPos).trim() : cookie.trim();
1607
+ this.remove(decodeURIComponent(name));
1608
+ }
1609
+ }
1610
+ }
1611
+
1612
+ /**
1613
+ * Crea una instancia de manejador de cookies encriptadas.
1614
+ * @param secretKey Llave secreta opcional para derivación de claves
1615
+ * @param domain Dominio opcional (incluir punto inicial para subdominios ej. '.dominio.com')
1616
+ * @param config Configuración adicional opcional
1617
+ * @returns Instancia de SecureCookie
1618
+ */
1619
+ function createEncryptedCookieManager(secretKey, domain, config) {
1620
+ const mergedConfig = {
1621
+ ...config,
1622
+ encryption: {
1623
+ ...config?.encryption,
1624
+ ...(secretKey ? { appIdentifier: secretKey } : {})
1625
+ },
1626
+ cookieOptions: {
1627
+ ...config?.cookieOptions,
1628
+ ...(domain ? { domain } : {})
1629
+ }
1630
+ };
1631
+ return SecureCookie.getInstance(mergedConfig);
1632
+ }
1633
+ /**
1634
+ * Crea una instancia de manejador de cookies plano (sin cifrar).
1635
+ * @param defaultOptions Opciones por defecto para las cookies (ej. domain)
1636
+ * @returns Instancia de PlainCookie
1637
+ */
1638
+ function createPlainCookieManager(defaultOptions) {
1639
+ return new PlainCookie(defaultOptions);
1640
+ }
1641
+
1642
+ /**
1643
+ * Instancia predeterminada de almacenamiento local (sin cifrar).
1644
+ */
1645
+ const localStore = createPlainStorage('localStorage');
1646
+ /**
1647
+ * Instancia predeterminada de almacenamiento de sesión (sin cifrar).
1648
+ */
1649
+ const sessionStore = createPlainStorage('sessionStorage');
1650
+ /**
1651
+ * Instancia predeterminada de gestor de cookies (sin cifrar).
1652
+ */
1653
+ const cookies = createPlainCookieManager();
1654
+
1223
1655
  /**
1224
1656
  * @bantis/local-cipher - Core Module
1225
1657
  * Framework-agnostic client-side encryption for browser storage
@@ -1229,8 +1661,9 @@ async function forceMigration(customKeys) {
1229
1661
  */
1230
1662
  // Core classes
1231
1663
  const secureStorage = SecureStorage.getInstance();
1664
+ const secureCookie = SecureCookie.getInstance();
1232
1665
  // Version
1233
- const VERSION = '2.1.0';
1666
+ const VERSION = '2.2.0';
1234
1667
 
1235
1668
  /******************************************************************************
1236
1669
  Copyright (c) Microsoft Corporation.
@@ -1523,6 +1956,50 @@ let SecureStorageService = (() => {
1523
1956
  return _classThis;
1524
1957
  })();
1525
1958
 
1959
+ let StorageService = (() => {
1960
+ let _classDecorators = [core.Injectable({
1961
+ providedIn: 'root'
1962
+ })];
1963
+ let _classDescriptor;
1964
+ let _classExtraInitializers = [];
1965
+ let _classThis;
1966
+ _classThis = class {
1967
+ constructor() {
1968
+ /** Acceso directo a localStorage sin cifrar */
1969
+ this.local = localStore;
1970
+ /** Acceso directo a sessionStorage sin cifrar */
1971
+ this.session = sessionStore;
1972
+ /** Acceso directo a cookies sin cifrar */
1973
+ this.cookies = cookies;
1974
+ }
1975
+ /**
1976
+ * Crea una instancia de almacenamiento sincronizado y cifrado
1977
+ * @param engine local o session storage
1978
+ * @param secretKey Llave de cifrado
1979
+ */
1980
+ createEncryptedStorage(engine, secretKey) {
1981
+ return createEncryptedStorage(engine, secretKey);
1982
+ }
1983
+ /**
1984
+ * Crea un gestor de cookies cifradas
1985
+ * @param secretKey Llave de cifrado
1986
+ * @param domain Configuración de subdominios
1987
+ */
1988
+ createEncryptedCookieManager(secretKey, domain) {
1989
+ return createEncryptedCookieManager(secretKey, domain);
1990
+ }
1991
+ };
1992
+ __setFunctionName(_classThis, "StorageService");
1993
+ (() => {
1994
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
1995
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
1996
+ _classThis = _classDescriptor.value;
1997
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
1998
+ __runInitializers(_classThis, _classExtraInitializers);
1999
+ })();
2000
+ return _classThis;
2001
+ })();
2002
+
1526
2003
  exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
1527
2004
  exports.EncryptionHelper = EncryptionHelper;
1528
2005
  exports.EventEmitter = EventEmitter;
@@ -1530,15 +2007,27 @@ exports.KeyRotation = KeyRotation;
1530
2007
  exports.LIBRARY_VERSION = LIBRARY_VERSION;
1531
2008
  exports.Logger = Logger;
1532
2009
  exports.NamespacedStorage = NamespacedStorage;
2010
+ exports.PlainCookie = PlainCookie;
2011
+ exports.PlainStorage = PlainStorage;
1533
2012
  exports.STORAGE_VERSION = STORAGE_VERSION;
2013
+ exports.SecureCookie = SecureCookie;
1534
2014
  exports.SecureStorage = SecureStorage;
1535
2015
  exports.SecureStorageService = SecureStorageService;
2016
+ exports.StorageService = StorageService;
1536
2017
  exports.VERSION = VERSION;
1537
2018
  exports.compress = compress;
2019
+ exports.cookies = cookies;
2020
+ exports.createEncryptedCookieManager = createEncryptedCookieManager;
2021
+ exports.createEncryptedStorage = createEncryptedStorage;
2022
+ exports.createPlainCookieManager = createPlainCookieManager;
2023
+ exports.createPlainStorage = createPlainStorage;
1538
2024
  exports.debugEncryptionState = debugEncryptionState;
1539
2025
  exports.decompress = decompress;
1540
2026
  exports.forceMigration = forceMigration;
1541
2027
  exports.isCompressionSupported = isCompressionSupported;
2028
+ exports.localStore = localStore;
2029
+ exports.secureCookie = secureCookie;
1542
2030
  exports.secureStorage = secureStorage;
2031
+ exports.sessionStore = sessionStore;
1543
2032
  exports.shouldCompress = shouldCompress;
1544
2033
  //# sourceMappingURL=angular.js.map