@bantis/local-cipher 2.1.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/README.md +85 -2
- package/dist/angular/SecureStorageService.d.ts +1 -1
- package/dist/angular/StorageService.d.ts +22 -0
- package/dist/angular.d.ts +1 -0
- package/dist/angular.esm.js +539 -53
- package/dist/angular.esm.js.map +1 -1
- package/dist/angular.js +550 -52
- package/dist/angular.js.map +1 -1
- package/dist/core/EncryptionHelper.d.ts +1 -0
- package/dist/core/NamespacedStorage.d.ts +3 -0
- package/dist/core/SecureCookie.d.ts +41 -0
- package/dist/core/SecureStorage.d.ts +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.esm.js +493 -51
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +503 -50
- package/dist/index.js.map +1 -1
- package/dist/managers/CookieManager.d.ts +17 -0
- package/dist/managers/PlainCookie.d.ts +14 -0
- package/dist/managers/PlainStorage.d.ts +13 -0
- package/dist/managers/StorageManager.d.ts +17 -0
- package/dist/managers/index.d.ts +17 -0
- package/dist/react/hooks.d.ts +20 -2
- package/dist/react.d.ts +1 -1
- package/dist/react.esm.js +518 -59
- package/dist/react.esm.js.map +1 -1
- package/dist/react.js +529 -59
- package/dist/react.js.map +1 -1
- package/dist/types/index.d.ts +36 -0
- package/package.json +8 -4
package/dist/react.esm.js
CHANGED
|
@@ -19,6 +19,9 @@ const DEFAULT_CONFIG = {
|
|
|
19
19
|
compressionThreshold: 1024,
|
|
20
20
|
autoCleanup: true,
|
|
21
21
|
cleanupInterval: 60000,
|
|
22
|
+
enableCache: true,
|
|
23
|
+
verifyIntegrityOnRead: false,
|
|
24
|
+
storageEngine: typeof window !== 'undefined' ? window.localStorage : {},
|
|
22
25
|
},
|
|
23
26
|
debug: {
|
|
24
27
|
enabled: false,
|
|
@@ -46,6 +49,8 @@ class EncryptionHelper {
|
|
|
46
49
|
this.baseKey = '';
|
|
47
50
|
this.baseKeyPromise = null;
|
|
48
51
|
this.keyVersion = 1;
|
|
52
|
+
// Cache para optimización de rendimiento
|
|
53
|
+
this.keyNameCache = new Map();
|
|
49
54
|
this.config = { ...DEFAULT_CONFIG.encryption, ...config };
|
|
50
55
|
// Load key version from storage
|
|
51
56
|
const storedVersion = localStorage.getItem(EncryptionHelper.KEY_VERSION_KEY);
|
|
@@ -108,7 +113,7 @@ class EncryptionHelper {
|
|
|
108
113
|
// Derivar la clave AES-GCM
|
|
109
114
|
return crypto.subtle.deriveKey({
|
|
110
115
|
name: 'PBKDF2',
|
|
111
|
-
salt,
|
|
116
|
+
salt: salt,
|
|
112
117
|
iterations: this.config.iterations,
|
|
113
118
|
hash: EncryptionHelper.HASH_ALGORITHM,
|
|
114
119
|
}, keyMaterial, {
|
|
@@ -215,10 +220,16 @@ class EncryptionHelper {
|
|
|
215
220
|
* @returns Nombre encriptado con prefijo __enc_
|
|
216
221
|
*/
|
|
217
222
|
async encryptKey(keyName) {
|
|
223
|
+
// Optimización: devolver desde cache si existe
|
|
224
|
+
if (this.keyNameCache.has(keyName)) {
|
|
225
|
+
return this.keyNameCache.get(keyName);
|
|
226
|
+
}
|
|
218
227
|
const baseKey = await this.generateBaseKey();
|
|
219
228
|
const combined = keyName + baseKey;
|
|
220
229
|
const hash = await this.hashString(combined);
|
|
221
|
-
|
|
230
|
+
const encryptedKey = `__enc_${hash.substring(0, 16)}`;
|
|
231
|
+
this.keyNameCache.set(keyName, encryptedKey);
|
|
232
|
+
return encryptedKey;
|
|
222
233
|
}
|
|
223
234
|
/**
|
|
224
235
|
* Limpia todos los datos encriptados del localStorage
|
|
@@ -236,10 +247,11 @@ class EncryptionHelper {
|
|
|
236
247
|
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
237
248
|
// Eliminar salt
|
|
238
249
|
localStorage.removeItem(EncryptionHelper.SALT_STORAGE_KEY);
|
|
239
|
-
// Resetear clave en memoria
|
|
250
|
+
// Resetear clave en memoria y cache
|
|
240
251
|
this.key = null;
|
|
241
252
|
this.baseKey = '';
|
|
242
253
|
this.baseKeyPromise = null;
|
|
254
|
+
this.keyNameCache.clear();
|
|
243
255
|
}
|
|
244
256
|
/**
|
|
245
257
|
* Verifica si el navegador soporta Web Crypto API
|
|
@@ -390,7 +402,7 @@ class Logger {
|
|
|
390
402
|
return false;
|
|
391
403
|
return Logger.LOG_LEVELS[level] <= Logger.LOG_LEVELS[this.logLevel];
|
|
392
404
|
}
|
|
393
|
-
formatMessage(level, message
|
|
405
|
+
formatMessage(level, message) {
|
|
394
406
|
const timestamp = new Date().toISOString();
|
|
395
407
|
return `[${this.prefix}] [${level.toUpperCase()}] ${timestamp} - ${message}`;
|
|
396
408
|
}
|
|
@@ -543,17 +555,37 @@ class NamespacedStorage {
|
|
|
543
555
|
this.storage = storage;
|
|
544
556
|
this.prefix = `__ns_${namespace}__`;
|
|
545
557
|
}
|
|
558
|
+
async getIndex() {
|
|
559
|
+
const indexValue = await this.storage.getItem(`${this.prefix}__index__`);
|
|
560
|
+
return indexValue ? JSON.parse(indexValue) : [];
|
|
561
|
+
}
|
|
562
|
+
async saveToIndex(key) {
|
|
563
|
+
const index = await this.getIndex();
|
|
564
|
+
if (!index.includes(key)) {
|
|
565
|
+
index.push(key);
|
|
566
|
+
await this.storage.setItem(`${this.prefix}__index__`, JSON.stringify(index));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async removeFromIndex(key) {
|
|
570
|
+
const index = await this.getIndex();
|
|
571
|
+
const newIndex = index.filter(k => k !== key);
|
|
572
|
+
if (newIndex.length !== index.length) {
|
|
573
|
+
await this.storage.setItem(`${this.prefix}__index__`, JSON.stringify(newIndex));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
546
576
|
/**
|
|
547
577
|
* Set item in this namespace
|
|
548
578
|
*/
|
|
549
579
|
async setItem(key, value) {
|
|
550
|
-
|
|
580
|
+
await this.storage.setItem(`${this.prefix}${key}`, value);
|
|
581
|
+
await this.saveToIndex(key);
|
|
551
582
|
}
|
|
552
583
|
/**
|
|
553
584
|
* Set item with expiry in this namespace
|
|
554
585
|
*/
|
|
555
586
|
async setItemWithExpiry(key, value, options) {
|
|
556
|
-
|
|
587
|
+
await this.storage.setItemWithExpiry(`${this.prefix}${key}`, value, options);
|
|
588
|
+
await this.saveToIndex(key);
|
|
557
589
|
}
|
|
558
590
|
/**
|
|
559
591
|
* Get item from this namespace
|
|
@@ -565,7 +597,8 @@ class NamespacedStorage {
|
|
|
565
597
|
* Remove item from this namespace
|
|
566
598
|
*/
|
|
567
599
|
async removeItem(key) {
|
|
568
|
-
|
|
600
|
+
await this.storage.removeItem(`${this.prefix}${key}`);
|
|
601
|
+
await this.removeFromIndex(key);
|
|
569
602
|
}
|
|
570
603
|
/**
|
|
571
604
|
* Check if item exists in this namespace
|
|
@@ -577,29 +610,17 @@ class NamespacedStorage {
|
|
|
577
610
|
* Clear all items in this namespace
|
|
578
611
|
*/
|
|
579
612
|
async clearNamespace() {
|
|
580
|
-
const keysToRemove =
|
|
581
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
582
|
-
const key = localStorage.key(i);
|
|
583
|
-
if (key && key.includes(this.prefix)) {
|
|
584
|
-
keysToRemove.push(key.replace(this.prefix, ''));
|
|
585
|
-
}
|
|
586
|
-
}
|
|
613
|
+
const keysToRemove = await this.getIndex();
|
|
587
614
|
for (const key of keysToRemove) {
|
|
588
|
-
await this.removeItem(key);
|
|
615
|
+
await this.storage.removeItem(`${this.prefix}${key}`);
|
|
589
616
|
}
|
|
617
|
+
await this.storage.removeItem(`${this.prefix}__index__`);
|
|
590
618
|
}
|
|
591
619
|
/**
|
|
592
620
|
* Get all keys in this namespace
|
|
593
621
|
*/
|
|
594
622
|
async keys() {
|
|
595
|
-
|
|
596
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
597
|
-
const key = localStorage.key(i);
|
|
598
|
-
if (key && key.includes(this.prefix)) {
|
|
599
|
-
keys.push(key.replace(this.prefix, ''));
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
return keys;
|
|
623
|
+
return this.getIndex();
|
|
603
624
|
}
|
|
604
625
|
}
|
|
605
626
|
|
|
@@ -622,7 +643,6 @@ async function compress(data) {
|
|
|
622
643
|
return encoder.encode(data);
|
|
623
644
|
}
|
|
624
645
|
try {
|
|
625
|
-
const encoder = new TextEncoder();
|
|
626
646
|
const stream = new Blob([data]).stream();
|
|
627
647
|
const compressedStream = stream.pipeThrough(new CompressionStream('gzip'));
|
|
628
648
|
const compressedBlob = await new Response(compressedStream).blob();
|
|
@@ -671,6 +691,7 @@ function shouldCompress(data, threshold = 1024) {
|
|
|
671
691
|
class SecureStorage {
|
|
672
692
|
constructor(config) {
|
|
673
693
|
this.cleanupInterval = null;
|
|
694
|
+
this.memoryCache = new Map();
|
|
674
695
|
// Merge config with defaults
|
|
675
696
|
this.config = {
|
|
676
697
|
encryption: { ...DEFAULT_CONFIG.encryption, ...config?.encryption },
|
|
@@ -752,8 +773,12 @@ class SecureStorage {
|
|
|
752
773
|
const encryptedKey = await this.encryptionHelper.encryptKey(key);
|
|
753
774
|
// Encrypt the value
|
|
754
775
|
const encryptedValue = await this.encryptionHelper.encrypt(serialized);
|
|
755
|
-
// Store in
|
|
756
|
-
|
|
776
|
+
// Store in storage
|
|
777
|
+
this.config.storage.storageEngine.setItem(encryptedKey, encryptedValue);
|
|
778
|
+
// Update cache
|
|
779
|
+
if (this.config.storage.enableCache) {
|
|
780
|
+
this.memoryCache.set(key, { value });
|
|
781
|
+
}
|
|
757
782
|
this.logger.verbose(`Stored key: ${key}, compressed: ${compressed}, size: ${encryptedValue.length}`);
|
|
758
783
|
this.eventEmitter.emit('encrypted', { key, metadata: { compressed, size: encryptedValue.length } });
|
|
759
784
|
this.logger.timeEnd(`setItem:${key}`);
|
|
@@ -761,7 +786,7 @@ class SecureStorage {
|
|
|
761
786
|
catch (error) {
|
|
762
787
|
this.logger.error(`Error al guardar dato encriptado para ${key}:`, error);
|
|
763
788
|
this.eventEmitter.emit('error', { key, error: error });
|
|
764
|
-
|
|
789
|
+
this.config.storage.storageEngine.setItem(key, value);
|
|
765
790
|
}
|
|
766
791
|
}
|
|
767
792
|
/**
|
|
@@ -814,8 +839,12 @@ class SecureStorage {
|
|
|
814
839
|
const encryptedKey = await this.encryptionHelper.encryptKey(key);
|
|
815
840
|
// Encrypt the value
|
|
816
841
|
const encryptedValue = await this.encryptionHelper.encrypt(serialized);
|
|
817
|
-
// Store in
|
|
818
|
-
|
|
842
|
+
// Store in storage
|
|
843
|
+
this.config.storage.storageEngine.setItem(encryptedKey, encryptedValue);
|
|
844
|
+
// Update cache
|
|
845
|
+
if (this.config.storage.enableCache) {
|
|
846
|
+
this.memoryCache.set(key, { value, expiresAt });
|
|
847
|
+
}
|
|
819
848
|
this.logger.verbose(`Stored key with expiry: ${key}, expiresAt: ${expiresAt}`);
|
|
820
849
|
this.eventEmitter.emit('encrypted', { key, metadata: { compressed, expiresAt } });
|
|
821
850
|
this.logger.timeEnd(`setItemWithExpiry:${key}`);
|
|
@@ -832,16 +861,31 @@ class SecureStorage {
|
|
|
832
861
|
async getItem(key) {
|
|
833
862
|
this.logger.time(`getItem:${key}`);
|
|
834
863
|
if (!EncryptionHelper.isSupported()) {
|
|
835
|
-
return
|
|
864
|
+
return this.config.storage.storageEngine.getItem(key);
|
|
836
865
|
}
|
|
837
866
|
try {
|
|
867
|
+
// Check memory cache first
|
|
868
|
+
if (this.config.storage.enableCache && this.memoryCache.has(key)) {
|
|
869
|
+
const cached = this.memoryCache.get(key);
|
|
870
|
+
if (cached.expiresAt && cached.expiresAt < Date.now()) {
|
|
871
|
+
this.logger.info(`Key expired in cache: ${key}`);
|
|
872
|
+
await this.removeItem(key);
|
|
873
|
+
this.eventEmitter.emit('expired', { key });
|
|
874
|
+
this.logger.timeEnd(`getItem:${key}`);
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
this.logger.debug(`Retrieved from cache: ${key}`);
|
|
878
|
+
this.eventEmitter.emit('decrypted', { key });
|
|
879
|
+
this.logger.timeEnd(`getItem:${key}`);
|
|
880
|
+
return cached.value;
|
|
881
|
+
}
|
|
838
882
|
// Encrypt the key
|
|
839
883
|
const encryptedKey = await this.encryptionHelper.encryptKey(key);
|
|
840
884
|
// Get encrypted value
|
|
841
|
-
let encryptedValue =
|
|
885
|
+
let encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
|
|
842
886
|
// Backward compatibility: try with plain key
|
|
843
887
|
if (!encryptedValue) {
|
|
844
|
-
encryptedValue =
|
|
888
|
+
encryptedValue = this.config.storage.storageEngine.getItem(key);
|
|
845
889
|
if (!encryptedValue) {
|
|
846
890
|
this.logger.timeEnd(`getItem:${key}`);
|
|
847
891
|
return null;
|
|
@@ -877,8 +921,8 @@ class SecureStorage {
|
|
|
877
921
|
this.logger.timeEnd(`getItem:${key}`);
|
|
878
922
|
return null;
|
|
879
923
|
}
|
|
880
|
-
// Verify integrity if checksum exists
|
|
881
|
-
if (storedValue.checksum) {
|
|
924
|
+
// Verify integrity if checksum exists and configured
|
|
925
|
+
if (storedValue.checksum && this.config.storage.verifyIntegrityOnRead) {
|
|
882
926
|
const calculatedChecksum = await this.calculateChecksum(storedValue.value);
|
|
883
927
|
if (calculatedChecksum !== storedValue.checksum) {
|
|
884
928
|
this.logger.warn(`Integrity check failed for key: ${key}`);
|
|
@@ -896,6 +940,9 @@ class SecureStorage {
|
|
|
896
940
|
finalValue = await decompress(new Uint8Array(compressedData));
|
|
897
941
|
this.eventEmitter.emit('decompressed', { key });
|
|
898
942
|
}
|
|
943
|
+
if (this.config.storage.enableCache) {
|
|
944
|
+
this.memoryCache.set(key, { value: finalValue, expiresAt: storedValue.expiresAt });
|
|
945
|
+
}
|
|
899
946
|
this.eventEmitter.emit('decrypted', { key });
|
|
900
947
|
this.logger.timeEnd(`getItem:${key}`);
|
|
901
948
|
return finalValue;
|
|
@@ -904,7 +951,7 @@ class SecureStorage {
|
|
|
904
951
|
this.logger.error(`Error al recuperar dato encriptado para ${key}:`, error);
|
|
905
952
|
this.eventEmitter.emit('error', { key, error: error });
|
|
906
953
|
// Fallback: try plain key
|
|
907
|
-
const fallback =
|
|
954
|
+
const fallback = this.config.storage.storageEngine.getItem(key);
|
|
908
955
|
this.logger.timeEnd(`getItem:${key}`);
|
|
909
956
|
return fallback;
|
|
910
957
|
}
|
|
@@ -914,19 +961,20 @@ class SecureStorage {
|
|
|
914
961
|
*/
|
|
915
962
|
async removeItem(key) {
|
|
916
963
|
if (!EncryptionHelper.isSupported()) {
|
|
917
|
-
|
|
964
|
+
this.config.storage.storageEngine.removeItem(key);
|
|
918
965
|
return;
|
|
919
966
|
}
|
|
920
967
|
try {
|
|
921
968
|
const encryptedKey = await this.encryptionHelper.encryptKey(key);
|
|
922
|
-
|
|
923
|
-
|
|
969
|
+
this.config.storage.storageEngine.removeItem(encryptedKey);
|
|
970
|
+
this.config.storage.storageEngine.removeItem(key); // Remove both versions
|
|
971
|
+
this.memoryCache.delete(key);
|
|
924
972
|
this.eventEmitter.emit('deleted', { key });
|
|
925
973
|
this.logger.info(`Removed key: ${key}`);
|
|
926
974
|
}
|
|
927
975
|
catch (error) {
|
|
928
976
|
this.logger.error(`Error al eliminar dato para ${key}:`, error);
|
|
929
|
-
|
|
977
|
+
this.config.storage.storageEngine.removeItem(key);
|
|
930
978
|
}
|
|
931
979
|
}
|
|
932
980
|
/**
|
|
@@ -941,6 +989,7 @@ class SecureStorage {
|
|
|
941
989
|
*/
|
|
942
990
|
clear() {
|
|
943
991
|
this.encryptionHelper.clearEncryptedData();
|
|
992
|
+
this.memoryCache.clear();
|
|
944
993
|
this.eventEmitter.emit('cleared', {});
|
|
945
994
|
this.logger.info('All encrypted data cleared');
|
|
946
995
|
}
|
|
@@ -951,21 +1000,28 @@ class SecureStorage {
|
|
|
951
1000
|
this.logger.info('Starting cleanup of expired items...');
|
|
952
1001
|
let cleanedCount = 0;
|
|
953
1002
|
const keysToCheck = [];
|
|
954
|
-
for (let i = 0; i <
|
|
955
|
-
const key =
|
|
1003
|
+
for (let i = 0; i < this.config.storage.storageEngine.length; i++) {
|
|
1004
|
+
const key = this.config.storage.storageEngine.key(i);
|
|
956
1005
|
if (key && key.startsWith('__enc_')) {
|
|
957
1006
|
keysToCheck.push(key);
|
|
958
1007
|
}
|
|
959
1008
|
}
|
|
960
1009
|
for (const encryptedKey of keysToCheck) {
|
|
961
1010
|
try {
|
|
962
|
-
const encryptedValue =
|
|
1011
|
+
const encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
|
|
963
1012
|
if (!encryptedValue)
|
|
964
1013
|
continue;
|
|
965
1014
|
const decrypted = await this.encryptionHelper.decrypt(encryptedValue);
|
|
966
1015
|
const storedValue = JSON.parse(decrypted);
|
|
967
1016
|
if (storedValue.expiresAt && storedValue.expiresAt < Date.now()) {
|
|
968
|
-
|
|
1017
|
+
this.config.storage.storageEngine.removeItem(encryptedKey);
|
|
1018
|
+
// Encontrar la clave original en el cache y eliminarla
|
|
1019
|
+
for (const [cacheKey] of Array.from(this.memoryCache.entries())) {
|
|
1020
|
+
this.encryptionHelper.encryptKey(cacheKey).then(enc => {
|
|
1021
|
+
if (enc === encryptedKey)
|
|
1022
|
+
this.memoryCache.delete(cacheKey);
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
969
1025
|
cleanedCount++;
|
|
970
1026
|
this.eventEmitter.emit('expired', { key: encryptedKey });
|
|
971
1027
|
}
|
|
@@ -983,7 +1039,7 @@ class SecureStorage {
|
|
|
983
1039
|
async verifyIntegrity(key) {
|
|
984
1040
|
try {
|
|
985
1041
|
const encryptedKey = await this.encryptionHelper.encryptKey(key);
|
|
986
|
-
const encryptedValue =
|
|
1042
|
+
const encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
|
|
987
1043
|
if (!encryptedValue)
|
|
988
1044
|
return false;
|
|
989
1045
|
const decrypted = await this.encryptionHelper.decrypt(encryptedValue);
|
|
@@ -1006,7 +1062,7 @@ class SecureStorage {
|
|
|
1006
1062
|
async getIntegrityInfo(key) {
|
|
1007
1063
|
try {
|
|
1008
1064
|
const encryptedKey = await this.encryptionHelper.encryptKey(key);
|
|
1009
|
-
const encryptedValue =
|
|
1065
|
+
const encryptedValue = this.config.storage.storageEngine.getItem(encryptedKey);
|
|
1010
1066
|
if (!encryptedValue)
|
|
1011
1067
|
return null;
|
|
1012
1068
|
const decrypted = await this.encryptionHelper.decrypt(encryptedValue);
|
|
@@ -1089,7 +1145,7 @@ class SecureStorage {
|
|
|
1089
1145
|
this.logger.info(`Iniciando migración de ${keys.length} claves...`);
|
|
1090
1146
|
for (const key of keys) {
|
|
1091
1147
|
try {
|
|
1092
|
-
const value =
|
|
1148
|
+
const value = this.config.storage.storageEngine.getItem(key);
|
|
1093
1149
|
if (value === null)
|
|
1094
1150
|
continue;
|
|
1095
1151
|
// Try to decrypt to check if already encrypted
|
|
@@ -1102,7 +1158,7 @@ class SecureStorage {
|
|
|
1102
1158
|
// Not encrypted, proceed with migration
|
|
1103
1159
|
}
|
|
1104
1160
|
await this.setItem(key, value);
|
|
1105
|
-
|
|
1161
|
+
this.config.storage.storageEngine.removeItem(key);
|
|
1106
1162
|
this.logger.info(`✓ ${key} migrado exitosamente`);
|
|
1107
1163
|
}
|
|
1108
1164
|
catch (error) {
|
|
@@ -1116,8 +1172,8 @@ class SecureStorage {
|
|
|
1116
1172
|
*/
|
|
1117
1173
|
getDebugInfo() {
|
|
1118
1174
|
const allKeys = [];
|
|
1119
|
-
for (let i = 0; i <
|
|
1120
|
-
const key =
|
|
1175
|
+
for (let i = 0; i < this.config.storage.storageEngine.length; i++) {
|
|
1176
|
+
const key = this.config.storage.storageEngine.key(i);
|
|
1121
1177
|
if (key)
|
|
1122
1178
|
allKeys.push(key);
|
|
1123
1179
|
}
|
|
@@ -1151,10 +1207,199 @@ class SecureStorage {
|
|
|
1151
1207
|
}
|
|
1152
1208
|
this.removeAllListeners();
|
|
1153
1209
|
this.logger.info('SecureStorage destroyed');
|
|
1210
|
+
SecureStorage.instance = null;
|
|
1154
1211
|
}
|
|
1155
1212
|
}
|
|
1156
1213
|
SecureStorage.instance = null;
|
|
1157
1214
|
|
|
1215
|
+
/**
|
|
1216
|
+
* SecureCookie - API de alto nivel para almacenamiento en cookies cifradas
|
|
1217
|
+
* Soporta opciones de cookies incluyendo domininios/subdominios y compresión.
|
|
1218
|
+
*/
|
|
1219
|
+
class SecureCookie {
|
|
1220
|
+
constructor(config) {
|
|
1221
|
+
this.config = {
|
|
1222
|
+
encryption: { ...DEFAULT_CONFIG.encryption, ...config?.encryption },
|
|
1223
|
+
cookieOptions: { path: '/', ...config?.cookieOptions },
|
|
1224
|
+
compression: config?.compression ?? true,
|
|
1225
|
+
debug: { ...DEFAULT_CONFIG.debug, ...config?.debug }
|
|
1226
|
+
};
|
|
1227
|
+
this.logger = new Logger(this.config.debug);
|
|
1228
|
+
this.encryptionHelper = new EncryptionHelper(this.config.encryption);
|
|
1229
|
+
this.logger.info('SecureCookie initialized', this.config);
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Obtiene la instancia singleton de SecureCookie
|
|
1233
|
+
*/
|
|
1234
|
+
static getInstance(config) {
|
|
1235
|
+
if (!SecureCookie.instance) {
|
|
1236
|
+
SecureCookie.instance = new SecureCookie(config);
|
|
1237
|
+
}
|
|
1238
|
+
return SecureCookie.instance;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Serializa las opciones de cookie en un string
|
|
1242
|
+
*/
|
|
1243
|
+
serializeOptions(options) {
|
|
1244
|
+
let cookieString = '';
|
|
1245
|
+
if (options.expires) {
|
|
1246
|
+
cookieString += `; expires=${options.expires.toUTCString()}`;
|
|
1247
|
+
}
|
|
1248
|
+
if (options.maxAge) {
|
|
1249
|
+
cookieString += `; max-age=${options.maxAge}`;
|
|
1250
|
+
}
|
|
1251
|
+
if (options.domain) {
|
|
1252
|
+
cookieString += `; domain=${options.domain}`;
|
|
1253
|
+
}
|
|
1254
|
+
if (options.path) {
|
|
1255
|
+
cookieString += `; path=${options.path}`;
|
|
1256
|
+
}
|
|
1257
|
+
if (options.secure) {
|
|
1258
|
+
cookieString += `; secure`;
|
|
1259
|
+
}
|
|
1260
|
+
if (options.sameSite) {
|
|
1261
|
+
cookieString += `; samesite=${options.sameSite}`;
|
|
1262
|
+
}
|
|
1263
|
+
return cookieString;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Guarda un valor encriptado en una cookie
|
|
1267
|
+
*/
|
|
1268
|
+
async set(name, value, options) {
|
|
1269
|
+
this.logger.time(`cookie:set:${name}`);
|
|
1270
|
+
if (!EncryptionHelper.isSupported()) {
|
|
1271
|
+
this.logger.warn('Web Crypto API no soportada, guardando cookie sin cifrar');
|
|
1272
|
+
const mergedOptions = { ...this.config.cookieOptions, ...options };
|
|
1273
|
+
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${this.serializeOptions(mergedOptions)}`;
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
try {
|
|
1277
|
+
// Check compression (aggressively compress cookies to save space since max is ~4KB)
|
|
1278
|
+
const shouldCompressData = this.config.compression && shouldCompress(value, 200);
|
|
1279
|
+
let processedValue = value;
|
|
1280
|
+
let metadataPrefix = 'P_'; // P_ = Plain
|
|
1281
|
+
if (shouldCompressData) {
|
|
1282
|
+
this.logger.debug(`Compressing cookie value for: ${name}`);
|
|
1283
|
+
const compressedData = await compress(value);
|
|
1284
|
+
processedValue = this.encryptionHelper['arrayBufferToBase64'](compressedData.buffer);
|
|
1285
|
+
metadataPrefix = 'C_'; // C_ = Compressed
|
|
1286
|
+
}
|
|
1287
|
+
// Encrypt key name
|
|
1288
|
+
const encryptedKey = await this.encryptionHelper.encryptKey(name);
|
|
1289
|
+
// Serialize and Encrypt Value
|
|
1290
|
+
// Adding metadata prefix to avoid bloating value with massive StoredValue JSON headers
|
|
1291
|
+
const encryptedData = await this.encryptionHelper.encrypt(processedValue);
|
|
1292
|
+
const finalValue = metadataPrefix + encryptedData;
|
|
1293
|
+
// Prepare Cookie string
|
|
1294
|
+
const mergedOptions = { ...this.config.cookieOptions, ...options };
|
|
1295
|
+
const cookieOptionsStr = this.serializeOptions(mergedOptions);
|
|
1296
|
+
const cookieString = `${encodeURIComponent(encryptedKey)}=${encodeURIComponent(finalValue)}${cookieOptionsStr}`;
|
|
1297
|
+
// Verificación de tamaño de cookie
|
|
1298
|
+
if (cookieString.length > 4096) {
|
|
1299
|
+
this.logger.warn(`Cookie '${name}' es muy grande (${cookieString.length} bytes). Puede ser rechazada por el navegador.`);
|
|
1300
|
+
}
|
|
1301
|
+
document.cookie = cookieString;
|
|
1302
|
+
this.logger.verbose(`Stored cookie: ${name}`);
|
|
1303
|
+
this.logger.timeEnd(`cookie:set:${name}`);
|
|
1304
|
+
}
|
|
1305
|
+
catch (error) {
|
|
1306
|
+
this.logger.error(`Error al guardar cookie encriptada para ${name}:`, error);
|
|
1307
|
+
// Fallback sin cifrar como último recurso
|
|
1308
|
+
const mergedOptions = { ...this.config.cookieOptions, ...options };
|
|
1309
|
+
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${this.serializeOptions(mergedOptions)}`;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Pide una cookie por nombre
|
|
1314
|
+
* Retorna el string o null si no existe
|
|
1315
|
+
*/
|
|
1316
|
+
getRawCookie(name) {
|
|
1317
|
+
const nameEQ = encodeURIComponent(name) + '=';
|
|
1318
|
+
const ca = document.cookie.split(';');
|
|
1319
|
+
for (let i = 0; i < ca.length; i++) {
|
|
1320
|
+
let c = ca[i];
|
|
1321
|
+
while (c.charAt(0) === ' ')
|
|
1322
|
+
c = c.substring(1, c.length);
|
|
1323
|
+
if (c.indexOf(nameEQ) === 0)
|
|
1324
|
+
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
1325
|
+
}
|
|
1326
|
+
return null;
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Recupera y desencripta un valor de cookie
|
|
1330
|
+
*/
|
|
1331
|
+
async get(name) {
|
|
1332
|
+
this.logger.time(`cookie:get:${name}`);
|
|
1333
|
+
if (!EncryptionHelper.isSupported()) {
|
|
1334
|
+
return this.getRawCookie(name);
|
|
1335
|
+
}
|
|
1336
|
+
try {
|
|
1337
|
+
// Find encrypted key
|
|
1338
|
+
const encryptedKey = await this.encryptionHelper.encryptKey(name);
|
|
1339
|
+
let rawValue = this.getRawCookie(encryptedKey);
|
|
1340
|
+
if (!rawValue) {
|
|
1341
|
+
// Backward compatibility just in case fallback was used
|
|
1342
|
+
rawValue = this.getRawCookie(name);
|
|
1343
|
+
if (!rawValue) {
|
|
1344
|
+
this.logger.timeEnd(`cookie:get:${name}`);
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
// Check if it has our metadata prefixes
|
|
1349
|
+
if (!rawValue.startsWith('P_') && !rawValue.startsWith('C_')) {
|
|
1350
|
+
// Podría ser un fallback plano
|
|
1351
|
+
this.logger.timeEnd(`cookie:get:${name}`);
|
|
1352
|
+
return rawValue; // Asumimos que es plano
|
|
1353
|
+
}
|
|
1354
|
+
const isCompressed = rawValue.startsWith('C_');
|
|
1355
|
+
const encryptedData = rawValue.substring(2);
|
|
1356
|
+
// Decrypt
|
|
1357
|
+
const decryptedString = await this.encryptionHelper.decrypt(encryptedData);
|
|
1358
|
+
// Decompress if needed
|
|
1359
|
+
let finalValue = decryptedString;
|
|
1360
|
+
if (isCompressed) {
|
|
1361
|
+
const compressedBuffer = this.encryptionHelper['base64ToArrayBuffer'](decryptedString);
|
|
1362
|
+
finalValue = await decompress(new Uint8Array(compressedBuffer));
|
|
1363
|
+
}
|
|
1364
|
+
this.logger.timeEnd(`cookie:get:${name}`);
|
|
1365
|
+
return finalValue;
|
|
1366
|
+
}
|
|
1367
|
+
catch (error) {
|
|
1368
|
+
this.logger.error(`Error al recuperar cookie encriptada para ${name}:`, error);
|
|
1369
|
+
// Fallback
|
|
1370
|
+
return this.getRawCookie(name);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Elimina una cookie
|
|
1375
|
+
*/
|
|
1376
|
+
async remove(name, options) {
|
|
1377
|
+
const mergedOptions = { ...this.config.cookieOptions, ...options };
|
|
1378
|
+
// Expirar la cookie configurándole una fecha del pasado
|
|
1379
|
+
const expireOptions = {
|
|
1380
|
+
...mergedOptions,
|
|
1381
|
+
expires: new Date(0),
|
|
1382
|
+
maxAge: 0
|
|
1383
|
+
};
|
|
1384
|
+
if (!EncryptionHelper.isSupported()) {
|
|
1385
|
+
document.cookie = `${encodeURIComponent(name)}=${this.serializeOptions(expireOptions)}`;
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
const encryptedKey = await this.encryptionHelper.encryptKey(name);
|
|
1389
|
+
// Remove encrypted cookie
|
|
1390
|
+
document.cookie = `${encodeURIComponent(encryptedKey)}=${this.serializeOptions(expireOptions)}`;
|
|
1391
|
+
// Also remove plain version just in case
|
|
1392
|
+
document.cookie = `${encodeURIComponent(name)}=${this.serializeOptions(expireOptions)}`;
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Limpia la instancia actual (útil para testing o refresco)
|
|
1396
|
+
*/
|
|
1397
|
+
destroy() {
|
|
1398
|
+
SecureCookie.instance = null;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
SecureCookie.instance = null;
|
|
1402
|
+
|
|
1158
1403
|
const secureStorage$2 = SecureStorage.getInstance();
|
|
1159
1404
|
/**
|
|
1160
1405
|
* Función de debug para verificar el estado del sistema de encriptación
|
|
@@ -1207,6 +1452,202 @@ async function forceMigration(customKeys) {
|
|
|
1207
1452
|
await debugEncryptionState();
|
|
1208
1453
|
}
|
|
1209
1454
|
|
|
1455
|
+
/**
|
|
1456
|
+
* PlainStorage - Una interfaz consistente con SecureStorage pero sin cifrado.
|
|
1457
|
+
* Envuelve localStorage o sessionStorage con serialización JSON.
|
|
1458
|
+
*/
|
|
1459
|
+
class PlainStorage {
|
|
1460
|
+
constructor(storageEngine = typeof window !== 'undefined' ? window.localStorage : {}) {
|
|
1461
|
+
this.storage = storageEngine;
|
|
1462
|
+
this.logger = new Logger({ prefix: 'PlainStorage' });
|
|
1463
|
+
}
|
|
1464
|
+
async set(key, value) {
|
|
1465
|
+
try {
|
|
1466
|
+
const serialized = typeof value === 'string' ? value : JSON.stringify(value);
|
|
1467
|
+
this.storage.setItem(key, serialized);
|
|
1468
|
+
}
|
|
1469
|
+
catch (error) {
|
|
1470
|
+
this.logger.error(`Error saving unencrypted item ${key}:`, error);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
async get(key) {
|
|
1474
|
+
try {
|
|
1475
|
+
const value = this.storage.getItem(key);
|
|
1476
|
+
if (value === null)
|
|
1477
|
+
return null;
|
|
1478
|
+
try {
|
|
1479
|
+
return JSON.parse(value);
|
|
1480
|
+
}
|
|
1481
|
+
catch {
|
|
1482
|
+
return value; // Fallback for plain strings
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
catch (error) {
|
|
1486
|
+
this.logger.error(`Error reading unencrypted item ${key}:`, error);
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
async remove(key) {
|
|
1491
|
+
this.storage.removeItem(key);
|
|
1492
|
+
}
|
|
1493
|
+
clear() {
|
|
1494
|
+
this.storage.clear();
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Crea una instancia de almacenamiento encriptado.
|
|
1500
|
+
* @param engine 'localStorage' o 'sessionStorage'
|
|
1501
|
+
* @param secretKey Llave secreta opcional para derivación de claves
|
|
1502
|
+
* @param config Configuración adicional opcional
|
|
1503
|
+
* @returns Instancia de SecureStorage
|
|
1504
|
+
*/
|
|
1505
|
+
function createEncryptedStorage(engine = 'localStorage', secretKey, config) {
|
|
1506
|
+
const defaultEngine = typeof window !== 'undefined'
|
|
1507
|
+
? (engine === 'sessionStorage' ? window.sessionStorage : window.localStorage)
|
|
1508
|
+
: {};
|
|
1509
|
+
const mergedConfig = {
|
|
1510
|
+
...config,
|
|
1511
|
+
encryption: {
|
|
1512
|
+
...config?.encryption,
|
|
1513
|
+
...(secretKey ? { appIdentifier: secretKey } : {})
|
|
1514
|
+
},
|
|
1515
|
+
storage: {
|
|
1516
|
+
...config?.storage,
|
|
1517
|
+
storageEngine: defaultEngine
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
return SecureStorage.getInstance(mergedConfig);
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Crea una instancia de almacenamiento plano (sin cifrar).
|
|
1524
|
+
* @param engine 'localStorage' o 'sessionStorage'
|
|
1525
|
+
* @returns Instancia de PlainStorage
|
|
1526
|
+
*/
|
|
1527
|
+
function createPlainStorage(engine = 'localStorage') {
|
|
1528
|
+
const defaultEngine = typeof window !== 'undefined'
|
|
1529
|
+
? (engine === 'sessionStorage' ? window.sessionStorage : window.localStorage)
|
|
1530
|
+
: {};
|
|
1531
|
+
return new PlainStorage(defaultEngine);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* PlainCookie - Una interfaz consistente con SecureCookie pero sin cifrado.
|
|
1536
|
+
*/
|
|
1537
|
+
class PlainCookie {
|
|
1538
|
+
constructor(defaultOptions) {
|
|
1539
|
+
this.logger = new Logger({ prefix: 'PlainCookie' });
|
|
1540
|
+
this.defaultOptions = { path: '/', ...defaultOptions };
|
|
1541
|
+
}
|
|
1542
|
+
serializeOptions(options) {
|
|
1543
|
+
let cookieString = '';
|
|
1544
|
+
if (options.expires)
|
|
1545
|
+
cookieString += `; expires=${options.expires.toUTCString()}`;
|
|
1546
|
+
if (options.maxAge)
|
|
1547
|
+
cookieString += `; max-age=${options.maxAge}`;
|
|
1548
|
+
if (options.domain)
|
|
1549
|
+
cookieString += `; domain=${options.domain}`;
|
|
1550
|
+
if (options.path)
|
|
1551
|
+
cookieString += `; path=${options.path}`;
|
|
1552
|
+
if (options.secure)
|
|
1553
|
+
cookieString += `; secure`;
|
|
1554
|
+
if (options.sameSite)
|
|
1555
|
+
cookieString += `; samesite=${options.sameSite}`;
|
|
1556
|
+
return cookieString;
|
|
1557
|
+
}
|
|
1558
|
+
async set(name, value, options) {
|
|
1559
|
+
try {
|
|
1560
|
+
const serialized = typeof value === 'string' ? value : JSON.stringify(value);
|
|
1561
|
+
const mergedOptions = { ...this.defaultOptions, ...options };
|
|
1562
|
+
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(serialized)}${this.serializeOptions(mergedOptions)}`;
|
|
1563
|
+
}
|
|
1564
|
+
catch (error) {
|
|
1565
|
+
this.logger.error(`Error saving unencrypted cookie ${name}:`, error);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
async get(name) {
|
|
1569
|
+
try {
|
|
1570
|
+
const nameEQ = encodeURIComponent(name) + '=';
|
|
1571
|
+
const ca = document.cookie.split(';');
|
|
1572
|
+
for (let i = 0; i < ca.length; i++) {
|
|
1573
|
+
let c = ca[i];
|
|
1574
|
+
while (c.charAt(0) === ' ')
|
|
1575
|
+
c = c.substring(1, c.length);
|
|
1576
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
1577
|
+
const decoded = decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
1578
|
+
try {
|
|
1579
|
+
return JSON.parse(decoded);
|
|
1580
|
+
}
|
|
1581
|
+
catch {
|
|
1582
|
+
return decoded;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return null;
|
|
1587
|
+
}
|
|
1588
|
+
catch (error) {
|
|
1589
|
+
this.logger.error(`Error reading unencrypted cookie ${name}:`, error);
|
|
1590
|
+
return null;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
async remove(name, options) {
|
|
1594
|
+
const mergedOptions = { ...this.defaultOptions, ...options };
|
|
1595
|
+
document.cookie = `${encodeURIComponent(name)}=${this.serializeOptions({ ...mergedOptions, expires: new Date(0), maxAge: 0 })}`;
|
|
1596
|
+
}
|
|
1597
|
+
clearAll() {
|
|
1598
|
+
const cookies = document.cookie.split(';');
|
|
1599
|
+
for (let i = 0; i < cookies.length; i++) {
|
|
1600
|
+
const cookie = cookies[i];
|
|
1601
|
+
const eqPos = cookie.indexOf('=');
|
|
1602
|
+
const name = eqPos > -1 ? cookie.substring(0, eqPos).trim() : cookie.trim();
|
|
1603
|
+
this.remove(decodeURIComponent(name));
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* Crea una instancia de manejador de cookies encriptadas.
|
|
1610
|
+
* @param secretKey Llave secreta opcional para derivación de claves
|
|
1611
|
+
* @param domain Dominio opcional (incluir punto inicial para subdominios ej. '.dominio.com')
|
|
1612
|
+
* @param config Configuración adicional opcional
|
|
1613
|
+
* @returns Instancia de SecureCookie
|
|
1614
|
+
*/
|
|
1615
|
+
function createEncryptedCookieManager(secretKey, domain, config) {
|
|
1616
|
+
const mergedConfig = {
|
|
1617
|
+
...config,
|
|
1618
|
+
encryption: {
|
|
1619
|
+
...config?.encryption,
|
|
1620
|
+
...(secretKey ? { appIdentifier: secretKey } : {})
|
|
1621
|
+
},
|
|
1622
|
+
cookieOptions: {
|
|
1623
|
+
...config?.cookieOptions,
|
|
1624
|
+
...(domain ? { domain } : {})
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
return SecureCookie.getInstance(mergedConfig);
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Crea una instancia de manejador de cookies plano (sin cifrar).
|
|
1631
|
+
* @param defaultOptions Opciones por defecto para las cookies (ej. domain)
|
|
1632
|
+
* @returns Instancia de PlainCookie
|
|
1633
|
+
*/
|
|
1634
|
+
function createPlainCookieManager(defaultOptions) {
|
|
1635
|
+
return new PlainCookie(defaultOptions);
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* Instancia predeterminada de almacenamiento local (sin cifrar).
|
|
1640
|
+
*/
|
|
1641
|
+
const localStore = createPlainStorage('localStorage');
|
|
1642
|
+
/**
|
|
1643
|
+
* Instancia predeterminada de almacenamiento de sesión (sin cifrar).
|
|
1644
|
+
*/
|
|
1645
|
+
const sessionStore = createPlainStorage('sessionStorage');
|
|
1646
|
+
/**
|
|
1647
|
+
* Instancia predeterminada de gestor de cookies (sin cifrar).
|
|
1648
|
+
*/
|
|
1649
|
+
const cookies = createPlainCookieManager();
|
|
1650
|
+
|
|
1210
1651
|
/**
|
|
1211
1652
|
* @bantis/local-cipher - Core Module
|
|
1212
1653
|
* Framework-agnostic client-side encryption for browser storage
|
|
@@ -1216,8 +1657,9 @@ async function forceMigration(customKeys) {
|
|
|
1216
1657
|
*/
|
|
1217
1658
|
// Core classes
|
|
1218
1659
|
const secureStorage$1 = SecureStorage.getInstance();
|
|
1660
|
+
const secureCookie = SecureCookie.getInstance();
|
|
1219
1661
|
// Version
|
|
1220
|
-
const VERSION = '2.
|
|
1662
|
+
const VERSION = '2.2.0';
|
|
1221
1663
|
|
|
1222
1664
|
/**
|
|
1223
1665
|
* React Hooks for @bantis/local-cipher
|
|
@@ -1397,18 +1839,35 @@ function useSecureStorageDebug(storage) {
|
|
|
1397
1839
|
}, [secureStorage]);
|
|
1398
1840
|
return debugInfo;
|
|
1399
1841
|
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Hook para usar un namespace de SecureStorage
|
|
1402
|
-
*/
|
|
1403
|
-
function useNamespace(namespace, storage) {
|
|
1404
|
-
const secureStorage = storage || getDefaultStorage();
|
|
1405
|
-
const [namespacedStorage] = useState(() => secureStorage.namespace(namespace));
|
|
1406
|
-
return namespacedStorage;
|
|
1407
|
-
}
|
|
1408
1842
|
/**
|
|
1409
1843
|
* Exportar la instancia de SecureStorage para uso directo
|
|
1410
1844
|
*/
|
|
1411
1845
|
const secureStorage = getDefaultStorage();
|
|
1846
|
+
/**
|
|
1847
|
+
* Hook para sincronizar el estado de React con el Storage Manager (por defecto localStore en texto plano)
|
|
1848
|
+
*/
|
|
1849
|
+
function useLocalStore(key, initialValue, storageManager = localStore) {
|
|
1850
|
+
const [value, setValue] = useState(initialValue);
|
|
1851
|
+
useEffect(() => {
|
|
1852
|
+
let mounted = true;
|
|
1853
|
+
storageManager.get(key).then((saved) => {
|
|
1854
|
+
if (!mounted)
|
|
1855
|
+
return;
|
|
1856
|
+
if (saved !== null) {
|
|
1857
|
+
setValue(saved);
|
|
1858
|
+
}
|
|
1859
|
+
else {
|
|
1860
|
+
storageManager.set(key, initialValue);
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
return () => { mounted = false; };
|
|
1864
|
+
}, [key, storageManager]);
|
|
1865
|
+
const setNewValue = useCallback(async (newValue) => {
|
|
1866
|
+
setValue(newValue);
|
|
1867
|
+
await storageManager.set(key, newValue);
|
|
1868
|
+
}, [key, storageManager]);
|
|
1869
|
+
return [value, setNewValue];
|
|
1870
|
+
}
|
|
1412
1871
|
|
|
1413
|
-
export { DEFAULT_CONFIG, EncryptionHelper, EventEmitter, KeyRotation, LIBRARY_VERSION, Logger, NamespacedStorage, STORAGE_VERSION, SecureStorage, VERSION, compress, debugEncryptionState, decompress, forceMigration, initializeSecureStorage, isCompressionSupported, secureStorage as reactSecureStorage, secureStorage$1 as secureStorage, shouldCompress,
|
|
1872
|
+
export { DEFAULT_CONFIG, EncryptionHelper, EventEmitter, KeyRotation, LIBRARY_VERSION, Logger, NamespacedStorage, PlainCookie, PlainStorage, STORAGE_VERSION, SecureCookie, SecureStorage, VERSION, compress, cookies, createEncryptedCookieManager, createEncryptedStorage, createPlainCookieManager, createPlainStorage, debugEncryptionState, decompress, forceMigration, initializeSecureStorage, isCompressionSupported, localStore, secureStorage as reactSecureStorage, secureCookie, secureStorage$1 as secureStorage, sessionStore, shouldCompress, useLocalStore, useSecureStorage, useSecureStorageDebug, useSecureStorageEvents, useSecureStorageItem, useSecureStorageWithExpiry };
|
|
1414
1873
|
//# sourceMappingURL=react.esm.js.map
|