trucostars-core-module 0.0.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/LICENSE +21 -0
- package/README.md +423 -0
- package/dist/app.controller.d.ts +6 -0
- package/dist/app.controller.js +43 -0
- package/dist/app.controller.js.map +1 -0
- package/dist/app.module.d.ts +4 -0
- package/dist/app.module.js +31 -0
- package/dist/app.module.js.map +1 -0
- package/dist/app.service.d.ts +5 -0
- package/dist/app.service.js +28 -0
- package/dist/app.service.js.map +1 -0
- package/dist/common/common.module.d.ts +2 -0
- package/dist/common/common.module.js +58 -0
- package/dist/common/common.module.js.map +1 -0
- package/dist/common/config/env.validation.d.ts +2 -0
- package/dist/common/config/env.validation.js +93 -0
- package/dist/common/config/env.validation.js.map +1 -0
- package/dist/common/config/firebase.config.d.ts +2 -0
- package/dist/common/config/firebase.config.js +9 -0
- package/dist/common/config/firebase.config.js.map +1 -0
- package/dist/common/config/index.d.ts +1 -0
- package/dist/common/config/index.js +18 -0
- package/dist/common/config/index.js.map +1 -0
- package/dist/common/database/database.module.d.ts +2 -0
- package/dist/common/database/database.module.js +54 -0
- package/dist/common/database/database.module.js.map +1 -0
- package/dist/common/database/database.service.d.ts +25 -0
- package/dist/common/database/database.service.js +91 -0
- package/dist/common/database/database.service.js.map +1 -0
- package/dist/common/decorators/api-performance.decorator.d.ts +1 -0
- package/dist/common/decorators/api-performance.decorator.js +42 -0
- package/dist/common/decorators/api-performance.decorator.js.map +1 -0
- package/dist/common/decorators/index.d.ts +1 -0
- package/dist/common/decorators/index.js +18 -0
- package/dist/common/decorators/index.js.map +1 -0
- package/dist/common/filters/all-exceptions.filter.d.ts +5 -0
- package/dist/common/filters/all-exceptions.filter.js +38 -0
- package/dist/common/filters/all-exceptions.filter.js.map +1 -0
- package/dist/common/firebase/firebase.controller.d.ts +35 -0
- package/dist/common/firebase/firebase.controller.js +115 -0
- package/dist/common/firebase/firebase.controller.js.map +1 -0
- package/dist/common/firebase/firebase.module.d.ts +3 -0
- package/dist/common/firebase/firebase.module.js +83 -0
- package/dist/common/firebase/firebase.module.js.map +1 -0
- package/dist/common/firebase/firebase.service.d.ts +17 -0
- package/dist/common/firebase/firebase.service.js +127 -0
- package/dist/common/firebase/firebase.service.js.map +1 -0
- package/dist/common/firebase/firebase.types.d.ts +22 -0
- package/dist/common/firebase/firebase.types.js +3 -0
- package/dist/common/firebase/firebase.types.js.map +1 -0
- package/dist/common/interceptors/logging.interceptor.d.ts +10 -0
- package/dist/common/interceptors/logging.interceptor.js +108 -0
- package/dist/common/interceptors/logging.interceptor.js.map +1 -0
- package/dist/common/interceptors/performance.interceptor.d.ts +7 -0
- package/dist/common/interceptors/performance.interceptor.js +58 -0
- package/dist/common/interceptors/performance.interceptor.js.map +1 -0
- package/dist/common/middleware/logger.middleware.d.ts +19 -0
- package/dist/common/middleware/logger.middleware.js +231 -0
- package/dist/common/middleware/logger.middleware.js.map +1 -0
- package/dist/common/redis/redis.module.d.ts +2 -0
- package/dist/common/redis/redis.module.js +21 -0
- package/dist/common/redis/redis.module.js.map +1 -0
- package/dist/common/redis/redis.service.d.ts +11 -0
- package/dist/common/redis/redis.service.js +52 -0
- package/dist/common/redis/redis.service.js.map +1 -0
- package/dist/common/sqs/sqs.consumer.d.ts +20 -0
- package/dist/common/sqs/sqs.consumer.js +140 -0
- package/dist/common/sqs/sqs.consumer.js.map +1 -0
- package/dist/common/sqs/sqs.controller.d.ts +21 -0
- package/dist/common/sqs/sqs.controller.js +70 -0
- package/dist/common/sqs/sqs.controller.js.map +1 -0
- package/dist/common/sqs/sqs.example.service.d.ts +27 -0
- package/dist/common/sqs/sqs.example.service.js +206 -0
- package/dist/common/sqs/sqs.example.service.js.map +1 -0
- package/dist/common/sqs/sqs.module.d.ts +2 -0
- package/dist/common/sqs/sqs.module.js +24 -0
- package/dist/common/sqs/sqs.module.js.map +1 -0
- package/dist/common/sqs/sqs.producer.d.ts +13 -0
- package/dist/common/sqs/sqs.producer.js +197 -0
- package/dist/common/sqs/sqs.producer.js.map +1 -0
- package/dist/common/websocket/websocket.gateway.d.ts +9 -0
- package/dist/common/websocket/websocket.gateway.js +41 -0
- package/dist/common/websocket/websocket.gateway.js.map +1 -0
- package/dist/common/websocket/websocket.module.d.ts +2 -0
- package/dist/common/websocket/websocket.module.js +22 -0
- package/dist/common/websocket/websocket.module.js.map +1 -0
- package/dist/common/websocket/websocket.service.d.ts +25 -0
- package/dist/common/websocket/websocket.service.js +248 -0
- package/dist/common/websocket/websocket.service.js.map +1 -0
- package/dist/data/data.d.ts +10 -0
- package/dist/data/data.js +10 -0
- package/dist/data/data.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +48 -0
- package/dist/main.js.map +1 -0
- package/dist/products/entities/product.entity.d.ts +56 -0
- package/dist/products/entities/product.entity.js +80 -0
- package/dist/products/entities/product.entity.js.map +1 -0
- package/dist/products/products.controller.d.ts +35 -0
- package/dist/products/products.controller.js +98 -0
- package/dist/products/products.controller.js.map +1 -0
- package/dist/products/products.module.d.ts +2 -0
- package/dist/products/products.module.js +27 -0
- package/dist/products/products.module.js.map +1 -0
- package/dist/products/products.service.d.ts +36 -0
- package/dist/products/products.service.example.d.ts +1 -0
- package/dist/products/products.service.example.js +3 -0
- package/dist/products/products.service.example.js.map +1 -0
- package/dist/products/products.service.js +74 -0
- package/dist/products/products.service.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +106 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jorge Nahuel Gerones
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# TrucoStars API
|
|
6
|
+
|
|
7
|
+
<p align="center">Una API RESTful construida con <a href="http://nestjs.com/" target="_blank">NestJS</a> y <a href="https://www.typescriptlang.org/" target="_blank">TypeScript</a> para gestionar productos, usuarios y comunicación en tiempo real.</p>
|
|
8
|
+
|
|
9
|
+
## 📑 Índice
|
|
10
|
+
|
|
11
|
+
- [Descripción](#descripcion)
|
|
12
|
+
- [Estructura del Proyecto](#estructura-proyecto)
|
|
13
|
+
- [Stack Tecnológico](#stack-tecnologico)
|
|
14
|
+
- [Módulos Principales](#modulos-principales)
|
|
15
|
+
- [Productos Module](#productos-module)
|
|
16
|
+
- [Firebase Module](#firebase-module)
|
|
17
|
+
- [Redis Module](#redis-module)
|
|
18
|
+
- [WebSocket Module](#websocket-module)
|
|
19
|
+
- [Configuración e Instalación](#configuracion-instalacion)
|
|
20
|
+
- [Ejecución](#ejecucion)
|
|
21
|
+
- [Testing](#testing)
|
|
22
|
+
- [Características Principales](#caracteristicas-principales)
|
|
23
|
+
- [Endpoints Principales](#endpoints-principales)
|
|
24
|
+
- [Dependencias Importantes](#dependencias-importantes)
|
|
25
|
+
- [Archivos de Configuración](#archivos-configuracion)
|
|
26
|
+
- [Autor](#autor)
|
|
27
|
+
- [Licencia](#licencia)
|
|
28
|
+
|
|
29
|
+
<a id="descripcion"></a>
|
|
30
|
+
## 📋 Descripción
|
|
31
|
+
|
|
32
|
+
TrucoStars API es una aplicación backend moderna construida con **NestJS**, que proporciona funcionalidades completas para:
|
|
33
|
+
|
|
34
|
+
- **Gestión de Productos**: CRUD de productos con almacenamiento en MongoDB
|
|
35
|
+
- **Autenticación y Autorización**: Integración con Firebase para autenticación segura
|
|
36
|
+
- **Caché en Tiempo Real**: Uso de Redis para optimizar el rendimiento
|
|
37
|
+
- **Comunicación WebSocket**: Soporte para comunicación bidireccional en tiempo real
|
|
38
|
+
- **Persistencia de Datos**: MongoDB como base de datos principal
|
|
39
|
+
|
|
40
|
+
<a id="estructura-proyecto"></a>
|
|
41
|
+
## 🏗️ Estructura del Proyecto
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
src/
|
|
45
|
+
├── app.controller.ts # Controlador principal
|
|
46
|
+
├── app.service.ts # Servicio principal
|
|
47
|
+
├── app.module.ts # Módulo raíz
|
|
48
|
+
├── main.ts # Punto de entrada
|
|
49
|
+
├── firebase/ # Módulo de autenticación Firebase
|
|
50
|
+
│ ├── firebase.config.ts # Configuración de Firebase
|
|
51
|
+
│ ├── firebase.controller.ts # Endpoints de autenticación
|
|
52
|
+
│ ├── firebase.service.ts # Lógica de autenticación
|
|
53
|
+
│ └── firebase.module.ts # Módulo Firebase
|
|
54
|
+
├── products/ # Módulo de productos
|
|
55
|
+
│ ├── products.controller.ts # Endpoints de productos
|
|
56
|
+
│ ├── products.service.ts # Lógica de negocio de productos
|
|
57
|
+
│ ├── products.module.ts # Módulo de productos
|
|
58
|
+
│ └── entities/
|
|
59
|
+
│ └── product.entity.ts # Esquema/entidad de producto
|
|
60
|
+
├── redis/ # Módulo de caché Redis
|
|
61
|
+
│ ├── redis.service.ts # Servicio de Redis
|
|
62
|
+
│ └── redis.module.ts # Módulo Redis
|
|
63
|
+
└── websocket/ # Módulo de WebSocket
|
|
64
|
+
├── websocket.gateway.ts # Gateway de WebSocket
|
|
65
|
+
├── websocket.service.ts # Lógica de WebSocket
|
|
66
|
+
└── websocket.module.ts # Módulo WebSocket
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
<a id="stack-tecnologico"></a>
|
|
70
|
+
## 🛠️ Stack Tecnológico
|
|
71
|
+
|
|
72
|
+
### Core Framework
|
|
73
|
+
- **NestJS v11**: Framework progresivo para aplicaciones Node.js
|
|
74
|
+
- **TypeScript**: Tipado seguro y desarrollo escalable
|
|
75
|
+
|
|
76
|
+
### Bases de Datos
|
|
77
|
+
- **MongoDB**: Base de datos NoSQL con Mongoose ODM
|
|
78
|
+
- **Redis**: Sistema de caché en memoria
|
|
79
|
+
|
|
80
|
+
### Funcionalidades
|
|
81
|
+
- **Firebase Admin SDK**: Autenticación y gestión de usuarios
|
|
82
|
+
- **Socket.io**: Comunicación WebSocket en tiempo real
|
|
83
|
+
- **Swagger**: Documentación API automática
|
|
84
|
+
|
|
85
|
+
### Testing & Quality
|
|
86
|
+
- **Jest**: Framework de testing
|
|
87
|
+
- **ESLint**: Linting de código
|
|
88
|
+
- **Prettier**: Formateo de código
|
|
89
|
+
|
|
90
|
+
### Dependencias Principales
|
|
91
|
+
```json
|
|
92
|
+
"@nestjs/core": "^11.0.1"
|
|
93
|
+
"@nestjs/mongoose": "^11.0.4"
|
|
94
|
+
"@nestjs/websockets": "^11.1.11"
|
|
95
|
+
"@nestjs/swagger": "^11.2.4"
|
|
96
|
+
"firebase-admin": "^13.6.0"
|
|
97
|
+
"mongoose": "^9.1.3"
|
|
98
|
+
"ioredis": "^5.9.2"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
<a id="modulos-principales"></a>
|
|
102
|
+
## 🚀 Módulos Principales
|
|
103
|
+
|
|
104
|
+
<a id="productos-module"></a>
|
|
105
|
+
### 📦 Productos Module
|
|
106
|
+
Gestión completa de productos con operaciones CRUD, validaciones y caché.
|
|
107
|
+
- `ProductsController`: Endpoints para CRUD de productos
|
|
108
|
+
- `ProductsService`: Lógica de negocio
|
|
109
|
+
- `ProductEntity`: Esquema MongoDB
|
|
110
|
+
|
|
111
|
+
<a id="firebase-module"></a>
|
|
112
|
+
### 🔐 Firebase Module
|
|
113
|
+
Autenticación y autorización segura mediante Firebase.
|
|
114
|
+
- `FirebaseService`: Gestión de tokens y autenticación
|
|
115
|
+
- `FirebaseController`: Endpoints de login y verificación
|
|
116
|
+
|
|
117
|
+
<a id="redis-module"></a>
|
|
118
|
+
### ⚡ Redis Module
|
|
119
|
+
Se implementa un sistema de caché distribuido para optimizar el rendimiento de la aplicación, utilizando Redis como backend de almacenamiento, a través del [Cache Manager de NestJS](https://docs.nestjs.com/techniques/caching) y reutilizando el cliente que provee `RedisService`.
|
|
120
|
+
|
|
121
|
+
Esto permite:
|
|
122
|
+
|
|
123
|
+
- Cacheo automático de respuestas HTTP mediante [CacheInterceptor](https://docs.nestjs.com/techniques/caching#auto-caching-responses)
|
|
124
|
+
- Compatibilidad total con interceptores y decoradores estándar de NestJS.
|
|
125
|
+
- Reducción de consultas repetidas a MongoDB
|
|
126
|
+
- Expiración de datos configurable (TTL)
|
|
127
|
+
|
|
128
|
+
Adicionalmente, se implementa Rate Limiting mediante el paquete [`@nestjs/throttler`](https://docs.nestjs.com/security/rate-limiting), que provee un `ThrottlerGuard` que limita la cantidad de solicitudes permitidas por cliente en un período de tiempo configurable, aplicándose a todos los endpoints de la aplicación y reutilizando el mismo cliente Redis expuesto por `RedisService`.
|
|
129
|
+
|
|
130
|
+
Este enfoque desacopla la lógica de caché de la lógica de negocio,
|
|
131
|
+
siguiendo las mejores prácticas recomendadas por NestJS.
|
|
132
|
+
|
|
133
|
+
<a id="websocket-module"></a>
|
|
134
|
+
### 🔌 WebSocket Module
|
|
135
|
+
Comunicación en tiempo real bidireccional.
|
|
136
|
+
- `WebSocketGateway`: Gestor de conexiones WebSocket
|
|
137
|
+
- `WebSocketService`: Lógica de mensajería
|
|
138
|
+
|
|
139
|
+
<a id="configuracion-instalacion"></a>
|
|
140
|
+
## ⚙️ Configuración e Instalación
|
|
141
|
+
|
|
142
|
+
### Requisitos Previos
|
|
143
|
+
- **Node.js**: v18+ recomendado
|
|
144
|
+
- **npm**: v9+
|
|
145
|
+
- **MongoDB**: Instancia local o conexión remota
|
|
146
|
+
- **Redis**: Instancia local o conexión remota
|
|
147
|
+
|
|
148
|
+
### Instalación
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Instalar dependencias
|
|
152
|
+
$ npm install
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Variables de Entorno
|
|
156
|
+
Crear un archivo `.env` en la raíz del proyecto:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
# MongoDB
|
|
160
|
+
MONGODB_URI=mongodb+srv://user:password@cluster.mongodb.net/?appName=Develop
|
|
161
|
+
|
|
162
|
+
# Redis
|
|
163
|
+
# GENERAL
|
|
164
|
+
REDIS_URL=redis://[usuario]:[password]@[host]:[port]/[db]
|
|
165
|
+
# LOCAL
|
|
166
|
+
REDIS_URL=redis://localhost:6379
|
|
167
|
+
|
|
168
|
+
# Firebase
|
|
169
|
+
FIREBASE_PROJECT_ID=your-project-id
|
|
170
|
+
FIREBASE_PRIVATE_KEY=your-private-key
|
|
171
|
+
FIREBASE_CLIENT_EMAIL=your-client-email
|
|
172
|
+
|
|
173
|
+
# API
|
|
174
|
+
PORT=3000
|
|
175
|
+
NODE_ENV=development
|
|
176
|
+
|
|
177
|
+
# Logging Configuration
|
|
178
|
+
LOG_LEVEL=debug # Nivel de logging: debug | log | warn | error
|
|
179
|
+
ENABLE_HTTP_LOGGING=true # Logs del middleware HTTP (request/response)
|
|
180
|
+
ENABLE_API_LOGGING=true # Logs del interceptor API (controllers/handlers)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Control de Logs
|
|
184
|
+
|
|
185
|
+
La aplicación incluye un sistema de logging profesional que puede controlarse mediante variables de entorno:
|
|
186
|
+
|
|
187
|
+
- **`LOG_LEVEL`**: Define el nivel de detalle de los logs
|
|
188
|
+
- `debug`: Todos los logs (desarrollo)
|
|
189
|
+
- `log`: Información general
|
|
190
|
+
- `warn`: Solo advertencias y errores
|
|
191
|
+
- `error`: Solo errores críticos
|
|
192
|
+
|
|
193
|
+
- **`ENABLE_HTTP_LOGGING`**: Controla los logs del middleware HTTP
|
|
194
|
+
- `true`: Muestra logs detallados de requests/responses con formato visual mejorado
|
|
195
|
+
- `false`: Desactiva completamente los logs HTTP
|
|
196
|
+
|
|
197
|
+
- **`ENABLE_API_LOGGING`**: Controla los logs del interceptor API
|
|
198
|
+
- `true`: Muestra logs de controllers, handlers y timing de ejecución
|
|
199
|
+
- `false`: Desactiva los logs de API
|
|
200
|
+
|
|
201
|
+
**Ejemplo para producción (logs mínimos):**
|
|
202
|
+
```env
|
|
203
|
+
NODE_ENV=production
|
|
204
|
+
LOG_LEVEL=error
|
|
205
|
+
ENABLE_HTTP_LOGGING=false
|
|
206
|
+
ENABLE_API_LOGGING=false
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Ejemplo para desarrollo (logs completos):**
|
|
210
|
+
```env
|
|
211
|
+
NODE_ENV=development
|
|
212
|
+
LOG_LEVEL=debug
|
|
213
|
+
ENABLE_HTTP_LOGGING=true
|
|
214
|
+
ENABLE_API_LOGGING=true
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
<a id="ejecucion"></a>
|
|
218
|
+
## 🚀 Ejecución
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Modo desarrollo (con reinicio automático)
|
|
222
|
+
$ npm run start:dev
|
|
223
|
+
|
|
224
|
+
# Modo desarrollo con depuración
|
|
225
|
+
$ npm run start:debug
|
|
226
|
+
|
|
227
|
+
# Modo producción
|
|
228
|
+
$ npm run start:prod
|
|
229
|
+
|
|
230
|
+
# Compilar proyecto
|
|
231
|
+
$ npm run build
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
<a id="testing"></a>
|
|
235
|
+
## 🧪 Testing
|
|
236
|
+
|
|
237
|
+
### Comandos Disponibles
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Ejecutar todas las pruebas unitarias
|
|
241
|
+
$ npm run test
|
|
242
|
+
|
|
243
|
+
# Ejecutar tests de módulos comunes (Database, Redis, Firebase, WebSocket)
|
|
244
|
+
$ npm run test:common
|
|
245
|
+
|
|
246
|
+
# Ejecutar solo tests de conexiones e inicialización
|
|
247
|
+
$ npm run test:connections
|
|
248
|
+
|
|
249
|
+
# Modo watch (re-ejecuta al cambiar archivos)
|
|
250
|
+
$ npm run test:watch
|
|
251
|
+
|
|
252
|
+
# Cobertura de pruebas
|
|
253
|
+
$ npm run test:cov
|
|
254
|
+
|
|
255
|
+
# Pruebas end-to-end
|
|
256
|
+
$ npm run test:e2e
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 📋 Tests de Conexiones e Inicialización
|
|
260
|
+
|
|
261
|
+
El proyecto incluye un conjunto completo de **unit tests** para verificar que las conexiones e inicialización de los módulos funcionen correctamente:
|
|
262
|
+
|
|
263
|
+
#### Tests Implementados
|
|
264
|
+
|
|
265
|
+
| Módulo | Archivo | Tests | Descripción |
|
|
266
|
+
|--------|---------|-------|-------------|
|
|
267
|
+
| **DatabaseService** | `database.service.spec.ts` | 10 tests | Verificación de conexión MongoDB, estados, health checks |
|
|
268
|
+
| **RedisService** | `redis.service.spec.ts` | 7 tests | Configuración de Redis, ciclo de vida, gestión de conexión |
|
|
269
|
+
| **FirebaseService** | `firebase.service.spec.ts` | 6 tests | Inicialización Firebase, notificaciones push, manejo de errores |
|
|
270
|
+
| **WebSocketService** | `websocket.service.spec.ts` | 9 tests | Broadcasting, gestión de conexiones, manejo de errores |
|
|
271
|
+
| **SqsProducer** | `sqs.producer.spec.ts` | 11 tests | Configuración AWS, inicialización cliente SQS, métodos de envío |
|
|
272
|
+
| **SqsConsumer** | `sqs.consumer.spec.ts` | 15 tests | Polling, procesamiento de mensajes, RxJS Subjects, lifecycle |
|
|
273
|
+
| **SqsModule** | `sqs.module.spec.ts` | 9 tests | Compilación módulo, providers, exports, integración |
|
|
274
|
+
| **CommonModule** | `common.module.spec.ts` | 8 tests | Compilación del módulo, configuración global, providers |
|
|
275
|
+
|
|
276
|
+
#### ✅ Qué Verifican los Tests
|
|
277
|
+
|
|
278
|
+
- ✅ **Inicialización correcta** de servicios y conexiones
|
|
279
|
+
- ✅ **Estados de conexión** (conectado, desconectado, conectando)
|
|
280
|
+
- ✅ **Configuración** de variables de entorno
|
|
281
|
+
- ✅ **Health checks** de conexiones a bases de datos y servicios externos
|
|
282
|
+
- ✅ **Manejo de errores** durante la inicialización
|
|
283
|
+
- ✅ **Inyección de dependencias** correcta en todos los módulos
|
|
284
|
+
|
|
285
|
+
#### 📊 Cobertura de Tests
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# Ejecutar solo tests de conexiones
|
|
289
|
+
$ npm run test:8 passed, 8 total
|
|
290
|
+
# Tests: 74 passed, 74
|
|
291
|
+
# Resultado esperado:
|
|
292
|
+
# Test Suites: 4 passed, 4 total
|
|
293
|
+
# Tests: 32 passed, 32 total
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### 🔍 Ejemplo de Test: DatabaseService
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
describe('DatabaseService - Connection Initialization', () => {
|
|
300
|
+
it('should initialize with a connected state', () => {
|
|
301
|
+
expect(mockConnection.readyState).toBe(1); // 1 = conectado
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should perform health check successfully', async () => {
|
|
305
|
+
const healthStatus = await service.checkHealth();
|
|
306
|
+
expect(healthStatus.status).toBe('healthy');
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
#### 📝 Documentación Completa
|
|
312
|
+
|
|
313
|
+
Para más detalles sobre los tests y cómo implementarlos, consulta:
|
|
314
|
+
- **[TESTING.md](src/common/TESTING.md)** - Guía completa de testing
|
|
315
|
+
- Incluye ejemplos de mocks, mejores prácticas y troubleshooting
|
|
316
|
+
|
|
317
|
+
#### 🎯 Beneficios
|
|
318
|
+
|
|
319
|
+
1. **Detección Temprana**: Identifica problemas de configuración antes del despliegue
|
|
320
|
+
2. **Documentación Viva**: Los tests sirven como documentación del comportamiento esperado
|
|
321
|
+
3. **Confianza en Refactoring**: Permite cambiar código con seguridad
|
|
322
|
+
4. **CI/CD Ready**: Tests automatizables para pipelines de integración continua
|
|
323
|
+
|
|
324
|
+
<a id="caracteristicas-principales"></a>
|
|
325
|
+
## 📝 Herramientas de Desarrollo
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# Linting y formateo
|
|
329
|
+
$ npm run lint # Ejecutar ESLint
|
|
330
|
+
$ npm run format # Formatear código con Prettier
|
|
331
|
+
|
|
332
|
+
# Gestión de puertos
|
|
333
|
+
$ npm run kill # Cerrar proceso en puerto 3000
|
|
334
|
+
$ npm run stop-force # Forzar cierre del puerto 3000
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## 📚 Características Principales
|
|
338
|
+
|
|
339
|
+
### ✅ CRUD de Productos
|
|
340
|
+
- Crear nuevos productos
|
|
341
|
+
- Listar y buscar productos
|
|
342
|
+
- Actualizar información de productos
|
|
343
|
+
- Eliminar productos
|
|
344
|
+
- Caché automático con Redis
|
|
345
|
+
|
|
346
|
+
### 🔐 Autenticación con Firebase
|
|
347
|
+
- Registro de usuarios
|
|
348
|
+
- Login seguro
|
|
349
|
+
- Verificación de tokens JWT
|
|
350
|
+
- Control de acceso basado en roles
|
|
351
|
+
|
|
352
|
+
### 💬 WebSocket en Tiempo Real
|
|
353
|
+
- Conexiones persistentes
|
|
354
|
+
- Notificaciones en tiempo real
|
|
355
|
+
- Mensajería bidireccional
|
|
356
|
+
- Manejo de desconexiones automático
|
|
357
|
+
|
|
358
|
+
### ⚡ Optimización con Redis
|
|
359
|
+
- Caché de productos frecuentes
|
|
360
|
+
- Reducción de consultas a MongoDB
|
|
361
|
+
- Sincronización automática de datos
|
|
362
|
+
- Expiración configurable de datos
|
|
363
|
+
|
|
364
|
+
<a id="endpoints-principales"></a>
|
|
365
|
+
## 🔗 Endpoints Principales
|
|
366
|
+
|
|
367
|
+
### Productos
|
|
368
|
+
- `GET /products` - Listar todos los productos
|
|
369
|
+
- `GET /products/:id` - Obtener un producto
|
|
370
|
+
- `POST /products` - Crear nuevo producto
|
|
371
|
+
- `PATCH /products/:id` - Actualizar producto
|
|
372
|
+
- `DELETE /products/:id` - Eliminar producto
|
|
373
|
+
|
|
374
|
+
### Firebase (Autenticación)
|
|
375
|
+
- `POST /auth/register` - Registrar usuario
|
|
376
|
+
- `POST /auth/login` - Iniciar sesión
|
|
377
|
+
- `POST /auth/verify` - Verificar token
|
|
378
|
+
|
|
379
|
+
### WebSocket
|
|
380
|
+
- Evento: `message` - Enviar mensaje en tiempo real
|
|
381
|
+
- Evento: `connect` - Conexión establecida
|
|
382
|
+
- Evento: `disconnect` - Conexión cerrada
|
|
383
|
+
|
|
384
|
+
<a id="dependencias-importantes"></a>
|
|
385
|
+
## 📦 Dependencias Importantes
|
|
386
|
+
|
|
387
|
+
| Paquete | Versión | Propósito |
|
|
388
|
+
|---------|---------|----------|
|
|
389
|
+
| `@nestjs/core` | ^11.0.1 | Core framework |
|
|
390
|
+
| `@nestjs/mongoose` | ^11.0.4 | ODM para MongoDB |
|
|
391
|
+
| `@nestjs/websockets` | ^11.1.11 | Soporte WebSocket |
|
|
392
|
+
| `@nestjs/swagger` | ^11.2.4 | Documentación API |
|
|
393
|
+
| `firebase-admin` | ^13.6.0 | Autenticación Firebase |
|
|
394
|
+
| `mongoose` | ^9.1.3 | MongoDB driver |
|
|
395
|
+
| `ioredis` | ^5.9.2 | Cliente Redis |
|
|
396
|
+
|
|
397
|
+
## 🛠️ Herramientas de Desarrollo
|
|
398
|
+
|
|
399
|
+
| Herramienta | Propósito |
|
|
400
|
+
|------------|----------|
|
|
401
|
+
| **Jest** | Testing unitario y e2e |
|
|
402
|
+
| **ESLint** | Linting de código |
|
|
403
|
+
| **Prettier** | Formateo de código |
|
|
404
|
+
| **Swagger** | Documentación de API |
|
|
405
|
+
| **TypeScript** | Tipado estático |
|
|
406
|
+
|
|
407
|
+
<a id="archivos-configuracion"></a>
|
|
408
|
+
## 📄 Archivos de Configuración
|
|
409
|
+
|
|
410
|
+
- **tsconfig.json** - Configuración TypeScript
|
|
411
|
+
- **nest-cli.json** - Configuración NestJS
|
|
412
|
+
- **eslint.config.mjs** - Reglas ESLint
|
|
413
|
+
- **package.json** - Dependencias y scripts
|
|
414
|
+
|
|
415
|
+
<a id="autor"></a>
|
|
416
|
+
## 🤝 Autor
|
|
417
|
+
|
|
418
|
+
**Fractured Mesh Studios**
|
|
419
|
+
|
|
420
|
+
<a id="licencia"></a>
|
|
421
|
+
## 📜 Licencia
|
|
422
|
+
|
|
423
|
+
Este proyecto está bajo licencia UNLICENSED.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AppController = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
15
|
+
const app_service_1 = require("./app.service");
|
|
16
|
+
let AppController = class AppController {
|
|
17
|
+
appService;
|
|
18
|
+
constructor(appService) {
|
|
19
|
+
this.appService = appService;
|
|
20
|
+
}
|
|
21
|
+
getHello() {
|
|
22
|
+
return this.appService.getHello();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.AppController = AppController;
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, common_1.Get)('/'),
|
|
28
|
+
(0, swagger_1.ApiOperation)({ summary: 'Welcome message' }),
|
|
29
|
+
(0, swagger_1.ApiResponse)({
|
|
30
|
+
status: 200,
|
|
31
|
+
description: 'Returns a welcome message',
|
|
32
|
+
example: 'Hello World!',
|
|
33
|
+
}),
|
|
34
|
+
__metadata("design:type", Function),
|
|
35
|
+
__metadata("design:paramtypes", []),
|
|
36
|
+
__metadata("design:returntype", String)
|
|
37
|
+
], AppController.prototype, "getHello", null);
|
|
38
|
+
exports.AppController = AppController = __decorate([
|
|
39
|
+
(0, swagger_1.ApiTags)('App'),
|
|
40
|
+
(0, common_1.Controller)(),
|
|
41
|
+
__metadata("design:paramtypes", [app_service_1.AppService])
|
|
42
|
+
], AppController);
|
|
43
|
+
//# sourceMappingURL=app.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA8D;AAC9D,6CAA+E;AAC/E,+CAA2C;AAIpC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACK;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IASvD,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CACF,CAAA;AAbY,sCAAa;AAUxB;IAPC,IAAA,YAAG,EAAC,GAAG,CAAC;IACR,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC5C,IAAA,qBAAW,EAAC;QACX,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,2BAA2B;QACxC,OAAO,EAAE,cAAc;KACxB,CAAC;;;;6CAGD;wBAZU,aAAa;IAFzB,IAAA,iBAAO,EAAC,KAAK,CAAC;IACd,IAAA,mBAAU,GAAE;qCAE8B,wBAAU;GADxC,aAAa,CAazB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AppModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const app_controller_1 = require("./app.controller");
|
|
12
|
+
const app_service_1 = require("./app.service");
|
|
13
|
+
const products_module_1 = require("./products/products.module");
|
|
14
|
+
const common_module_1 = require("./common/common.module");
|
|
15
|
+
const logger_middleware_1 = require("./common/middleware/logger.middleware");
|
|
16
|
+
let AppModule = class AppModule {
|
|
17
|
+
configure(consumer) {
|
|
18
|
+
consumer
|
|
19
|
+
.apply(logger_middleware_1.LoggerMiddleware)
|
|
20
|
+
.forRoutes('*');
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.AppModule = AppModule;
|
|
24
|
+
exports.AppModule = AppModule = __decorate([
|
|
25
|
+
(0, common_1.Module)({
|
|
26
|
+
imports: [common_module_1.CommonModule, products_module_1.ProductsModule],
|
|
27
|
+
controllers: [app_controller_1.AppController],
|
|
28
|
+
providers: [app_service_1.AppService],
|
|
29
|
+
})
|
|
30
|
+
], AppModule);
|
|
31
|
+
//# sourceMappingURL=app.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwE;AACxE,qDAAiD;AACjD,+CAA2C;AAC3C,gEAA4D;AAC5D,0DAAsD;AACtD,6EAAyE;AAOlE,IAAM,SAAS,GAAf,MAAM,SAAS;IACpB,SAAS,CAAC,QAA4B;QACpC,QAAQ;aACL,KAAK,CAAC,oCAAgB,CAAC;aACvB,SAAS,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;CACF,CAAA;AANY,8BAAS;oBAAT,SAAS;IALrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,EAAE,gCAAc,CAAC;QACvC,WAAW,EAAE,CAAC,8BAAa,CAAC;QAC5B,SAAS,EAAE,CAAC,wBAAU,CAAC;KACxB,CAAC;GACW,SAAS,CAMrB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var AppService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.AppService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
let AppService = AppService_1 = class AppService {
|
|
16
|
+
logger = new common_1.Logger(AppService_1.name);
|
|
17
|
+
constructor() { }
|
|
18
|
+
getHello() {
|
|
19
|
+
this.logger.log('getHello endpoint called');
|
|
20
|
+
return 'Hello World!';
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.AppService = AppService;
|
|
24
|
+
exports.AppService = AppService = AppService_1 = __decorate([
|
|
25
|
+
(0, common_1.Injectable)(),
|
|
26
|
+
__metadata("design:paramtypes", [])
|
|
27
|
+
], AppService);
|
|
28
|
+
//# sourceMappingURL=app.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["../src/app.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AAG7C,IAAM,UAAU,kBAAhB,MAAM,UAAU;IACJ,MAAM,GAAG,IAAI,eAAM,CAAC,YAAU,CAAC,IAAI,CAAC,CAAC;IAEtD,gBAAe,CAAC;IAEhB,QAAQ;QACN,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAC5C,OAAO,cAAc,CAAC;IACxB,CAAC;CACF,CAAA;AATY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;;GACA,UAAU,CAStB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CommonModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const config_1 = require("@nestjs/config");
|
|
12
|
+
const cache_manager_1 = require("@nestjs/cache-manager");
|
|
13
|
+
const throttler_1 = require("@nestjs/throttler");
|
|
14
|
+
const core_1 = require("@nestjs/core");
|
|
15
|
+
const env_validation_1 = require("./config/env.validation");
|
|
16
|
+
const database_module_1 = require("./database/database.module");
|
|
17
|
+
const redis_module_1 = require("./redis/redis.module");
|
|
18
|
+
const websocket_module_1 = require("./websocket/websocket.module");
|
|
19
|
+
const firebase_module_1 = require("./firebase/firebase.module");
|
|
20
|
+
const sqs_module_1 = require("./sqs/sqs.module");
|
|
21
|
+
let CommonModule = class CommonModule {
|
|
22
|
+
};
|
|
23
|
+
exports.CommonModule = CommonModule;
|
|
24
|
+
exports.CommonModule = CommonModule = __decorate([
|
|
25
|
+
(0, common_1.Global)(),
|
|
26
|
+
(0, common_1.Module)({
|
|
27
|
+
imports: [
|
|
28
|
+
config_1.ConfigModule.forRoot({
|
|
29
|
+
isGlobal: true,
|
|
30
|
+
envFilePath: '.env',
|
|
31
|
+
validationSchema: (0, env_validation_1.createValidationSchema)(),
|
|
32
|
+
validationOptions: {
|
|
33
|
+
allowUnknown: true,
|
|
34
|
+
abortEarly: true,
|
|
35
|
+
},
|
|
36
|
+
expandVariables: true,
|
|
37
|
+
}),
|
|
38
|
+
cache_manager_1.CacheModule.register({
|
|
39
|
+
isGlobal: true,
|
|
40
|
+
}),
|
|
41
|
+
throttler_1.ThrottlerModule.forRoot({
|
|
42
|
+
throttlers: [
|
|
43
|
+
{
|
|
44
|
+
limit: 100,
|
|
45
|
+
ttl: (0, throttler_1.seconds)(60),
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
}),
|
|
49
|
+
database_module_1.DatabaseModule,
|
|
50
|
+
redis_module_1.RedisModule,
|
|
51
|
+
websocket_module_1.WebSocketModule,
|
|
52
|
+
firebase_module_1.FirebaseModule,
|
|
53
|
+
sqs_module_1.SqsModuleCustom,
|
|
54
|
+
],
|
|
55
|
+
providers: [{ provide: core_1.APP_GUARD, useClass: throttler_1.ThrottlerGuard }],
|
|
56
|
+
})
|
|
57
|
+
], CommonModule);
|
|
58
|
+
//# sourceMappingURL=common.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.module.js","sourceRoot":"","sources":["../../src/common/common.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,2CAA8C;AAC9C,yDAAoD;AACpD,iDAA6E;AAC7E,uCAAyC;AACzC,4DAAiE;AACjE,gEAA4D;AAC5D,uDAAmD;AACnD,mEAA+D;AAC/D,gEAA4D;AAC5D,iDAAmD;AAuC5C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IAnCxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,MAAM;gBACnB,gBAAgB,EAAE,IAAA,uCAAsB,GAAE;gBAC1C,iBAAiB,EAAE;oBACjB,YAAY,EAAE,IAAI;oBAClB,UAAU,EAAE,IAAI;iBACjB;gBACD,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,2BAAW,CAAC,QAAQ,CAAC;gBACnB,QAAQ,EAAE,IAAI;aACf,CAAC;YAEF,2BAAe,CAAC,OAAO,CAAC;gBACtB,UAAU,EAAE;oBACV;wBACE,KAAK,EAAE,GAAG;wBACV,GAAG,EAAE,IAAA,mBAAO,EAAC,EAAE,CAAC;qBACjB;iBACF;aACF,CAAC;YAEF,gCAAc;YACd,0BAAW;YACX,kCAAe;YACf,gCAAc;YACd,4BAAe;SAChB;QACD,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAS,EAAE,QAAQ,EAAE,0BAAc,EAAE,CAAC;KAC9D,CAAC;GACW,YAAY,CAAG"}
|