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