@bantis/local-cipher 1.0.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.
@@ -0,0 +1,767 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { Injectable } from '@angular/core';
3
+ import { BehaviorSubject, from } from 'rxjs';
4
+ import { map, catchError } from 'rxjs/operators';
5
+
6
+ /**
7
+ * EncryptionHelper - Clase responsable de todas las operaciones criptográficas
8
+ * Implementa AES-256-GCM con derivación de claves PBKDF2 y fingerprinting del navegador
9
+ */
10
+ class EncryptionHelper {
11
+ constructor() {
12
+ // Propiedades privadas
13
+ this.key = null;
14
+ this.baseKey = '';
15
+ this.baseKeyPromise = null;
16
+ }
17
+ /**
18
+ * Genera un fingerprint único del navegador
19
+ * Combina múltiples características del navegador para crear una huella digital
20
+ */
21
+ async generateBaseKey() {
22
+ if (this.baseKeyPromise) {
23
+ return this.baseKeyPromise;
24
+ }
25
+ this.baseKeyPromise = (async () => {
26
+ const components = [
27
+ navigator.userAgent,
28
+ navigator.language,
29
+ screen.width.toString(),
30
+ screen.height.toString(),
31
+ screen.colorDepth.toString(),
32
+ new Intl.DateTimeFormat().resolvedOptions().timeZone,
33
+ EncryptionHelper.APP_IDENTIFIER,
34
+ ];
35
+ const fingerprint = components.join('|');
36
+ this.baseKey = await this.hashString(fingerprint);
37
+ return this.baseKey;
38
+ })();
39
+ return this.baseKeyPromise;
40
+ }
41
+ /**
42
+ * Hashea un string usando SHA-256
43
+ * @param str - String a hashear
44
+ * @returns Hash hexadecimal
45
+ */
46
+ async hashString(str) {
47
+ const encoder = new TextEncoder();
48
+ const data = encoder.encode(str);
49
+ const hashBuffer = await crypto.subtle.digest(EncryptionHelper.HASH_ALGORITHM, data);
50
+ return this.arrayBufferToHex(hashBuffer);
51
+ }
52
+ /**
53
+ * Deriva una clave criptográfica usando PBKDF2
54
+ * @param password - Password base (fingerprint)
55
+ * @param salt - Salt aleatorio
56
+ * @returns CryptoKey para AES-GCM
57
+ */
58
+ async deriveKey(password, salt) {
59
+ const encoder = new TextEncoder();
60
+ const passwordBuffer = encoder.encode(password);
61
+ // Importar el password como material de clave
62
+ const keyMaterial = await crypto.subtle.importKey('raw', passwordBuffer, 'PBKDF2', false, ['deriveBits', 'deriveKey']);
63
+ // Derivar la clave AES-GCM
64
+ return crypto.subtle.deriveKey({
65
+ name: 'PBKDF2',
66
+ salt,
67
+ iterations: EncryptionHelper.ITERATIONS,
68
+ hash: EncryptionHelper.HASH_ALGORITHM,
69
+ }, keyMaterial, {
70
+ name: EncryptionHelper.ALGORITHM,
71
+ length: EncryptionHelper.KEY_LENGTH,
72
+ }, false, ['encrypt', 'decrypt']);
73
+ }
74
+ /**
75
+ * Inicializa el sistema de encriptación generando un nuevo salt
76
+ */
77
+ async initialize() {
78
+ // Generar salt aleatorio
79
+ const salt = crypto.getRandomValues(new Uint8Array(EncryptionHelper.SALT_LENGTH));
80
+ // Obtener y hashear el baseKey
81
+ const baseKey = await this.generateBaseKey();
82
+ // Derivar la clave
83
+ this.key = await this.deriveKey(baseKey, salt);
84
+ // Guardar el salt en localStorage
85
+ localStorage.setItem(EncryptionHelper.SALT_STORAGE_KEY, this.arrayBufferToBase64(salt.buffer));
86
+ }
87
+ /**
88
+ * Inicializa desde un salt almacenado previamente
89
+ */
90
+ async initializeFromStored() {
91
+ const storedSalt = localStorage.getItem(EncryptionHelper.SALT_STORAGE_KEY);
92
+ if (!storedSalt) {
93
+ // Si no hay salt almacenado, inicializar uno nuevo
94
+ await this.initialize();
95
+ return;
96
+ }
97
+ // Recuperar el salt
98
+ const salt = new Uint8Array(this.base64ToArrayBuffer(storedSalt));
99
+ // Obtener el baseKey
100
+ const baseKey = await this.generateBaseKey();
101
+ // Derivar la misma clave
102
+ this.key = await this.deriveKey(baseKey, salt);
103
+ }
104
+ /**
105
+ * Encripta un texto plano usando AES-256-GCM
106
+ * @param plaintext - Texto a encriptar
107
+ * @returns Texto encriptado en Base64 (IV + datos encriptados)
108
+ */
109
+ async encrypt(plaintext) {
110
+ if (!this.key) {
111
+ await this.initializeFromStored();
112
+ }
113
+ if (!this.key) {
114
+ throw new Error('No se pudo inicializar la clave de encriptación');
115
+ }
116
+ // Convertir texto a bytes
117
+ const encoder = new TextEncoder();
118
+ const data = encoder.encode(plaintext);
119
+ // Generar IV aleatorio
120
+ const iv = crypto.getRandomValues(new Uint8Array(EncryptionHelper.IV_LENGTH));
121
+ // Encriptar
122
+ const encryptedBuffer = await crypto.subtle.encrypt({
123
+ name: EncryptionHelper.ALGORITHM,
124
+ iv,
125
+ }, this.key, data);
126
+ // Combinar IV + datos encriptados
127
+ const combined = new Uint8Array(iv.length + encryptedBuffer.byteLength);
128
+ combined.set(iv, 0);
129
+ combined.set(new Uint8Array(encryptedBuffer), iv.length);
130
+ // Retornar en Base64
131
+ return this.arrayBufferToBase64(combined.buffer);
132
+ }
133
+ /**
134
+ * Desencripta un texto encriptado
135
+ * @param ciphertext - Texto encriptado en Base64
136
+ * @returns Texto plano
137
+ */
138
+ async decrypt(ciphertext) {
139
+ if (!this.key) {
140
+ await this.initializeFromStored();
141
+ }
142
+ if (!this.key) {
143
+ throw new Error('No se pudo inicializar la clave de encriptación');
144
+ }
145
+ try {
146
+ // Decodificar de Base64
147
+ const combined = new Uint8Array(this.base64ToArrayBuffer(ciphertext));
148
+ // Extraer IV y datos encriptados
149
+ const iv = combined.slice(0, EncryptionHelper.IV_LENGTH);
150
+ const encryptedData = combined.slice(EncryptionHelper.IV_LENGTH);
151
+ // Desencriptar
152
+ const decryptedBuffer = await crypto.subtle.decrypt({
153
+ name: EncryptionHelper.ALGORITHM,
154
+ iv,
155
+ }, this.key, encryptedData);
156
+ // Convertir bytes a texto
157
+ const decoder = new TextDecoder();
158
+ return decoder.decode(decryptedBuffer);
159
+ }
160
+ catch (error) {
161
+ throw new Error(`Error al desencriptar: ${error instanceof Error ? error.message : 'Error desconocido'}`);
162
+ }
163
+ }
164
+ /**
165
+ * Encripta el nombre de una clave para ofuscar nombres en localStorage
166
+ * @param keyName - Nombre original de la clave
167
+ * @returns Nombre encriptado con prefijo __enc_
168
+ */
169
+ async encryptKey(keyName) {
170
+ const baseKey = await this.generateBaseKey();
171
+ const combined = keyName + baseKey;
172
+ const hash = await this.hashString(combined);
173
+ return `__enc_${hash.substring(0, 16)}`;
174
+ }
175
+ /**
176
+ * Limpia todos los datos encriptados del localStorage
177
+ */
178
+ clearEncryptedData() {
179
+ const keysToRemove = [];
180
+ // Identificar todas las claves encriptadas
181
+ for (let i = 0; i < localStorage.length; i++) {
182
+ const key = localStorage.key(i);
183
+ if (key && key.startsWith('__enc_')) {
184
+ keysToRemove.push(key);
185
+ }
186
+ }
187
+ // Eliminar claves encriptadas
188
+ keysToRemove.forEach(key => localStorage.removeItem(key));
189
+ // Eliminar salt
190
+ localStorage.removeItem(EncryptionHelper.SALT_STORAGE_KEY);
191
+ // Resetear clave en memoria
192
+ this.key = null;
193
+ this.baseKey = '';
194
+ this.baseKeyPromise = null;
195
+ }
196
+ /**
197
+ * Verifica si el navegador soporta Web Crypto API
198
+ */
199
+ static isSupported() {
200
+ return !!(typeof crypto !== 'undefined' &&
201
+ crypto.subtle &&
202
+ crypto.getRandomValues);
203
+ }
204
+ // Métodos auxiliares para conversión de datos
205
+ arrayBufferToBase64(buffer) {
206
+ const bytes = new Uint8Array(buffer);
207
+ let binary = '';
208
+ for (let i = 0; i < bytes.length; i++) {
209
+ binary += String.fromCharCode(bytes[i]);
210
+ }
211
+ return btoa(binary);
212
+ }
213
+ base64ToArrayBuffer(base64) {
214
+ const binary = atob(base64);
215
+ const bytes = new Uint8Array(binary.length);
216
+ for (let i = 0; i < binary.length; i++) {
217
+ bytes[i] = binary.charCodeAt(i);
218
+ }
219
+ return bytes.buffer;
220
+ }
221
+ arrayBufferToHex(buffer) {
222
+ const bytes = new Uint8Array(buffer);
223
+ return Array.from(bytes)
224
+ .map(b => b.toString(16).padStart(2, '0'))
225
+ .join('');
226
+ }
227
+ }
228
+ // Constantes criptográficas
229
+ EncryptionHelper.ALGORITHM = 'AES-GCM';
230
+ EncryptionHelper.KEY_LENGTH = 256;
231
+ EncryptionHelper.IV_LENGTH = 12; // 96 bits para GCM
232
+ EncryptionHelper.SALT_LENGTH = 16; // 128 bits
233
+ EncryptionHelper.ITERATIONS = 100000;
234
+ EncryptionHelper.HASH_ALGORITHM = 'SHA-256';
235
+ EncryptionHelper.SALT_STORAGE_KEY = '__app_salt';
236
+ EncryptionHelper.APP_IDENTIFIER = 'mtt-local-cipher-v1'; // Identificador único de la app
237
+
238
+ /**
239
+ * SecureStorage - API de alto nivel que imita localStorage con cifrado automático
240
+ * Implementa el patrón Singleton
241
+ */
242
+ class SecureStorage {
243
+ constructor() {
244
+ this.encryptionHelper = new EncryptionHelper();
245
+ }
246
+ /**
247
+ * Obtiene la instancia singleton de SecureStorage
248
+ */
249
+ static getInstance() {
250
+ if (!SecureStorage.instance) {
251
+ SecureStorage.instance = new SecureStorage();
252
+ }
253
+ return SecureStorage.instance;
254
+ }
255
+ /**
256
+ * Guarda un valor encriptado en localStorage
257
+ * @param key - Clave para almacenar
258
+ * @param value - Valor a encriptar y almacenar
259
+ */
260
+ async setItem(key, value) {
261
+ if (!EncryptionHelper.isSupported()) {
262
+ console.warn('Web Crypto API no soportada, usando localStorage sin encriptar');
263
+ localStorage.setItem(key, value);
264
+ return;
265
+ }
266
+ try {
267
+ // Encriptar el nombre de la clave
268
+ const encryptedKey = await this.encryptionHelper.encryptKey(key);
269
+ // Encriptar el valor
270
+ const encryptedValue = await this.encryptionHelper.encrypt(value);
271
+ // Guardar en localStorage
272
+ localStorage.setItem(encryptedKey, encryptedValue);
273
+ }
274
+ catch (error) {
275
+ console.error('Error al guardar dato encriptado, usando fallback:', error);
276
+ localStorage.setItem(key, value);
277
+ }
278
+ }
279
+ /**
280
+ * Recupera y desencripta un valor de localStorage
281
+ * @param key - Clave a buscar
282
+ * @returns Valor desencriptado o null si no existe
283
+ */
284
+ async getItem(key) {
285
+ if (!EncryptionHelper.isSupported()) {
286
+ return localStorage.getItem(key);
287
+ }
288
+ try {
289
+ // Encriptar el nombre de la clave
290
+ const encryptedKey = await this.encryptionHelper.encryptKey(key);
291
+ // Buscar el valor encriptado
292
+ let encryptedValue = localStorage.getItem(encryptedKey);
293
+ // Retrocompatibilidad: si no existe con clave encriptada, buscar con clave normal
294
+ if (!encryptedValue) {
295
+ encryptedValue = localStorage.getItem(key);
296
+ if (!encryptedValue) {
297
+ return null;
298
+ }
299
+ // Si encontramos un valor con clave normal, intentar desencriptarlo
300
+ // (podría ser un valor ya encriptado pero con clave antigua)
301
+ }
302
+ // Desencriptar el valor
303
+ return await this.encryptionHelper.decrypt(encryptedValue);
304
+ }
305
+ catch (error) {
306
+ console.error('Error al recuperar dato encriptado:', error);
307
+ // Fallback: intentar leer directamente
308
+ return localStorage.getItem(key);
309
+ }
310
+ }
311
+ /**
312
+ * Elimina un valor de localStorage
313
+ * @param key - Clave a eliminar
314
+ */
315
+ async removeItem(key) {
316
+ if (!EncryptionHelper.isSupported()) {
317
+ localStorage.removeItem(key);
318
+ return;
319
+ }
320
+ try {
321
+ // Encriptar el nombre de la clave
322
+ const encryptedKey = await this.encryptionHelper.encryptKey(key);
323
+ // Eliminar ambas versiones (encriptada y normal) por seguridad
324
+ localStorage.removeItem(encryptedKey);
325
+ localStorage.removeItem(key);
326
+ }
327
+ catch (error) {
328
+ console.error('Error al eliminar dato encriptado:', error);
329
+ localStorage.removeItem(key);
330
+ }
331
+ }
332
+ /**
333
+ * Verifica si existe un valor para la clave dada
334
+ * @param key - Clave a verificar
335
+ * @returns true si existe, false si no
336
+ */
337
+ async hasItem(key) {
338
+ const value = await this.getItem(key);
339
+ return value !== null;
340
+ }
341
+ /**
342
+ * Limpia todos los datos encriptados
343
+ */
344
+ clear() {
345
+ this.encryptionHelper.clearEncryptedData();
346
+ }
347
+ /**
348
+ * Migra datos existentes no encriptados a formato encriptado
349
+ * @param keys - Array de claves a migrar
350
+ */
351
+ async migrateExistingData(keys) {
352
+ if (!EncryptionHelper.isSupported()) {
353
+ console.warn('Web Crypto API no soportada, no se puede migrar');
354
+ return;
355
+ }
356
+ console.log(`🔄 Iniciando migración de ${keys.length} claves...`);
357
+ for (const key of keys) {
358
+ try {
359
+ // Leer el valor no encriptado
360
+ const value = localStorage.getItem(key);
361
+ if (value === null) {
362
+ continue; // La clave no existe, saltar
363
+ }
364
+ // Verificar si ya está encriptado intentando desencriptarlo
365
+ try {
366
+ await this.encryptionHelper.decrypt(value);
367
+ console.log(`✓ ${key} ya está encriptado, saltando`);
368
+ continue;
369
+ }
370
+ catch {
371
+ // No está encriptado, proceder con la migración
372
+ }
373
+ // Guardar usando setItem (que encriptará automáticamente)
374
+ await this.setItem(key, value);
375
+ // Eliminar la versión no encriptada
376
+ localStorage.removeItem(key);
377
+ console.log(`✓ ${key} migrado exitosamente`);
378
+ }
379
+ catch (error) {
380
+ console.error(`✗ Error al migrar ${key}:`, error);
381
+ }
382
+ }
383
+ console.log('✅ Migración completada');
384
+ }
385
+ /**
386
+ * Obtiene información de debug sobre el estado del almacenamiento
387
+ */
388
+ getDebugInfo() {
389
+ const allKeys = [];
390
+ for (let i = 0; i < localStorage.length; i++) {
391
+ const key = localStorage.key(i);
392
+ if (key)
393
+ allKeys.push(key);
394
+ }
395
+ const encryptedKeys = allKeys.filter(key => key.startsWith('__enc_'));
396
+ const unencryptedKeys = allKeys.filter(key => !key.startsWith('__enc_') && key !== '__app_salt');
397
+ return {
398
+ cryptoSupported: EncryptionHelper.isSupported(),
399
+ encryptedKeys,
400
+ unencryptedKeys,
401
+ totalKeys: allKeys.length,
402
+ };
403
+ }
404
+ }
405
+ SecureStorage.instance = null;
406
+
407
+ const secureStorage$2 = SecureStorage.getInstance();
408
+ /**
409
+ * Hook de React para usar SecureStorage de forma reactiva
410
+ * Similar a useState pero con persistencia encriptada
411
+ *
412
+ * @param key - Clave para almacenar en localStorage
413
+ * @param initialValue - Valor inicial si no existe en storage
414
+ * @returns [value, setValue, loading, error]
415
+ *
416
+ * @example
417
+ * const [token, setToken, loading] = useSecureStorage('accessToken', '');
418
+ */
419
+ function useSecureStorage(key, initialValue) {
420
+ const [storedValue, setStoredValue] = useState(initialValue);
421
+ const [loading, setLoading] = useState(true);
422
+ const [error, setError] = useState(null);
423
+ // Cargar valor inicial
424
+ useEffect(() => {
425
+ const loadValue = async () => {
426
+ try {
427
+ setLoading(true);
428
+ setError(null);
429
+ const item = await secureStorage$2.getItem(key);
430
+ if (item !== null) {
431
+ // Si T es un objeto, parsear JSON
432
+ if (typeof initialValue === 'object') {
433
+ setStoredValue(JSON.parse(item));
434
+ }
435
+ else {
436
+ setStoredValue(item);
437
+ }
438
+ }
439
+ else {
440
+ setStoredValue(initialValue);
441
+ }
442
+ }
443
+ catch (err) {
444
+ setError(err instanceof Error ? err : new Error('Error al cargar valor'));
445
+ setStoredValue(initialValue);
446
+ }
447
+ finally {
448
+ setLoading(false);
449
+ }
450
+ };
451
+ loadValue();
452
+ }, [key]); // Solo recargar si cambia la clave
453
+ // Función para actualizar el valor
454
+ const setValue = useCallback(async (value) => {
455
+ try {
456
+ setError(null);
457
+ setStoredValue(value);
458
+ // Si T es un objeto, convertir a JSON
459
+ const valueToStore = typeof value === 'object'
460
+ ? JSON.stringify(value)
461
+ : String(value);
462
+ await secureStorage$2.setItem(key, valueToStore);
463
+ }
464
+ catch (err) {
465
+ setError(err instanceof Error ? err : new Error('Error al guardar valor'));
466
+ throw err;
467
+ }
468
+ }, [key]);
469
+ return [storedValue, setValue, loading, error];
470
+ }
471
+ /**
472
+ * Hook para verificar si una clave existe en SecureStorage
473
+ *
474
+ * @param key - Clave a verificar
475
+ * @returns [exists, loading, error]
476
+ *
477
+ * @example
478
+ * const [hasToken, loading] = useSecureStorageItem('accessToken');
479
+ */
480
+ function useSecureStorageItem(key) {
481
+ const [exists, setExists] = useState(false);
482
+ const [loading, setLoading] = useState(true);
483
+ const [error, setError] = useState(null);
484
+ useEffect(() => {
485
+ const checkExists = async () => {
486
+ try {
487
+ setLoading(true);
488
+ setError(null);
489
+ const hasItem = await secureStorage$2.hasItem(key);
490
+ setExists(hasItem);
491
+ }
492
+ catch (err) {
493
+ setError(err instanceof Error ? err : new Error('Error al verificar clave'));
494
+ setExists(false);
495
+ }
496
+ finally {
497
+ setLoading(false);
498
+ }
499
+ };
500
+ checkExists();
501
+ }, [key]);
502
+ return [exists, loading, error];
503
+ }
504
+ /**
505
+ * Hook para obtener información de debug del almacenamiento
506
+ *
507
+ * @returns Información de debug
508
+ *
509
+ * @example
510
+ * const debugInfo = useSecureStorageDebug();
511
+ * console.log(`Claves encriptadas: ${debugInfo.encryptedKeys.length}`);
512
+ */
513
+ function useSecureStorageDebug() {
514
+ const [debugInfo, setDebugInfo] = useState(secureStorage$2.getDebugInfo());
515
+ useEffect(() => {
516
+ // Actualizar cada segundo
517
+ const interval = setInterval(() => {
518
+ setDebugInfo(secureStorage$2.getDebugInfo());
519
+ }, 1000);
520
+ return () => clearInterval(interval);
521
+ }, []);
522
+ return debugInfo;
523
+ }
524
+
525
+ /******************************************************************************
526
+ Copyright (c) Microsoft Corporation.
527
+
528
+ Permission to use, copy, modify, and/or distribute this software for any
529
+ purpose with or without fee is hereby granted.
530
+
531
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
532
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
533
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
534
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
535
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
536
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
537
+ PERFORMANCE OF THIS SOFTWARE.
538
+ ***************************************************************************** */
539
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
540
+
541
+
542
+ function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
543
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
544
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
545
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
546
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
547
+ var _, done = false;
548
+ for (var i = decorators.length - 1; i >= 0; i--) {
549
+ var context = {};
550
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
551
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
552
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
553
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
554
+ if (kind === "accessor") {
555
+ if (result === void 0) continue;
556
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
557
+ if (_ = accept(result.get)) descriptor.get = _;
558
+ if (_ = accept(result.set)) descriptor.set = _;
559
+ if (_ = accept(result.init)) initializers.unshift(_);
560
+ }
561
+ else if (_ = accept(result)) {
562
+ if (kind === "field") initializers.unshift(_);
563
+ else descriptor[key] = _;
564
+ }
565
+ }
566
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
567
+ done = true;
568
+ }
569
+ function __runInitializers(thisArg, initializers, value) {
570
+ var useValue = arguments.length > 2;
571
+ for (var i = 0; i < initializers.length; i++) {
572
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
573
+ }
574
+ return useValue ? value : void 0;
575
+ }
576
+ function __setFunctionName(f, name, prefix) {
577
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
578
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
579
+ }
580
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
581
+ var e = new Error(message);
582
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
583
+ };
584
+
585
+ /**
586
+ * Servicio de Angular para SecureStorage
587
+ * Proporciona una API reactiva usando RxJS Observables
588
+ *
589
+ * @example
590
+ * constructor(private secureStorage: SecureStorageService) {}
591
+ *
592
+ * // Guardar
593
+ * this.secureStorage.setItem('token', 'abc123').subscribe();
594
+ *
595
+ * // Leer
596
+ * this.secureStorage.getItem('token').subscribe(token => console.log(token));
597
+ */
598
+ let SecureStorageService = (() => {
599
+ let _classDecorators = [Injectable({
600
+ providedIn: 'root'
601
+ })];
602
+ let _classDescriptor;
603
+ let _classExtraInitializers = [];
604
+ let _classThis;
605
+ _classThis = class {
606
+ constructor() {
607
+ this.debugInfo$ = new BehaviorSubject(this.getDebugInfo());
608
+ this.storage = SecureStorage.getInstance();
609
+ // Actualizar debug info cada segundo
610
+ setInterval(() => {
611
+ this.debugInfo$.next(this.getDebugInfo());
612
+ }, 1000);
613
+ }
614
+ /**
615
+ * Guarda un valor encriptado
616
+ * @param key - Clave
617
+ * @param value - Valor a guardar
618
+ * @returns Observable que completa cuando se guarda
619
+ */
620
+ setItem(key, value) {
621
+ return from(this.storage.setItem(key, value));
622
+ }
623
+ /**
624
+ * Recupera un valor desencriptado
625
+ * @param key - Clave
626
+ * @returns Observable con el valor o null
627
+ */
628
+ getItem(key) {
629
+ return from(this.storage.getItem(key));
630
+ }
631
+ /**
632
+ * Elimina un valor
633
+ * @param key - Clave a eliminar
634
+ * @returns Observable que completa cuando se elimina
635
+ */
636
+ removeItem(key) {
637
+ return from(this.storage.removeItem(key));
638
+ }
639
+ /**
640
+ * Verifica si existe una clave
641
+ * @param key - Clave a verificar
642
+ * @returns Observable con true/false
643
+ */
644
+ hasItem(key) {
645
+ return from(this.storage.hasItem(key));
646
+ }
647
+ /**
648
+ * Limpia todos los datos encriptados
649
+ */
650
+ clear() {
651
+ this.storage.clear();
652
+ }
653
+ /**
654
+ * Migra datos existentes a formato encriptado
655
+ * @param keys - Array de claves a migrar
656
+ * @returns Observable que completa cuando termina la migración
657
+ */
658
+ migrateExistingData(keys) {
659
+ return from(this.storage.migrateExistingData(keys));
660
+ }
661
+ /**
662
+ * Obtiene información de debug como Observable
663
+ * @returns Observable con información de debug que se actualiza automáticamente
664
+ */
665
+ getDebugInfo$() {
666
+ return this.debugInfo$.asObservable();
667
+ }
668
+ /**
669
+ * Obtiene información de debug de forma síncrona
670
+ */
671
+ getDebugInfo() {
672
+ return this.storage.getDebugInfo();
673
+ }
674
+ /**
675
+ * Helper para guardar objetos JSON
676
+ * @param key - Clave
677
+ * @param value - Objeto a guardar
678
+ */
679
+ setObject(key, value) {
680
+ return this.setItem(key, JSON.stringify(value));
681
+ }
682
+ /**
683
+ * Helper para recuperar objetos JSON
684
+ * @param key - Clave
685
+ * @returns Observable con el objeto parseado o null
686
+ */
687
+ getObject(key) {
688
+ return this.getItem(key).pipe(map(value => value ? JSON.parse(value) : null), catchError(() => from([null])));
689
+ }
690
+ };
691
+ __setFunctionName(_classThis, "SecureStorageService");
692
+ (() => {
693
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
694
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
695
+ _classThis = _classDescriptor.value;
696
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
697
+ __runInitializers(_classThis, _classExtraInitializers);
698
+ })();
699
+ return _classThis;
700
+ })();
701
+
702
+ const secureStorage$1 = SecureStorage.getInstance();
703
+ /**
704
+ * Función de debug para verificar el estado del sistema de encriptación
705
+ * Muestra información detallada en la consola
706
+ */
707
+ async function debugEncryptionState() {
708
+ console.group('🔐 Estado del Sistema de Encriptación');
709
+ console.log('Soporte Crypto API:', EncryptionHelper.isSupported());
710
+ // Obtener información de debug
711
+ const debugInfo = secureStorage$1.getDebugInfo();
712
+ console.log('Claves encriptadas:', debugInfo.encryptedKeys.length);
713
+ console.log('Claves sin encriptar:', debugInfo.unencryptedKeys);
714
+ console.log('Total de claves:', debugInfo.totalKeys);
715
+ if (debugInfo.encryptedKeys.length > 0) {
716
+ console.log('✅ Datos encriptados encontrados:');
717
+ debugInfo.encryptedKeys.forEach(key => {
718
+ const value = localStorage.getItem(key);
719
+ console.log(` ${key}: ${value?.substring(0, 30)}...`);
720
+ });
721
+ }
722
+ else {
723
+ console.log('⚠️ No se encontraron datos encriptados');
724
+ }
725
+ if (debugInfo.unencryptedKeys.length > 0) {
726
+ console.log('⚠️ Claves sin encriptar encontradas:');
727
+ debugInfo.unencryptedKeys.forEach(key => {
728
+ console.log(` ${key}`);
729
+ });
730
+ }
731
+ console.groupEnd();
732
+ }
733
+ /**
734
+ * Fuerza la migración de claves comunes a formato encriptado
735
+ * Útil para desarrollo y testing
736
+ */
737
+ async function forceMigration(customKeys) {
738
+ const defaultKeys = [
739
+ 'accessToken',
740
+ 'refreshToken',
741
+ 'user',
742
+ 'sessionId',
743
+ 'authToken',
744
+ 'userData',
745
+ ];
746
+ const keysToMigrate = customKeys || defaultKeys;
747
+ console.log(`🔄 Iniciando migración forzada de ${keysToMigrate.length} claves...`);
748
+ await secureStorage$1.migrateExistingData(keysToMigrate);
749
+ console.log('✅ Migración forzada completada');
750
+ // Mostrar estado después de la migración
751
+ await debugEncryptionState();
752
+ }
753
+
754
+ /**
755
+ * @mtt/local-cipher
756
+ * Librería de cifrado local AES-256-GCM para Angular, React y JavaScript
757
+ *
758
+ * @author MTT
759
+ * @license MIT
760
+ */
761
+ // Core exports
762
+ const secureStorage = SecureStorage.getInstance();
763
+ // Version
764
+ const VERSION = '1.0.0';
765
+
766
+ export { EncryptionHelper, SecureStorage, SecureStorageService, VERSION, debugEncryptionState, forceMigration, secureStorage$2 as reactSecureStorage, secureStorage, useSecureStorage, useSecureStorageDebug, useSecureStorageItem };
767
+ //# sourceMappingURL=index.esm.js.map