@mp-front/common 0.0.1 → 0.0.2-next.1

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 CHANGED
@@ -1 +1,1006 @@
1
- # front-utils
1
+ # @mp-front/common - Documentación Técnica
2
+
3
+ ## 📚 Tabla de Contenidos
4
+
5
+ 1. [Arquitectura General](#arquitectura-general)
6
+ 2. [Módulos y Componentes](#módulos-y-componentes)
7
+ 3. [Guías de Implementación](#guías-de-implementación)
8
+ 4. [Configuración](#configuración)
9
+ 5. [Casos de Uso Avanzados](#casos-de-uso-avanzados)
10
+
11
+ ---
12
+
13
+ ## 🏗️ Arquitectura General
14
+
15
+ La biblioteca `@mp-front/common` está diseñada con una arquitectura modular que permite importar solo los componentes necesarios, optimizando el tamaño del bundle final.
16
+
17
+ ### Estructura de Directorios
18
+
19
+ ```
20
+ src/
21
+ ├── adapters/ # Adaptadores NextAuth (Redis)
22
+ ├── auth/ # Servicios de autenticación
23
+ ├── cache/ # Gestión de caché
24
+ │ ├── session-handler/ # Manejo de sesiones
25
+ │ └── terminal-handler/ # Manejo de terminales
26
+ ├── cache-providers/ # Proveedores de caché
27
+ │ └── redis/ # Implementación Redis
28
+ ├── engine/ # Motor de autenticación Azure
29
+ ├── errors/ # Sistema de errores
30
+ ├── helpers/ # Utilidades generales
31
+ ├── http/ # Cliente HTTP
32
+ ├── middleware/ # Middlewares API
33
+ └── rxjs/ # Utilidades RxJS
34
+ ```
35
+
36
+ ### Principios de Diseño
37
+
38
+ - **Modularidad**: Cada módulo es independiente y puede importarse por separado
39
+ - **Programación Reactiva**: Uso extensivo de RxJS para operaciones asíncronas
40
+ - **Type Safety**: TypeScript completo con tipos estrictos
41
+ - **Singleton Pattern**: Para gestores globales (ErrorHandler, LoadingHandler)
42
+ - **Encriptación**: Seguridad en datos sensibles con node-jose
43
+
44
+ ---
45
+
46
+ ## 📦 Módulos y Componentes
47
+
48
+ ### 1. Auth Module (`@mp-front/common/auth`)
49
+
50
+ #### AuthorizationService
51
+
52
+ Servicio para obtener tokens de autenticación desde un backend.
53
+
54
+ **Características:**
55
+
56
+ - Autenticación mediante credenciales
57
+ - Retorna Observable con el token
58
+ - Logging integrado de requests/responses
59
+
60
+ **Implementación:**
61
+
62
+ ```typescript
63
+ import { AuthorizationService } from "@mp-front/common/auth"
64
+
65
+ const authService = new AuthorizationService()
66
+
67
+ // Obtener token
68
+ authService.get().subscribe({
69
+ next: token => {
70
+ console.log("Token obtenido:", token)
71
+ // Usar token en headers
72
+ },
73
+ error: error => {
74
+ console.error("Error de autenticación:", error)
75
+ },
76
+ })
77
+ ```
78
+
79
+ **Variables de entorno requeridas:**
80
+
81
+ ```env
82
+ API_AUTH_BACK_URL=https://api.example.com/auth
83
+ API_AUTH_BACK_USERNAME_AUTH=your_username
84
+ API_AUTH_BACK_PASSWORD_AUTH=your_password
85
+ ID_FRONT=frontend-app-id
86
+ ```
87
+
88
+ ---
89
+
90
+ ### 2. Engine Module (`@mp-front/common/engine`)
91
+
92
+ #### AuthEngine
93
+
94
+ Motor de autenticación para Azure Entra ID (anteriormente Azure AD).
95
+
96
+ **Características:**
97
+
98
+ - Verificación de roles de usuario
99
+ - Verificación de grupos de usuario
100
+ - Integración con Microsoft Graph API
101
+
102
+ **Métodos principales:**
103
+
104
+ ##### `getRoles(email: string, idObjApp: string)`
105
+
106
+ Obtiene los roles asignados a un usuario en una aplicación.
107
+
108
+ ```typescript
109
+ import { AuthEngine } from "@mp-front/common/engine"
110
+
111
+ const authEngine = new AuthEngine()
112
+
113
+ authEngine.getRoles("user@example.com", "app-object-id").subscribe({
114
+ next: ({ roles }) => {
115
+ console.log("Roles del usuario:", roles)
116
+ },
117
+ })
118
+ ```
119
+
120
+ ##### `inRole(email: string, idRole: string, idObjApp: string)`
121
+
122
+ Verifica si un usuario tiene un rol específico.
123
+
124
+ ```typescript
125
+ authEngine.inRole("user@example.com", "role-id", "app-object-id").subscribe({
126
+ next: hasRole => {
127
+ if (hasRole) {
128
+ console.log("Usuario tiene el rol")
129
+ }
130
+ },
131
+ })
132
+ ```
133
+
134
+ ##### `inGroup(email: string, idGroup: string)`
135
+
136
+ Verifica si un usuario pertenece a un grupo.
137
+
138
+ ```typescript
139
+ authEngine.inGroup("user@example.com", "group-id").subscribe({
140
+ next: inGroup => {
141
+ if (inGroup) {
142
+ console.log("Usuario pertenece al grupo")
143
+ }
144
+ },
145
+ })
146
+ ```
147
+
148
+ **Variables de entorno requeridas:**
149
+
150
+ ```env
151
+ AZURE_AD_GRAPH_GET_APP_ROLES=https://graph.microsoft.com/v1.0/applications/{id-obj}/appRoles
152
+ AZURE_AD_GRAPH_GET_USER_BY_EMAIL=https://graph.microsoft.com/v1.0/users/{user-mail}
153
+ AZURE_AD_GRAPH_GROUPS=https://graph.microsoft.com/v1.0/users/idUser/memberOf
154
+ ```
155
+
156
+ ---
157
+
158
+ ### 3. HTTP Module (`@mp-front/common/http`)
159
+
160
+ #### HttpClient
161
+
162
+ Cliente HTTP basado en RxJS con características avanzadas.
163
+
164
+ **Características:**
165
+
166
+ - Métodos HTTP: GET, POST, PUT, PATCH, DELETE
167
+ - Encoding/Decoding automático de datos
168
+ - Manejo de loading state global
169
+ - Manejo de errores centralizado
170
+ - Logging de requests/responses
171
+
172
+ **Métodos:**
173
+
174
+ ##### GET Request
175
+
176
+ ```typescript
177
+ import { HttpClient } from "@mp-front/common/http"
178
+
179
+ const client = new HttpClient()
180
+
181
+ interface User {
182
+ id: number
183
+ name: string
184
+ email: string
185
+ }
186
+
187
+ client.get<User>("/api/users/1").subscribe({
188
+ next: user => {
189
+ console.log("Usuario:", user)
190
+ },
191
+ error: error => {
192
+ console.error("Error:", error)
193
+ },
194
+ })
195
+
196
+ // Con parámetros
197
+ client
198
+ .get<User[]>("/api/users", {
199
+ params: { page: 1, limit: 10 },
200
+ })
201
+ .subscribe(users => {
202
+ console.log("Usuarios:", users)
203
+ })
204
+ ```
205
+
206
+ ##### POST Request
207
+
208
+ ```typescript
209
+ client
210
+ .post<User>("/api/users", {
211
+ name: "John Doe",
212
+ email: "john@example.com",
213
+ })
214
+ .subscribe(newUser => {
215
+ console.log("Usuario creado:", newUser)
216
+ })
217
+ ```
218
+
219
+ ##### PUT Request
220
+
221
+ ```typescript
222
+ client
223
+ .put<User>("/api/users/1", {
224
+ name: "Jane Doe",
225
+ })
226
+ .subscribe(updatedUser => {
227
+ console.log("Usuario actualizado:", updatedUser)
228
+ })
229
+ ```
230
+
231
+ ##### DELETE Request
232
+
233
+ ```typescript
234
+ client.delete("/api/users/1").subscribe(() => {
235
+ console.log("Usuario eliminado")
236
+ })
237
+ ```
238
+
239
+ ##### Deshabilitar Loading Global
240
+
241
+ ```typescript
242
+ const client = new HttpClient()
243
+ client.setIsLoadingEnabled(false)
244
+
245
+ // Este request no activará el loading global
246
+ client.get("/api/data").subscribe(data => {
247
+ console.log(data)
248
+ })
249
+ ```
250
+
251
+ ---
252
+
253
+ ### 4. Cache Module (`@mp-front/common/cache`)
254
+
255
+ #### SessionCache
256
+
257
+ Gestión de sesiones de usuario con Redis.
258
+
259
+ **Características:**
260
+
261
+ - Almacenamiento de sesión en Redis
262
+ - Renovación automática de timeout
263
+ - Integración con NextAuth
264
+ - Datos de usuario y tienda
265
+
266
+ **Métodos:**
267
+
268
+ ##### `getBasicSession()`
269
+
270
+ Obtiene la sesión básica del usuario.
271
+
272
+ ```typescript
273
+ import { SessionCache } from "@mp-front/common/cache"
274
+
275
+ const sessionCache = new SessionCache("user-id-123")
276
+
277
+ const session = await sessionCache.getBasicSession()
278
+
279
+ console.log("Usuario:", session.user)
280
+ // {
281
+ // name: 'John Doe',
282
+ // email: 'john@example.com',
283
+ // image: 'https://...',
284
+ // cveUsuario: '12345',
285
+ // rol: 'admin',
286
+ // appGroups: ['group1', 'group2']
287
+ // }
288
+ ```
289
+
290
+ ##### `getUserAndShoppingStore()`
291
+
292
+ Obtiene sesión completa con datos de tienda.
293
+
294
+ ```typescript
295
+ const fullSession = await sessionCache.getUserAndShoppingStore()
296
+
297
+ console.log("Sesión completa:", fullSession)
298
+ ```
299
+
300
+ #### TerminalCache
301
+
302
+ Gestión de datos de terminal con Redis.
303
+
304
+ **Características:**
305
+
306
+ - Almacenamiento por clave de vendedor
307
+ - Generación de UUID único por vendedor
308
+ - Timeout configurable
309
+
310
+ **Métodos:**
311
+
312
+ ##### `set(sellerKey: string, terminal: TerminalValue)`
313
+
314
+ Guarda datos de terminal.
315
+
316
+ ```typescript
317
+ import { TerminalCache } from "@mp-front/common/cache"
318
+
319
+ const terminalCache = new TerminalCache()
320
+
321
+ await terminalCache.set("SELLER001", {
322
+ terminalId: "TERM123",
323
+ location: "Store A",
324
+ status: "active",
325
+ })
326
+ ```
327
+
328
+ ##### `get(sellerKey: string)`
329
+
330
+ Obtiene datos de terminal.
331
+
332
+ ```typescript
333
+ const terminal = await terminalCache.get("SELLER001")
334
+
335
+ if (terminal) {
336
+ console.log("Terminal:", terminal)
337
+ }
338
+ ```
339
+
340
+ ##### `delete(sellerKey: string)`
341
+
342
+ Elimina datos de terminal.
343
+
344
+ ```typescript
345
+ await terminalCache.delete("SELLER001")
346
+ ```
347
+
348
+ ---
349
+
350
+ ### 5. Cache Providers Module (`@mp-front/common/cache-providers`)
351
+
352
+ #### RedisCache
353
+
354
+ Proveedor genérico de caché con Redis.
355
+
356
+ **Características:**
357
+
358
+ - Operaciones CRUD completas
359
+ - Encriptación opcional
360
+ - TTL (Time To Live) configurable
361
+ - Soporte para tipos genéricos
362
+
363
+ **Métodos:**
364
+
365
+ ##### `set(params: ISetStateParams<T>)`
366
+
367
+ Guarda datos en Redis.
368
+
369
+ ```typescript
370
+ import { RedisCache } from "@mp-front/common/cache-providers"
371
+
372
+ interface UserData {
373
+ name: string
374
+ email: string
375
+ }
376
+
377
+ const redis = new RedisCache<UserData>()
378
+
379
+ await redis.set({
380
+ prefix: "user:123",
381
+ idData: "profile",
382
+ body: {
383
+ name: "John Doe",
384
+ email: "john@example.com",
385
+ },
386
+ expire: 3600, // 1 hora en segundos
387
+ encrypted: true, // Opcional: encriptar datos
388
+ })
389
+ ```
390
+
391
+ ##### `get<T>(prefix: string, idData: string)`
392
+
393
+ Obtiene datos de Redis.
394
+
395
+ ```typescript
396
+ const { data, sha } = await redis.get<UserData>("user:123", "profile")
397
+
398
+ console.log("Datos:", data)
399
+ console.log("SHA key:", sha)
400
+ ```
401
+
402
+ ##### `delete(prefix: string, idData: string)`
403
+
404
+ Elimina datos de Redis.
405
+
406
+ ```typescript
407
+ await redis.delete("user:123", "profile")
408
+ ```
409
+
410
+ ##### Operaciones simples
411
+
412
+ ```typescript
413
+ // GET simple
414
+ const value = await redis.simpleGet("key")
415
+
416
+ // HGET (hash get)
417
+ const fieldValue = await redis.simpleHGet("hash-key", "field")
418
+
419
+ // HSET (hash set)
420
+ await redis.simpleHSet("hash-key", "field", "value")
421
+
422
+ // HGETALL (obtener todo el hash)
423
+ const allFields = await redis.simpleHGetAll("hash-key")
424
+ ```
425
+
426
+ **Variables de entorno:**
427
+
428
+ ```env
429
+ REDIS_URL=redis://localhost:6379
430
+ TIMEOUT_SESSION_MINUTES=60
431
+ SECRET_SIGNATURE=your-secret-key-for-encryption
432
+ ```
433
+
434
+ ---
435
+
436
+ ### 6. Helpers Module (`@mp-front/common/helpers`)
437
+
438
+ #### Logger
439
+
440
+ Sistema de logging con niveles y colores.
441
+
442
+ **Niveles de log:**
443
+
444
+ - `error`: Errores críticos
445
+ - `warn`: Advertencias
446
+ - `info`: Información general
447
+ - `http`: Logs HTTP
448
+ - `verbose`: Información detallada
449
+ - `debug`: Debugging
450
+ - `silly`: Todo
451
+
452
+ **Uso:**
453
+
454
+ ```typescript
455
+ import { Logger } from "@mp-front/common/helpers"
456
+
457
+ const logger = new Logger()
458
+
459
+ logger.logError("Error crítico", JSON.stringify(error))
460
+ logger.logWarn("Advertencia: API deprecated")
461
+ logger.logInfo("Usuario autenticado correctamente")
462
+ logger.logDebug("Datos de request:", JSON.stringify(data))
463
+ ```
464
+
465
+ **Configuración:**
466
+
467
+ ```env
468
+ NEXT_PUBLIC_APP_LOGS_NAME=MyApp
469
+ NEXT_PUBLIC_LOGS_LEVEL=debug
470
+ NEXT_PUBLIC_SILENT_LOGS=false
471
+ ```
472
+
473
+ #### Encoder
474
+
475
+ Codificación/decodificación de datos en Base64.
476
+
477
+ ```typescript
478
+ import { Encoder } from "@mp-front/common/helpers"
479
+
480
+ const encoder = new Encoder()
481
+
482
+ // Codificar
483
+ const encoded = encoder.encode({ name: "John" }, "request-id-123")
484
+ // Resultado: { info: "base64...", requestID: "request-id-123" }
485
+
486
+ // Decodificar
487
+ const decoded = encoder.decode(encoded)
488
+ // Resultado: { name: 'John' }
489
+ ```
490
+
491
+ #### Encrypter
492
+
493
+ Encriptación/desencriptación con node-jose (JWE).
494
+
495
+ ```typescript
496
+ import { Encrypter } from "@mp-front/common/helpers"
497
+
498
+ const encrypter = new Encrypter()
499
+
500
+ // Encriptar
501
+ const encrypted = await encrypter.encrypt({
502
+ password: "secret123",
503
+ apiKey: "key-xyz",
504
+ })
505
+
506
+ // Desencriptar
507
+ const decrypted = await encrypter.decrypt(encrypted)
508
+
509
+ // Verificar si está encriptado
510
+ const isEncrypted = await encrypter.isEncrypted(someString)
511
+
512
+ // Generar SHA256
513
+ const sha = encrypter.generateSHA({ userId: "123" })
514
+ ```
515
+
516
+ ---
517
+
518
+ ### 7. Errors Module (`@mp-front/common/errors`)
519
+
520
+ #### ErrorHandler
521
+
522
+ Gestor global de errores con patrón Singleton.
523
+
524
+ ```typescript
525
+ import { ErrorHandler } from "@mp-front/common/errors"
526
+
527
+ // Suscribirse a errores globales
528
+ ErrorHandler.getInstance()
529
+ .getSubject()
530
+ .subscribe(error => {
531
+ console.error("Error global:", error)
532
+
533
+ // Mostrar notificación
534
+ showNotification({
535
+ type: "error",
536
+ message: error.message,
537
+ code: error.code,
538
+ })
539
+ })
540
+ ```
541
+
542
+ #### RuntimeError
543
+
544
+ Clase de error personalizada.
545
+
546
+ ```typescript
547
+ import { RuntimeError, RuntimeErrorCode } from "@mp-front/common/errors"
548
+
549
+ // Lanzar error
550
+ throw new RuntimeError(RuntimeErrorCode.VALIDATION_ERROR, "request-id-123")
551
+
552
+ // En un catch
553
+ try {
554
+ // código
555
+ } catch (error) {
556
+ throw new RuntimeError("CUSTOM_ERROR", requestId)
557
+ }
558
+ ```
559
+
560
+ ---
561
+
562
+ ### 8. RxJS Module (`@mp-front/common/rxjs`)
563
+
564
+ #### LoadingHandler
565
+
566
+ Gestor global de estado de carga.
567
+
568
+ ```typescript
569
+ import { LoadingHandler } from "@mp-front/common/rxjs"
570
+
571
+ // Suscribirse al estado de loading
572
+ LoadingHandler.getInstance()
573
+ .getSubject()
574
+ .subscribe(isLoading => {
575
+ if (isLoading) {
576
+ showSpinner()
577
+ } else {
578
+ hideSpinner()
579
+ }
580
+ })
581
+
582
+ // Activar loading manualmente
583
+ LoadingHandler.getInstance().setSubject(true)
584
+
585
+ // Desactivar loading
586
+ LoadingHandler.getInstance().setSubject(false)
587
+ ```
588
+
589
+ **Integración con React:**
590
+
591
+ ```typescript
592
+ import { useEffect, useState } from 'react'
593
+ import { LoadingHandler } from '@mp-front/common/rxjs'
594
+
595
+ function App() {
596
+ const [isLoading, setIsLoading] = useState(false)
597
+
598
+ useEffect(() => {
599
+ const subscription = LoadingHandler.getInstance()
600
+ .getSubject()
601
+ .subscribe(setIsLoading)
602
+
603
+ return () => subscription.unsubscribe()
604
+ }, [])
605
+
606
+ return (
607
+ <>
608
+ {isLoading && <Spinner />}
609
+ {/* resto de la app */}
610
+ </>
611
+ )
612
+ }
613
+ ```
614
+
615
+ ---
616
+
617
+ ### 9. Middleware Module (`@mp-front/common/middleware`)
618
+
619
+ #### ApiMiddleware
620
+
621
+ Middleware para APIs de Next.js con manejo de sesión.
622
+
623
+ **Características:**
624
+
625
+ - Encoding/Decoding automático
626
+ - Manejo de errores
627
+ - Logging de requests
628
+ - Soporte para archivos
629
+
630
+ **Uso básico:**
631
+
632
+ ```typescript
633
+ import { ApiMiddleware } from "@mp-front/common/middleware"
634
+
635
+ class MyApiMiddleware extends ApiMiddleware {
636
+ constructor() {
637
+ super()
638
+ }
639
+ }
640
+
641
+ const middleware = new MyApiMiddleware()
642
+
643
+ // Configurar sesión
644
+ middleware.setSession(session)
645
+
646
+ // Handler para GET/POST
647
+ export const POST = middleware.get<RequestType, ResponseType>(
648
+ (params, { requestID, headers }) => {
649
+ // Lógica de negocio
650
+ return of({
651
+ success: true,
652
+ data: processedData,
653
+ })
654
+ }
655
+ )
656
+ ```
657
+
658
+ **Handler para archivos:**
659
+
660
+ ```typescript
661
+ export const POST = middleware.getFile<ResponseType>(file => {
662
+ // Procesar archivo
663
+ return of({
664
+ filename: file.name,
665
+ size: file.size,
666
+ })
667
+ })
668
+ ```
669
+
670
+ ---
671
+
672
+ ### 10. Adapters Module (`@mp-front/common/adapters`)
673
+
674
+ #### IORedisAdapter
675
+
676
+ Adaptador de Redis para NextAuth.
677
+
678
+ ```typescript
679
+ import { IORedisAdapter } from "@mp-front/common/adapters"
680
+
681
+ export const authOptions = {
682
+ adapter: IORedisAdapter({
683
+ expire: 3600, // segundos
684
+ }),
685
+ // ... otras opciones
686
+ }
687
+ ```
688
+
689
+ ---
690
+
691
+ ## ⚙️ Configuración Completa
692
+
693
+ ### Variables de Entorno
694
+
695
+ ```env
696
+ # Logging
697
+ NEXT_PUBLIC_APP_LOGS_NAME=MyApp
698
+ NEXT_PUBLIC_LOGS_LEVEL=debug
699
+ NEXT_PUBLIC_SILENT_LOGS=false
700
+
701
+ # Authentication Backend
702
+ API_AUTH_BACK_URL=https://api.example.com/auth
703
+ API_AUTH_BACK_USERNAME_AUTH=username
704
+ API_AUTH_BACK_PASSWORD_AUTH=password
705
+ ID_FRONT=frontend-app-id
706
+
707
+ # Azure Entra ID
708
+ AZURE_AD_GRAPH_GET_APP_ROLES=https://graph.microsoft.com/v1.0/applications/{id-obj}/appRoles
709
+ AZURE_AD_GRAPH_GET_USER_BY_EMAIL=https://graph.microsoft.com/v1.0/users/{user-mail}
710
+ AZURE_AD_GRAPH_GROUPS=https://graph.microsoft.com/v1.0/users/idUser/memberOf
711
+
712
+ # Redis
713
+ REDIS_URL=redis://localhost:6379
714
+ TIMEOUT_SESSION_MINUTES=60
715
+ PREFIX_LOGIN=app
716
+
717
+ # Encryption
718
+ SECRET_SIGNATURE=your-secret-key-min-32-chars
719
+ ```
720
+
721
+ ---
722
+
723
+ ## 🚀 Casos de Uso Avanzados
724
+
725
+ ### 1. Sistema de Autenticación Completo
726
+
727
+ ```typescript
728
+ import { AuthorizationService } from "@mp-front/common/auth"
729
+ import { HttpClient } from "@mp-front/common/http"
730
+ import { SessionCache } from "@mp-front/common/cache"
731
+
732
+ class AuthSystem {
733
+ private authService = new AuthorizationService()
734
+ private httpClient = new HttpClient()
735
+ private sessionCache: SessionCache
736
+
737
+ async login(userId: string) {
738
+ // Obtener token
739
+ const token = await firstValueFrom(this.authService.get())
740
+
741
+ // Configurar sesión
742
+ this.sessionCache = new SessionCache(userId)
743
+ const session = await this.sessionCache.getBasicSession()
744
+
745
+ // Hacer request autenticado
746
+ this.httpClient
747
+ .get("/api/protected", {
748
+ headers: {
749
+ Authorization: `Bearer ${token}`,
750
+ },
751
+ })
752
+ .subscribe(data => {
753
+ console.log("Datos protegidos:", data)
754
+ })
755
+ }
756
+ }
757
+ ```
758
+
759
+ ### 2. Sistema de Caché Multinivel
760
+
761
+ ```typescript
762
+ import { RedisCache } from "@mp-front/common/cache-providers"
763
+ import { Encrypter } from "@mp-front/common/helpers"
764
+
765
+ class CacheSystem<T> {
766
+ private redis = new RedisCache<T>()
767
+ private encrypter = new Encrypter()
768
+ private memoryCache = new Map<string, T>()
769
+
770
+ async get(key: string): Promise<T | null> {
771
+ // Nivel 1: Memoria
772
+ if (this.memoryCache.has(key)) {
773
+ return this.memoryCache.get(key)!
774
+ }
775
+
776
+ // Nivel 2: Redis
777
+ try {
778
+ const { data } = await this.redis.get<T>(key, "data")
779
+ this.memoryCache.set(key, data)
780
+ return data
781
+ } catch {
782
+ return null
783
+ }
784
+ }
785
+
786
+ async set(key: string, value: T, ttl: number = 3600) {
787
+ // Guardar en memoria
788
+ this.memoryCache.set(key, value)
789
+
790
+ // Guardar en Redis
791
+ await this.redis.set({
792
+ prefix: key,
793
+ idData: "data",
794
+ body: value,
795
+ expire: ttl,
796
+ encrypted: true,
797
+ })
798
+ }
799
+ }
800
+ ```
801
+
802
+ ### 3. Sistema de Manejo de Errores Global
803
+
804
+ ```typescript
805
+ import { ErrorHandler, RuntimeError } from "@mp-front/common/errors"
806
+ import { LoadingHandler } from "@mp-front/common/rxjs"
807
+
808
+ class GlobalErrorSystem {
809
+ constructor() {
810
+ this.setupErrorHandling()
811
+ this.setupLoadingHandling()
812
+ }
813
+
814
+ private setupErrorHandling() {
815
+ ErrorHandler.getInstance()
816
+ .getSubject()
817
+ .subscribe(error => {
818
+ // Ocultar loading
819
+ LoadingHandler.getInstance().setSubject(false)
820
+
821
+ // Log error
822
+ console.error("Error:", error)
823
+
824
+ // Mostrar notificación
825
+ this.showErrorNotification(error)
826
+
827
+ // Enviar a servicio de tracking
828
+ this.trackError(error)
829
+ })
830
+ }
831
+
832
+ private setupLoadingHandling() {
833
+ LoadingHandler.getInstance()
834
+ .getSubject()
835
+ .subscribe(isLoading => {
836
+ document.body.classList.toggle("loading", isLoading)
837
+ })
838
+ }
839
+
840
+ private showErrorNotification(error: RuntimeError) {
841
+ // Implementación de notificación
842
+ }
843
+
844
+ private trackError(error: RuntimeError) {
845
+ // Enviar a Sentry, DataDog, etc.
846
+ }
847
+ }
848
+ ```
849
+
850
+ ### 4. API Middleware con Validación
851
+
852
+ ```typescript
853
+ import { ApiMiddleware } from "@mp-front/common/middleware"
854
+ import { RuntimeError } from "@mp-front/common/errors"
855
+ import { of, throwError } from "rxjs"
856
+
857
+ interface CreateUserRequest {
858
+ name: string
859
+ email: string
860
+ }
861
+
862
+ interface CreateUserResponse {
863
+ id: string
864
+ name: string
865
+ email: string
866
+ }
867
+
868
+ class UserApiMiddleware extends ApiMiddleware {
869
+ createUser = this.get<CreateUserRequest, CreateUserResponse>(
870
+ (params, { requestID }) => {
871
+ // Validación
872
+ if (!params.name || !params.email) {
873
+ throw new RuntimeError("VALIDATION_ERROR", requestID)
874
+ }
875
+
876
+ // Obtener sesión
877
+ const session = this.getSession()
878
+
879
+ // Lógica de negocio
880
+ const newUser = {
881
+ id: generateId(),
882
+ name: params.name,
883
+ email: params.email,
884
+ }
885
+
886
+ return of(newUser)
887
+ }
888
+ )
889
+ }
890
+
891
+ export const POST = new UserApiMiddleware().createUser
892
+ ```
893
+
894
+ ---
895
+
896
+ ## 📝 Mejores Prácticas
897
+
898
+ ### 1. Manejo de Suscripciones RxJS
899
+
900
+ ```typescript
901
+ import { Component, OnDestroy } from "@angular/core"
902
+ import { Subject, takeUntil } from "rxjs"
903
+
904
+ class MyComponent implements OnDestroy {
905
+ private destroy$ = new Subject<void>()
906
+
907
+ ngOnInit() {
908
+ this.httpClient
909
+ .get("/api/data")
910
+ .pipe(takeUntil(this.destroy$))
911
+ .subscribe(data => {
912
+ // Procesar datos
913
+ })
914
+ }
915
+
916
+ ngOnDestroy() {
917
+ this.destroy$.next()
918
+ this.destroy$.complete()
919
+ }
920
+ }
921
+ ```
922
+
923
+ ### 2. Tipado Estricto
924
+
925
+ ```typescript
926
+ // Definir interfaces
927
+ interface User {
928
+ id: string
929
+ name: string
930
+ email: string
931
+ }
932
+
933
+ // Usar tipos genéricos
934
+ const redis = new RedisCache<User>()
935
+ const client = new HttpClient()
936
+
937
+ client.get<User>("/api/user/1").subscribe(user => {
938
+ // TypeScript sabe que user es de tipo User
939
+ console.log(user.name)
940
+ })
941
+ ```
942
+
943
+ ### 3. Manejo de Errores
944
+
945
+ ```typescript
946
+ import { catchError, of } from "rxjs"
947
+
948
+ client
949
+ .get<Data>("/api/data")
950
+ .pipe(
951
+ catchError(error => {
952
+ console.error("Error:", error)
953
+ return of(null) // Valor por defecto
954
+ })
955
+ )
956
+ .subscribe(data => {
957
+ if (data) {
958
+ // Procesar datos
959
+ }
960
+ })
961
+ ```
962
+
963
+ ---
964
+
965
+ ## 🔧 Troubleshooting
966
+
967
+ ### Problema: Redis no conecta
968
+
969
+ **Solución:**
970
+
971
+ ```typescript
972
+ import { RedisCache } from "@mp-front/common/cache-providers"
973
+
974
+ const redis = new RedisCache()
975
+
976
+ redis
977
+ .statusHost()
978
+ .then(() => {
979
+ console.log("Redis conectado")
980
+ })
981
+ .catch(error => {
982
+ console.error("Error de conexión:", error)
983
+ })
984
+ ```
985
+
986
+ ### Problema: Logs no aparecen
987
+
988
+ **Verificar:**
989
+
990
+ 1. `NEXT_PUBLIC_SILENT_LOGS=false`
991
+ 2. `NEXT_PUBLIC_LOGS_LEVEL` está configurado correctamente
992
+ 3. El nivel del log es mayor o igual al configurado
993
+
994
+ ### Problema: Encriptación falla
995
+
996
+ **Verificar:**
997
+
998
+ 1. `SECRET_SIGNATURE` está definido en `.env`
999
+ 2. La clave tiene al menos 32 caracteres
1000
+ 3. La clave es la misma para encriptar y desencriptar
1001
+
1002
+ ---
1003
+
1004
+ ## 📄 Licencia
1005
+
1006
+ Privada - Uso interno únicamente