@hemia/common 0.0.6 → 0.0.8

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,4 +1,3 @@
1
-
2
1
  # @hemia/common
3
2
 
4
3
  Sistema de decoradores y utilidades para crear controladores HTTP con TypeScript usando metadata reflection.
@@ -241,6 +240,103 @@ externalRedirect() {
241
240
  // Redirige temporalmente
242
241
  }
243
242
  ```
243
+
244
+ ---
245
+
246
+ ## 🆕 Decoradores Avanzados
247
+
248
+ ### Validación y Transformación
249
+
250
+ #### `@Validate(validatorFn)`
251
+ Valida el parámetro usando una función personalizada.
252
+
253
+ ```typescript
254
+ @Post('/')
255
+ create(@Body() data: any, @Validate(validateUser) _: any) { /* ... */ }
256
+ ```
257
+
258
+ #### `@Transform(transformFn)`
259
+ Transforma el parámetro usando una función personalizada.
260
+
261
+ ```typescript
262
+ @Post('/')
263
+ create(@Body() data: any, @Transform(toUserDto) _: any) { /* ... */ }
264
+ ```
265
+
266
+ #### `@Custom(key: string)`
267
+ Extrae datos personalizados del request.
268
+
269
+ ```typescript
270
+ @Get('/')
271
+ handler(@Custom('customData') custom: any) { /* ... */ }
272
+ ```
273
+
274
+ ### Decoradores de Guardias y Autenticación
275
+
276
+ #### `@ReqUser()`
277
+ Inyecta el usuario autenticado (por ejemplo, desde JWTGuard).
278
+
279
+ ```typescript
280
+ @Get('/profile')
281
+ profile(@ReqUser() user: any) { /* ... */ }
282
+ ```
283
+
284
+ #### `@ReqPermissions()`
285
+ Inyecta los permisos del usuario.
286
+
287
+ ```typescript
288
+ @Get('/admin')
289
+ admin(@ReqPermissions() permissions: string[]) { /* ... */ }
290
+ ```
291
+
292
+ #### `@ReqContext()`
293
+ Inyecta el contexto de la solicitud.
294
+
295
+ ```typescript
296
+ @Get('/context')
297
+ context(@ReqContext() ctx: any) { /* ... */ }
298
+ ```
299
+
300
+ #### `@Cookies(key?: string)`
301
+ Extrae cookies de la petición.
302
+
303
+ ```typescript
304
+ @Get('/')
305
+ handler(@Cookies('token') token: string) { /* ... */ }
306
+ ```
307
+
308
+ #### `@Files()`
309
+ Extrae todos los archivos enviados en la solicitud.
310
+
311
+ ```typescript
312
+ @Post('/upload')
313
+ upload(@Files() files: any[]) { /* ... */ }
314
+ ```
315
+
316
+ #### `@File(key?: string)`
317
+ Extrae un archivo específico.
318
+
319
+ ```typescript
320
+ @Post('/avatar')
321
+ avatar(@File('avatar') avatar: any) { /* ... */ }
322
+ ```
323
+
324
+ #### `@Locale()`
325
+ Extrae el locale del usuario.
326
+
327
+ ```typescript
328
+ @Get('/')
329
+ handler(@Locale() locale: string) { /* ... */ }
330
+ ```
331
+
332
+ #### `@ReqAuth()`
333
+ Extrae datos de autenticación.
334
+
335
+ ```typescript
336
+ @Get('/auth')
337
+ auth(@ReqAuth() auth: any) { /* ... */ }
338
+ ```
339
+
244
340
  ---
245
341
 
246
342
  ## 🔍 Metadata Keys
@@ -288,4 +384,3 @@ ISC
288
384
  ## ✨ Generado con Hemia CLI
289
385
 
290
386
  Este paquete fue generado usando [Hemia CLI](https://www.npmjs.com/package/@hemia/cli).
291
-
@@ -1,4 +1,5 @@
1
1
  import 'reflect-metadata';
2
+ import { decorate, injectable } from 'inversify';
2
3
 
3
4
  const METADATA_KEYS = {
4
5
  BASE_PATH: 'base_path',
@@ -6,18 +7,18 @@ const METADATA_KEYS = {
6
7
  PARAMS: 'params',
7
8
  HEADERS: 'headers',
8
9
  REDIRECT: 'redirect',
10
+ COOKIES: 'cookies',
9
11
  GUARDS: 'hemia:guards',
10
12
  INTERCEPTORS: 'hemia:interceptors',
11
13
  CUSTOM: 'hemia:custom',
12
14
  SERIALIZE: 'hemia:serialize',
13
15
  ROLES: 'hemia:roles',
14
- PERMISSIONS: 'hemia:permissions'
15
- };
16
-
17
- const Controller = (basePath = '/') => {
18
- return (target) => {
19
- Reflect.defineMetadata(METADATA_KEYS.BASE_PATH, basePath, target);
20
- };
16
+ PERMISSIONS: 'hemia:permissions',
17
+ TRANSFORMERS: 'hemia:transformers',
18
+ VALIDATORS: 'hemia:validators',
19
+ CUSTOMS: 'hemia:customs',
20
+ MODULE: "hemia:module",
21
+ INJECTION_ID: "hemia:injection_id"
21
22
  };
22
23
 
23
24
  var HttpMethod;
@@ -43,6 +44,14 @@ var ParamType;
43
44
  ParamType["SESSION"] = "SESSION";
44
45
  ParamType["IP"] = "IP";
45
46
  ParamType["HOST"] = "HOST";
47
+ ParamType["COOKIES"] = "COOKIES";
48
+ ParamType["FILES"] = "FILES";
49
+ ParamType["FILE"] = "FILE";
50
+ ParamType["LOCALE"] = "LOCALE";
51
+ ParamType["AUTH"] = "AUTH";
52
+ ParamType["USER"] = "USER";
53
+ ParamType["PERMISSIONS"] = "PERMISSIONS";
54
+ ParamType["CONTEXT"] = "CONTEXT";
46
55
  })(ParamType || (ParamType = {}));
47
56
 
48
57
  const createMethodDecorator = (method) => {
@@ -71,18 +80,126 @@ const createParamDecorator = (type) => {
71
80
  };
72
81
  };
73
82
  };
83
+ /**
84
+ * Inyecta el objeto Request completo.
85
+ * @example
86
+ * async handler(@Request() req) {}
87
+ */
74
88
  const Request = createParamDecorator(ParamType.REQUEST);
89
+ /**
90
+ * Alias para Request.
91
+ * @example
92
+ * async handler(@Req() req) {}
93
+ */
75
94
  const Req = createParamDecorator(ParamType.REQUEST);
95
+ /**
96
+ * Inyecta el objeto Response.
97
+ * @example
98
+ * async handler(@Response() res) {}
99
+ */
76
100
  const Response = createParamDecorator(ParamType.RESPONSE);
101
+ /**
102
+ * Alias para Response.
103
+ * @example
104
+ * async handler(@Res() res) {}
105
+ */
77
106
  const Res = createParamDecorator(ParamType.RESPONSE);
107
+ /**
108
+ * Inyecta el objeto Next para middlewares.
109
+ * @example
110
+ * async handler(@Next() next) {}
111
+ */
78
112
  const Next = createParamDecorator(ParamType.NEXT);
113
+ /**
114
+ * Inyecta la sesión del usuario.
115
+ * @example
116
+ * async handler(@Session() session) {}
117
+ */
79
118
  const Session = createParamDecorator(ParamType.SESSION);
119
+ /**
120
+ * Inyecta la IP del cliente.
121
+ * @example
122
+ * async handler(@Ip() ip) {}
123
+ */
80
124
  const Ip = createParamDecorator(ParamType.IP);
125
+ /**
126
+ * Inyecta el host de la solicitud.
127
+ * @example
128
+ * async handler(@Host() host) {}
129
+ */
81
130
  const Host = createParamDecorator(ParamType.HOST);
131
+ /**
132
+ * Inyecta el cuerpo de la solicitud.
133
+ * @example
134
+ * async handler(@Body() body) {}
135
+ */
82
136
  const Body = createParamDecorator(ParamType.BODY);
137
+ /**
138
+ * Inyecta los parámetros de consulta (query).
139
+ * @example
140
+ * async handler(@Query('search') search) {}
141
+ */
83
142
  const Query = createParamDecorator(ParamType.QUERY);
143
+ /**
144
+ * Inyecta los parámetros de la URL.
145
+ * @example
146
+ * async handler(@Param('id') id) {}
147
+ */
84
148
  const Param = createParamDecorator(ParamType.PARAM);
149
+ /**
150
+ * Inyecta los encabezados de la solicitud.
151
+ * @example
152
+ * async handler(@Headers('authorization') auth) {}
153
+ */
85
154
  const Headers = createParamDecorator(ParamType.HEADERS);
155
+ /**
156
+ * Inyecta los cookies de la solicitud.
157
+ * @example
158
+ * async handler(@Cookies('token') token) {}
159
+ */
160
+ const Cookies = createParamDecorator(ParamType.COOKIES);
161
+ /**
162
+ * Inyecta todos los archivos enviados en la solicitud.
163
+ * @example
164
+ * async handler(@Files() files) {}
165
+ */
166
+ const Files = createParamDecorator(ParamType.FILES);
167
+ /**
168
+ * Inyecta un archivo específico de la solicitud.
169
+ * @example
170
+ * async handler(@File('avatar') avatar) {}
171
+ */
172
+ const File = createParamDecorator(ParamType.FILE);
173
+ /**
174
+ * Inyecta el locale del usuario.
175
+ * @example
176
+ * async handler(@Locale() locale) {}
177
+ */
178
+ const Locale = createParamDecorator(ParamType.LOCALE);
179
+ /**
180
+ * Inyecta datos de autenticación.
181
+ * @example
182
+ * async handler(@ReqAuth() auth) {}
183
+ */
184
+ const ReqAuth = createParamDecorator(ParamType.AUTH);
185
+ /**
186
+ * Inyecta el usuario autenticado.
187
+ * @example
188
+ * async handler(@ReqUser() user) {}
189
+ */
190
+ const ReqUser = createParamDecorator(ParamType.USER);
191
+ /**
192
+ * Inyecta los permisos del usuario.
193
+ * @example
194
+ * async handler(@ReqPermissions() permissions) {}
195
+ */
196
+ const ReqPermissions = createParamDecorator(ParamType.PERMISSIONS);
197
+ /**
198
+ * Inyecta el contexto de la solicitud.
199
+ * @example
200
+ * async handler(@ReqContext() context) {}
201
+ */
202
+ const ReqContext = createParamDecorator(ParamType.CONTEXT);
86
203
 
87
204
  const Header = (name, value) => {
88
205
  return (target, propertyKey, descriptor) => {
@@ -162,6 +279,104 @@ function Serialize(dto) {
162
279
  return SetMetadata(METADATA_KEYS.SERIALIZE, dto);
163
280
  }
164
281
 
282
+ function Module(metadata) {
283
+ return (target) => {
284
+ Reflect.defineMetadata(METADATA_KEYS.MODULE, metadata, target);
285
+ };
286
+ }
287
+
288
+ function registerProvider(target, id) {
289
+ decorate(injectable(), target);
290
+ Reflect.defineMetadata(METADATA_KEYS.INJECTION_ID, id || target, target);
291
+ }
292
+ function Service(identifier) {
293
+ return (target) => registerProvider(target, identifier);
294
+ }
295
+ function Repository(identifier) {
296
+ return (target) => registerProvider(target, identifier);
297
+ }
298
+
299
+ function isRedirectResponse(value) {
300
+ return value && typeof value === 'object' && typeof value.url === 'string';
301
+ }
302
+
303
+ class ControllerRegistry {
304
+ static register(target) {
305
+ if (!this.controllers.includes(target)) {
306
+ this.controllers.push(target);
307
+ }
308
+ }
309
+ static getAll() {
310
+ return this.controllers.slice();
311
+ }
312
+ static clear() {
313
+ this.controllers = [];
314
+ }
315
+ }
316
+ ControllerRegistry.controllers = [];
317
+
318
+ const Controller = (basePath = '/') => {
319
+ return (target) => {
320
+ decorate(injectable(), target);
321
+ Reflect.defineMetadata(METADATA_KEYS.BASE_PATH, basePath, target);
322
+ ControllerRegistry.register(target);
323
+ };
324
+ };
325
+
326
+ /**
327
+ * Decorador para extraer datos personalizados del request.
328
+ *
329
+ * @example
330
+ * // Extrae el valor de 'customData' del request
331
+ * @Custom('customData')
332
+ * async handler(@Custom() customData: any) {}
333
+ *
334
+ * @param key Clave personalizada a extraer del request.
335
+ */
336
+ function Custom(key) {
337
+ return (target, propertyKey, parameterIndex) => {
338
+ const existingCustoms = Reflect.getMetadata(METADATA_KEYS.CUSTOMS, target, propertyKey || '') || [];
339
+ existingCustoms.push({ index: parameterIndex, key });
340
+ Reflect.defineMetadata(METADATA_KEYS.CUSTOMS, existingCustoms, target, propertyKey || '');
341
+ };
342
+ }
343
+
344
+ /**
345
+ * Decorador para validar datos de entrada en un controlador.
346
+ *
347
+ * @example
348
+ * // Valida el parámetro 'body' usando la función validateUser
349
+ * @Validate(validateUser)
350
+ * async create(@Body() body: any) {}
351
+ *
352
+ * @param validator Función de validación que recibe el valor y retorna booleano o lanza error.
353
+ */
354
+ function Validate(validator) {
355
+ return (target, propertyKey, parameterIndex) => {
356
+ const existingValidators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, target, propertyKey || '') || [];
357
+ existingValidators.push({ index: parameterIndex, validator });
358
+ Reflect.defineMetadata(METADATA_KEYS.VALIDATORS, existingValidators, target, propertyKey || '');
359
+ };
360
+ }
361
+
362
+ /**
363
+ * Decorador para transformar datos de entrada en un controlador.
364
+ *
365
+ * @example
366
+ * // Transforma el parámetro 'body' usando la función toUserDto
367
+ * @Transform(toUserDto)
368
+ * async create(@Body() body: any) {}
369
+ *
370
+ * @param transformer Función de transformación que recibe el valor y retorna el nuevo valor.
371
+ */
372
+ function Transform(transformer) {
373
+ return (target, propertyKey, parameterIndex) => {
374
+ const existingTransformers = Reflect.getMetadata(METADATA_KEYS.TRANSFORMERS, target, propertyKey || '') || [];
375
+ existingTransformers.push({ index: parameterIndex, transformer });
376
+ Reflect.defineMetadata(METADATA_KEYS.TRANSFORMERS, existingTransformers, target, propertyKey || '');
377
+ };
378
+ }
379
+
165
380
  class ApiResponse {
166
381
  static success(data, message = 'OK', status = 200) {
167
382
  return {
@@ -181,10 +396,6 @@ class ApiResponse {
181
396
  }
182
397
  }
183
398
 
184
- function isRedirectResponse(value) {
185
- return value && typeof value === 'object' && typeof value.url === 'string';
186
- }
187
-
188
399
  class HttpError extends Error {
189
400
  constructor(message, statusCode, error) {
190
401
  super(message);
@@ -481,4 +692,4 @@ class InfraDataDeserializationError extends InfrastructureError {
481
692
  }
482
693
  }
483
694
 
484
- export { ApiResponse, BackupError, BadRequestError, Body, BusinessRuleViolationError, ConfigurationError, ConflictError, ConnectionError, Controller, CustomHttpError, DataConflictError, DataIntegrityError, DataMigrationError, DataNotFoundError, DataValidationError, Delete, DependencyError, DomainError, DuplicateEntityError, EntityNotFoundError, ForbiddenError, GatewayTimeoutError, Get, Head, Header, Headers, Host, HttpError, HttpErrorWithDetails, HttpMethod, IndexingError, InfraAuthenticationError, InfraAuthorizationError, InfraCacheConnectionError, InfraConfigurationError, InfraDataDeserializationError, InfraDataSerializationError, InfraDatabaseConnectionError, InfraExternalServiceError, InfraMessageQueueError, InfraNetworkError, InfraServiceUnavailableError, InfraTimeoutError, InfrastructureError, InternalServerError, Ip, METADATA_KEYS, Next, NotFoundError, OperationNotAllowedError, Options, Param, ParamType, Patch, PersistenceError, Post, Put, Query, QueryExecutionError, Redirect, Req, Request, Res, ResourceLimitError, ResourceLimitExceededError, Response, RestoreError, SchemaMismatchError, Serialize, ServiceUnavailableError, Session, SetMetadata, TimeoutError, TransactionError, UnauthorizedError, UnprocessableEntityError, UseGuards, UseInterceptors, ValidationError, isRedirectResponse };
695
+ export { ApiResponse, BackupError, BadRequestError, Body, BusinessRuleViolationError, ConfigurationError, ConflictError, ConnectionError, Controller, ControllerRegistry, Cookies, Custom, CustomHttpError, DataConflictError, DataIntegrityError, DataMigrationError, DataNotFoundError, DataValidationError, Delete, DependencyError, DomainError, DuplicateEntityError, EntityNotFoundError, File, Files, ForbiddenError, GatewayTimeoutError, Get, Head, Header, Headers, Host, HttpError, HttpErrorWithDetails, HttpMethod, IndexingError, InfraAuthenticationError, InfraAuthorizationError, InfraCacheConnectionError, InfraConfigurationError, InfraDataDeserializationError, InfraDataSerializationError, InfraDatabaseConnectionError, InfraExternalServiceError, InfraMessageQueueError, InfraNetworkError, InfraServiceUnavailableError, InfraTimeoutError, InfrastructureError, InternalServerError, Ip, Locale, METADATA_KEYS, Module, Next, NotFoundError, OperationNotAllowedError, Options, Param, ParamType, Patch, PersistenceError, Post, Put, Query, QueryExecutionError, Redirect, Repository, Req, ReqAuth, ReqContext, ReqPermissions, ReqUser, Request, Res, ResourceLimitError, ResourceLimitExceededError, Response, RestoreError, SchemaMismatchError, Serialize, Service, ServiceUnavailableError, Session, SetMetadata, TimeoutError, TransactionError, Transform, UnauthorizedError, UnprocessableEntityError, UseGuards, UseInterceptors, Validate, ValidationError, isRedirectResponse };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  require('reflect-metadata');
4
+ var inversify = require('inversify');
4
5
 
5
6
  const METADATA_KEYS = {
6
7
  BASE_PATH: 'base_path',
@@ -8,18 +9,18 @@ const METADATA_KEYS = {
8
9
  PARAMS: 'params',
9
10
  HEADERS: 'headers',
10
11
  REDIRECT: 'redirect',
12
+ COOKIES: 'cookies',
11
13
  GUARDS: 'hemia:guards',
12
14
  INTERCEPTORS: 'hemia:interceptors',
13
15
  CUSTOM: 'hemia:custom',
14
16
  SERIALIZE: 'hemia:serialize',
15
17
  ROLES: 'hemia:roles',
16
- PERMISSIONS: 'hemia:permissions'
17
- };
18
-
19
- const Controller = (basePath = '/') => {
20
- return (target) => {
21
- Reflect.defineMetadata(METADATA_KEYS.BASE_PATH, basePath, target);
22
- };
18
+ PERMISSIONS: 'hemia:permissions',
19
+ TRANSFORMERS: 'hemia:transformers',
20
+ VALIDATORS: 'hemia:validators',
21
+ CUSTOMS: 'hemia:customs',
22
+ MODULE: "hemia:module",
23
+ INJECTION_ID: "hemia:injection_id"
23
24
  };
24
25
 
25
26
  exports.HttpMethod = void 0;
@@ -45,6 +46,14 @@ exports.ParamType = void 0;
45
46
  ParamType["SESSION"] = "SESSION";
46
47
  ParamType["IP"] = "IP";
47
48
  ParamType["HOST"] = "HOST";
49
+ ParamType["COOKIES"] = "COOKIES";
50
+ ParamType["FILES"] = "FILES";
51
+ ParamType["FILE"] = "FILE";
52
+ ParamType["LOCALE"] = "LOCALE";
53
+ ParamType["AUTH"] = "AUTH";
54
+ ParamType["USER"] = "USER";
55
+ ParamType["PERMISSIONS"] = "PERMISSIONS";
56
+ ParamType["CONTEXT"] = "CONTEXT";
48
57
  })(exports.ParamType || (exports.ParamType = {}));
49
58
 
50
59
  const createMethodDecorator = (method) => {
@@ -73,18 +82,126 @@ const createParamDecorator = (type) => {
73
82
  };
74
83
  };
75
84
  };
85
+ /**
86
+ * Inyecta el objeto Request completo.
87
+ * @example
88
+ * async handler(@Request() req) {}
89
+ */
76
90
  const Request = createParamDecorator(exports.ParamType.REQUEST);
91
+ /**
92
+ * Alias para Request.
93
+ * @example
94
+ * async handler(@Req() req) {}
95
+ */
77
96
  const Req = createParamDecorator(exports.ParamType.REQUEST);
97
+ /**
98
+ * Inyecta el objeto Response.
99
+ * @example
100
+ * async handler(@Response() res) {}
101
+ */
78
102
  const Response = createParamDecorator(exports.ParamType.RESPONSE);
103
+ /**
104
+ * Alias para Response.
105
+ * @example
106
+ * async handler(@Res() res) {}
107
+ */
79
108
  const Res = createParamDecorator(exports.ParamType.RESPONSE);
109
+ /**
110
+ * Inyecta el objeto Next para middlewares.
111
+ * @example
112
+ * async handler(@Next() next) {}
113
+ */
80
114
  const Next = createParamDecorator(exports.ParamType.NEXT);
115
+ /**
116
+ * Inyecta la sesión del usuario.
117
+ * @example
118
+ * async handler(@Session() session) {}
119
+ */
81
120
  const Session = createParamDecorator(exports.ParamType.SESSION);
121
+ /**
122
+ * Inyecta la IP del cliente.
123
+ * @example
124
+ * async handler(@Ip() ip) {}
125
+ */
82
126
  const Ip = createParamDecorator(exports.ParamType.IP);
127
+ /**
128
+ * Inyecta el host de la solicitud.
129
+ * @example
130
+ * async handler(@Host() host) {}
131
+ */
83
132
  const Host = createParamDecorator(exports.ParamType.HOST);
133
+ /**
134
+ * Inyecta el cuerpo de la solicitud.
135
+ * @example
136
+ * async handler(@Body() body) {}
137
+ */
84
138
  const Body = createParamDecorator(exports.ParamType.BODY);
139
+ /**
140
+ * Inyecta los parámetros de consulta (query).
141
+ * @example
142
+ * async handler(@Query('search') search) {}
143
+ */
85
144
  const Query = createParamDecorator(exports.ParamType.QUERY);
145
+ /**
146
+ * Inyecta los parámetros de la URL.
147
+ * @example
148
+ * async handler(@Param('id') id) {}
149
+ */
86
150
  const Param = createParamDecorator(exports.ParamType.PARAM);
151
+ /**
152
+ * Inyecta los encabezados de la solicitud.
153
+ * @example
154
+ * async handler(@Headers('authorization') auth) {}
155
+ */
87
156
  const Headers = createParamDecorator(exports.ParamType.HEADERS);
157
+ /**
158
+ * Inyecta los cookies de la solicitud.
159
+ * @example
160
+ * async handler(@Cookies('token') token) {}
161
+ */
162
+ const Cookies = createParamDecorator(exports.ParamType.COOKIES);
163
+ /**
164
+ * Inyecta todos los archivos enviados en la solicitud.
165
+ * @example
166
+ * async handler(@Files() files) {}
167
+ */
168
+ const Files = createParamDecorator(exports.ParamType.FILES);
169
+ /**
170
+ * Inyecta un archivo específico de la solicitud.
171
+ * @example
172
+ * async handler(@File('avatar') avatar) {}
173
+ */
174
+ const File = createParamDecorator(exports.ParamType.FILE);
175
+ /**
176
+ * Inyecta el locale del usuario.
177
+ * @example
178
+ * async handler(@Locale() locale) {}
179
+ */
180
+ const Locale = createParamDecorator(exports.ParamType.LOCALE);
181
+ /**
182
+ * Inyecta datos de autenticación.
183
+ * @example
184
+ * async handler(@ReqAuth() auth) {}
185
+ */
186
+ const ReqAuth = createParamDecorator(exports.ParamType.AUTH);
187
+ /**
188
+ * Inyecta el usuario autenticado.
189
+ * @example
190
+ * async handler(@ReqUser() user) {}
191
+ */
192
+ const ReqUser = createParamDecorator(exports.ParamType.USER);
193
+ /**
194
+ * Inyecta los permisos del usuario.
195
+ * @example
196
+ * async handler(@ReqPermissions() permissions) {}
197
+ */
198
+ const ReqPermissions = createParamDecorator(exports.ParamType.PERMISSIONS);
199
+ /**
200
+ * Inyecta el contexto de la solicitud.
201
+ * @example
202
+ * async handler(@ReqContext() context) {}
203
+ */
204
+ const ReqContext = createParamDecorator(exports.ParamType.CONTEXT);
88
205
 
89
206
  const Header = (name, value) => {
90
207
  return (target, propertyKey, descriptor) => {
@@ -164,6 +281,104 @@ function Serialize(dto) {
164
281
  return SetMetadata(METADATA_KEYS.SERIALIZE, dto);
165
282
  }
166
283
 
284
+ function Module(metadata) {
285
+ return (target) => {
286
+ Reflect.defineMetadata(METADATA_KEYS.MODULE, metadata, target);
287
+ };
288
+ }
289
+
290
+ function registerProvider(target, id) {
291
+ inversify.decorate(inversify.injectable(), target);
292
+ Reflect.defineMetadata(METADATA_KEYS.INJECTION_ID, id || target, target);
293
+ }
294
+ function Service(identifier) {
295
+ return (target) => registerProvider(target, identifier);
296
+ }
297
+ function Repository(identifier) {
298
+ return (target) => registerProvider(target, identifier);
299
+ }
300
+
301
+ function isRedirectResponse(value) {
302
+ return value && typeof value === 'object' && typeof value.url === 'string';
303
+ }
304
+
305
+ class ControllerRegistry {
306
+ static register(target) {
307
+ if (!this.controllers.includes(target)) {
308
+ this.controllers.push(target);
309
+ }
310
+ }
311
+ static getAll() {
312
+ return this.controllers.slice();
313
+ }
314
+ static clear() {
315
+ this.controllers = [];
316
+ }
317
+ }
318
+ ControllerRegistry.controllers = [];
319
+
320
+ const Controller = (basePath = '/') => {
321
+ return (target) => {
322
+ inversify.decorate(inversify.injectable(), target);
323
+ Reflect.defineMetadata(METADATA_KEYS.BASE_PATH, basePath, target);
324
+ ControllerRegistry.register(target);
325
+ };
326
+ };
327
+
328
+ /**
329
+ * Decorador para extraer datos personalizados del request.
330
+ *
331
+ * @example
332
+ * // Extrae el valor de 'customData' del request
333
+ * @Custom('customData')
334
+ * async handler(@Custom() customData: any) {}
335
+ *
336
+ * @param key Clave personalizada a extraer del request.
337
+ */
338
+ function Custom(key) {
339
+ return (target, propertyKey, parameterIndex) => {
340
+ const existingCustoms = Reflect.getMetadata(METADATA_KEYS.CUSTOMS, target, propertyKey || '') || [];
341
+ existingCustoms.push({ index: parameterIndex, key });
342
+ Reflect.defineMetadata(METADATA_KEYS.CUSTOMS, existingCustoms, target, propertyKey || '');
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Decorador para validar datos de entrada en un controlador.
348
+ *
349
+ * @example
350
+ * // Valida el parámetro 'body' usando la función validateUser
351
+ * @Validate(validateUser)
352
+ * async create(@Body() body: any) {}
353
+ *
354
+ * @param validator Función de validación que recibe el valor y retorna booleano o lanza error.
355
+ */
356
+ function Validate(validator) {
357
+ return (target, propertyKey, parameterIndex) => {
358
+ const existingValidators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, target, propertyKey || '') || [];
359
+ existingValidators.push({ index: parameterIndex, validator });
360
+ Reflect.defineMetadata(METADATA_KEYS.VALIDATORS, existingValidators, target, propertyKey || '');
361
+ };
362
+ }
363
+
364
+ /**
365
+ * Decorador para transformar datos de entrada en un controlador.
366
+ *
367
+ * @example
368
+ * // Transforma el parámetro 'body' usando la función toUserDto
369
+ * @Transform(toUserDto)
370
+ * async create(@Body() body: any) {}
371
+ *
372
+ * @param transformer Función de transformación que recibe el valor y retorna el nuevo valor.
373
+ */
374
+ function Transform(transformer) {
375
+ return (target, propertyKey, parameterIndex) => {
376
+ const existingTransformers = Reflect.getMetadata(METADATA_KEYS.TRANSFORMERS, target, propertyKey || '') || [];
377
+ existingTransformers.push({ index: parameterIndex, transformer });
378
+ Reflect.defineMetadata(METADATA_KEYS.TRANSFORMERS, existingTransformers, target, propertyKey || '');
379
+ };
380
+ }
381
+
167
382
  class ApiResponse {
168
383
  static success(data, message = 'OK', status = 200) {
169
384
  return {
@@ -183,10 +398,6 @@ class ApiResponse {
183
398
  }
184
399
  }
185
400
 
186
- function isRedirectResponse(value) {
187
- return value && typeof value === 'object' && typeof value.url === 'string';
188
- }
189
-
190
401
  class HttpError extends Error {
191
402
  constructor(message, statusCode, error) {
192
403
  super(message);
@@ -492,6 +703,9 @@ exports.ConfigurationError = ConfigurationError;
492
703
  exports.ConflictError = ConflictError;
493
704
  exports.ConnectionError = ConnectionError;
494
705
  exports.Controller = Controller;
706
+ exports.ControllerRegistry = ControllerRegistry;
707
+ exports.Cookies = Cookies;
708
+ exports.Custom = Custom;
495
709
  exports.CustomHttpError = CustomHttpError;
496
710
  exports.DataConflictError = DataConflictError;
497
711
  exports.DataIntegrityError = DataIntegrityError;
@@ -503,6 +717,8 @@ exports.DependencyError = DependencyError;
503
717
  exports.DomainError = DomainError;
504
718
  exports.DuplicateEntityError = DuplicateEntityError;
505
719
  exports.EntityNotFoundError = EntityNotFoundError;
720
+ exports.File = File;
721
+ exports.Files = Files;
506
722
  exports.ForbiddenError = ForbiddenError;
507
723
  exports.GatewayTimeoutError = GatewayTimeoutError;
508
724
  exports.Get = Get;
@@ -528,7 +744,9 @@ exports.InfraTimeoutError = InfraTimeoutError;
528
744
  exports.InfrastructureError = InfrastructureError;
529
745
  exports.InternalServerError = InternalServerError;
530
746
  exports.Ip = Ip;
747
+ exports.Locale = Locale;
531
748
  exports.METADATA_KEYS = METADATA_KEYS;
749
+ exports.Module = Module;
532
750
  exports.Next = Next;
533
751
  exports.NotFoundError = NotFoundError;
534
752
  exports.OperationNotAllowedError = OperationNotAllowedError;
@@ -541,7 +759,12 @@ exports.Put = Put;
541
759
  exports.Query = Query;
542
760
  exports.QueryExecutionError = QueryExecutionError;
543
761
  exports.Redirect = Redirect;
762
+ exports.Repository = Repository;
544
763
  exports.Req = Req;
764
+ exports.ReqAuth = ReqAuth;
765
+ exports.ReqContext = ReqContext;
766
+ exports.ReqPermissions = ReqPermissions;
767
+ exports.ReqUser = ReqUser;
545
768
  exports.Request = Request;
546
769
  exports.Res = Res;
547
770
  exports.ResourceLimitError = ResourceLimitError;
@@ -550,14 +773,17 @@ exports.Response = Response;
550
773
  exports.RestoreError = RestoreError;
551
774
  exports.SchemaMismatchError = SchemaMismatchError;
552
775
  exports.Serialize = Serialize;
776
+ exports.Service = Service;
553
777
  exports.ServiceUnavailableError = ServiceUnavailableError;
554
778
  exports.Session = Session;
555
779
  exports.SetMetadata = SetMetadata;
556
780
  exports.TimeoutError = TimeoutError;
557
781
  exports.TransactionError = TransactionError;
782
+ exports.Transform = Transform;
558
783
  exports.UnauthorizedError = UnauthorizedError;
559
784
  exports.UnprocessableEntityError = UnprocessableEntityError;
560
785
  exports.UseGuards = UseGuards;
561
786
  exports.UseInterceptors = UseInterceptors;
787
+ exports.Validate = Validate;
562
788
  exports.ValidationError = ValidationError;
563
789
  exports.isRedirectResponse = isRedirectResponse;
@@ -2,3 +2,6 @@ export * from "./use-guards.decorator";
2
2
  export * from "./use-interceptors.decorator";
3
3
  export * from "./set-metadata.decorator";
4
4
  export * from "./serialize.decorator";
5
+ export * from "./module.decorator";
6
+ export * from "./service.decorator";
7
+ export * from "./controller.decorator";
@@ -0,0 +1,8 @@
1
+ import "reflect-metadata";
2
+ export interface ModuleMetadata {
3
+ imports?: any[];
4
+ controllers?: any[];
5
+ providers?: any[];
6
+ exports?: any[];
7
+ }
8
+ export declare function Module(metadata: ModuleMetadata): ClassDecorator;
@@ -0,0 +1,2 @@
1
+ export declare function Service(identifier?: symbol | string): ClassDecorator;
2
+ export declare function Repository(identifier?: symbol | string): ClassDecorator;
@@ -0,0 +1,12 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * Decorador para extraer datos personalizados del request.
4
+ *
5
+ * @example
6
+ * // Extrae el valor de 'customData' del request
7
+ * @Custom('customData')
8
+ * async handler(@Custom() customData: any) {}
9
+ *
10
+ * @param key Clave personalizada a extraer del request.
11
+ */
12
+ export declare function Custom(key: string): ParameterDecorator;
@@ -0,0 +1 @@
1
+ export * from './custom.decorator';
@@ -1,4 +1,3 @@
1
- export * from "./controller.decorator";
2
1
  export * from "./method.decorators";
3
2
  export * from "./param.decorators";
4
3
  export * from "./response.decorators";
@@ -1,13 +1,121 @@
1
1
  import 'reflect-metadata';
2
+ /**
3
+ * Inyecta el objeto Request completo.
4
+ * @example
5
+ * async handler(@Request() req) {}
6
+ */
2
7
  export declare const Request: (data?: string) => ParameterDecorator;
8
+ /**
9
+ * Alias para Request.
10
+ * @example
11
+ * async handler(@Req() req) {}
12
+ */
3
13
  export declare const Req: (data?: string) => ParameterDecorator;
14
+ /**
15
+ * Inyecta el objeto Response.
16
+ * @example
17
+ * async handler(@Response() res) {}
18
+ */
4
19
  export declare const Response: (data?: string) => ParameterDecorator;
20
+ /**
21
+ * Alias para Response.
22
+ * @example
23
+ * async handler(@Res() res) {}
24
+ */
5
25
  export declare const Res: (data?: string) => ParameterDecorator;
26
+ /**
27
+ * Inyecta el objeto Next para middlewares.
28
+ * @example
29
+ * async handler(@Next() next) {}
30
+ */
6
31
  export declare const Next: (data?: string) => ParameterDecorator;
32
+ /**
33
+ * Inyecta la sesión del usuario.
34
+ * @example
35
+ * async handler(@Session() session) {}
36
+ */
7
37
  export declare const Session: (data?: string) => ParameterDecorator;
38
+ /**
39
+ * Inyecta la IP del cliente.
40
+ * @example
41
+ * async handler(@Ip() ip) {}
42
+ */
8
43
  export declare const Ip: (data?: string) => ParameterDecorator;
44
+ /**
45
+ * Inyecta el host de la solicitud.
46
+ * @example
47
+ * async handler(@Host() host) {}
48
+ */
9
49
  export declare const Host: (data?: string) => ParameterDecorator;
50
+ /**
51
+ * Inyecta el cuerpo de la solicitud.
52
+ * @example
53
+ * async handler(@Body() body) {}
54
+ */
10
55
  export declare const Body: (data?: string) => ParameterDecorator;
56
+ /**
57
+ * Inyecta los parámetros de consulta (query).
58
+ * @example
59
+ * async handler(@Query('search') search) {}
60
+ */
11
61
  export declare const Query: (data?: string) => ParameterDecorator;
62
+ /**
63
+ * Inyecta los parámetros de la URL.
64
+ * @example
65
+ * async handler(@Param('id') id) {}
66
+ */
12
67
  export declare const Param: (data?: string) => ParameterDecorator;
68
+ /**
69
+ * Inyecta los encabezados de la solicitud.
70
+ * @example
71
+ * async handler(@Headers('authorization') auth) {}
72
+ */
13
73
  export declare const Headers: (data?: string) => ParameterDecorator;
74
+ /**
75
+ * Inyecta los cookies de la solicitud.
76
+ * @example
77
+ * async handler(@Cookies('token') token) {}
78
+ */
79
+ export declare const Cookies: (data?: string) => ParameterDecorator;
80
+ /**
81
+ * Inyecta todos los archivos enviados en la solicitud.
82
+ * @example
83
+ * async handler(@Files() files) {}
84
+ */
85
+ export declare const Files: (data?: string) => ParameterDecorator;
86
+ /**
87
+ * Inyecta un archivo específico de la solicitud.
88
+ * @example
89
+ * async handler(@File('avatar') avatar) {}
90
+ */
91
+ export declare const File: (data?: string) => ParameterDecorator;
92
+ /**
93
+ * Inyecta el locale del usuario.
94
+ * @example
95
+ * async handler(@Locale() locale) {}
96
+ */
97
+ export declare const Locale: (data?: string) => ParameterDecorator;
98
+ /**
99
+ * Inyecta datos de autenticación.
100
+ * @example
101
+ * async handler(@ReqAuth() auth) {}
102
+ */
103
+ export declare const ReqAuth: (data?: string) => ParameterDecorator;
104
+ /**
105
+ * Inyecta el usuario autenticado.
106
+ * @example
107
+ * async handler(@ReqUser() user) {}
108
+ */
109
+ export declare const ReqUser: (data?: string) => ParameterDecorator;
110
+ /**
111
+ * Inyecta los permisos del usuario.
112
+ * @example
113
+ * async handler(@ReqPermissions() permissions) {}
114
+ */
115
+ export declare const ReqPermissions: (data?: string) => ParameterDecorator;
116
+ /**
117
+ * Inyecta el contexto de la solicitud.
118
+ * @example
119
+ * async handler(@ReqContext() context) {}
120
+ */
121
+ export declare const ReqContext: (data?: string) => ParameterDecorator;
@@ -1,3 +1,6 @@
1
1
  export * from "./http";
2
2
  export * from "./metadata";
3
3
  export * from "./core";
4
+ export * from "./custom";
5
+ export * from "./validation";
6
+ export * from "./transform";
@@ -4,10 +4,16 @@ export declare const METADATA_KEYS: {
4
4
  readonly PARAMS: "params";
5
5
  readonly HEADERS: "headers";
6
6
  readonly REDIRECT: "redirect";
7
+ readonly COOKIES: "cookies";
7
8
  readonly GUARDS: "hemia:guards";
8
9
  readonly INTERCEPTORS: "hemia:interceptors";
9
10
  readonly CUSTOM: "hemia:custom";
10
11
  readonly SERIALIZE: "hemia:serialize";
11
12
  readonly ROLES: "hemia:roles";
12
13
  readonly PERMISSIONS: "hemia:permissions";
14
+ readonly TRANSFORMERS: "hemia:transformers";
15
+ readonly VALIDATORS: "hemia:validators";
16
+ readonly CUSTOMS: "hemia:customs";
17
+ readonly MODULE: "hemia:module";
18
+ readonly INJECTION_ID: "hemia:injection_id";
13
19
  };
@@ -0,0 +1 @@
1
+ export * from './transform.decorator';
@@ -0,0 +1,12 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * Decorador para transformar datos de entrada en un controlador.
4
+ *
5
+ * @example
6
+ * // Transforma el parámetro 'body' usando la función toUserDto
7
+ * @Transform(toUserDto)
8
+ * async create(@Body() body: any) {}
9
+ *
10
+ * @param transformer Función de transformación que recibe el valor y retorna el nuevo valor.
11
+ */
12
+ export declare function Transform(transformer: (value: any) => any): ParameterDecorator;
@@ -0,0 +1 @@
1
+ export * from "./validate.decorator";
@@ -0,0 +1,12 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * Decorador para validar datos de entrada en un controlador.
4
+ *
5
+ * @example
6
+ * // Valida el parámetro 'body' usando la función validateUser
7
+ * @Validate(validateUser)
8
+ * async create(@Body() body: any) {}
9
+ *
10
+ * @param validator Función de validación que recibe el valor y retorna booleano o lanza error.
11
+ */
12
+ export declare function Validate(validator: (value: any) => boolean | void): ParameterDecorator;
@@ -8,5 +8,13 @@ export declare enum ParamType {
8
8
  HEADERS = "HEADERS",
9
9
  SESSION = "SESSION",
10
10
  IP = "IP",
11
- HOST = "HOST"
11
+ HOST = "HOST",
12
+ COOKIES = "COOKIES",
13
+ FILES = "FILES",
14
+ FILE = "FILE",
15
+ LOCALE = "LOCALE",
16
+ AUTH = "AUTH",
17
+ USER = "USER",
18
+ PERMISSIONS = "PERMISSIONS",
19
+ CONTEXT = "CONTEXT"
12
20
  }
@@ -0,0 +1,6 @@
1
+ export declare class ControllerRegistry {
2
+ private static controllers;
3
+ static register(target: Function): void;
4
+ static getAll(): Function[];
5
+ static clear(): void;
6
+ }
@@ -1 +1,2 @@
1
1
  export * from "./type-guards";
2
+ export * from "./controller-registry";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hemia/common",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Paquete común para proyectos de Hemia",
5
5
  "main": "dist/hemia-common.js",
6
6
  "module": "dist/hemia-common.esm.js",
@@ -28,7 +28,8 @@
28
28
  "rollup-plugin-typescript2": "^0.36.0",
29
29
  "ts-jest": "^29.2.5",
30
30
  "ts-node": "^8.9.0",
31
- "typescript": "^5.5.4"
31
+ "typescript": "^5.5.4",
32
+ "inversify": "^7.11.0"
32
33
  },
33
34
  "peerDependencies": {
34
35
  "reflect-metadata": "^0.2.2"
@@ -37,5 +38,8 @@
37
38
  "license": "ISC",
38
39
  "files": [
39
40
  "dist"
40
- ]
41
+ ],
42
+ "dependencies": {
43
+
44
+ }
41
45
  }