framework-do-dede 3.3.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +515 -4
  2. package/dist/application/controller.d.ts +6 -2
  3. package/dist/application/controller.js +10 -18
  4. package/dist/application/index.d.ts +2 -2
  5. package/dist/application/index.js +2 -2
  6. package/dist/application/services.d.ts +2 -1
  7. package/dist/application/services.js +3 -3
  8. package/dist/dede.d.ts +10 -1
  9. package/dist/dede.js +30 -9
  10. package/dist/domain/entity.d.ts +4 -0
  11. package/dist/domain/entity.js +25 -0
  12. package/dist/domain/errors/app-error.d.ts +12 -0
  13. package/dist/domain/errors/app-error.js +14 -0
  14. package/dist/domain/errors/http-errors.d.ts +42 -0
  15. package/dist/domain/errors/http-errors.js +40 -0
  16. package/dist/domain/index.d.ts +2 -0
  17. package/dist/domain/index.js +2 -0
  18. package/dist/http/controller.handler.d.ts +4 -5
  19. package/dist/http/controller.handler.js +27 -119
  20. package/dist/http/errors/server.d.ts +2 -28
  21. package/dist/http/errors/server.js +2 -49
  22. package/dist/http/http-server.d.ts +2 -0
  23. package/dist/http/http-server.js +1 -1
  24. package/dist/http/index.d.ts +2 -2
  25. package/dist/http/index.js +2 -2
  26. package/dist/index.d.ts +4 -3
  27. package/dist/index.js +4 -3
  28. package/dist/infra/di/registry.d.ts +4 -6
  29. package/dist/infra/di/registry.js +7 -10
  30. package/dist/{application → infra/serialization}/entity.d.ts +8 -1
  31. package/dist/{application → infra/serialization}/entity.js +87 -23
  32. package/dist/interface/errors/http-error-mapper.d.ts +10 -0
  33. package/dist/interface/errors/http-error-mapper.js +31 -0
  34. package/dist/interface/http/middleware-executor.d.ts +10 -0
  35. package/dist/interface/http/middleware-executor.js +33 -0
  36. package/dist/interface/http/request-mapper.d.ts +21 -0
  37. package/dist/interface/http/request-mapper.js +55 -0
  38. package/dist/interface/validation/class-validator.d.ts +6 -0
  39. package/dist/interface/validation/class-validator.js +28 -0
  40. package/dist/interface/validation/validator.d.ts +5 -0
  41. package/dist/interface/validation/validator.js +1 -0
  42. package/dist/protocols/repository.d.ts +1 -1
  43. package/package.json +5 -2
package/README.md CHANGED
@@ -1,15 +1,526 @@
1
1
  # Framework do Dedé
2
2
 
3
- To install dependencies:
3
+ Um framework TypeScript simples para construir APIs HTTP com controllers, use cases e entities, com suporte a Express ou Elysia, DI leve e serialização de entidades.
4
+
5
+ ## Índice
6
+
7
+ - Instalação
8
+ - Quickstart
9
+ - Conceitos
10
+ - Controllers e Rotas
11
+ - Input, params e filtros
12
+ - Middlewares
13
+ - Tracing
14
+ - UseCase e Decorators
15
+ - Entity e Serialização
16
+ - Hooks Before/After ToEntity
17
+ - Storage Gateway
18
+ - DI (Container/Inject)
19
+ - Errors
20
+ - Protocolos de Repositório
21
+ - Exemplos
22
+ - Express
23
+ - Elysia
24
+ - Fila com AfterToEntity
25
+ - Testes
26
+ - Benchmark
27
+
28
+ ## Instalação
4
29
 
5
30
  ```bash
6
31
  bun install
7
32
  ```
8
33
 
9
- To run:
34
+ Para executar o exemplo (Express e Elysia):
35
+
36
+ ```bash
37
+ bun run example/express_app/server.ts
38
+ ```
39
+
40
+ ## Quickstart
41
+
42
+ ```ts
43
+ import { Controller, Get, Post, UseCase, Dede } from './src';
44
+
45
+ @Controller('/hello')
46
+ class HelloController {
47
+ @Get({ statusCode: 200 })
48
+ async get() {
49
+ const useCase = new HelloUseCase({ data: undefined });
50
+ return await useCase.execute();
51
+ }
52
+
53
+ @Post({ statusCode: 201, body: ['name|string'] })
54
+ async post(request: { data: { name: string } }) {
55
+ const useCase = new HelloUseCase({ data: request.data });
56
+ return await useCase.execute();
57
+ }
58
+ }
59
+
60
+ class HelloUseCase extends UseCase<{ name?: string }, { message: string }> {
61
+ async execute() {
62
+ return { message: `Hello ${this.data?.name ?? 'world'}` };
63
+ }
64
+ }
65
+
66
+ const app = await Dede.create({
67
+ framework: { use: 'express', port: 3000 },
68
+ registries: []
69
+ });
70
+ app.registerControllers([HelloController]);
71
+ app.listen();
72
+ ```
73
+
74
+ ## Conceitos
75
+
76
+ ### Controllers e Rotas
77
+
78
+ Use decorators para expor métodos como rotas HTTP. O Controller define metadados, e o ControllerHandler monta as rotas em runtime a partir da lista de controllers passada ao `app.registerControllers(...)`.
79
+
80
+ ```ts
81
+ import { Controller, Get, Post, Put, Delete, Patch } from './src';
82
+
83
+ @Controller('/users')
84
+ export class UsersController {
85
+ @Get({ statusCode: 200 })
86
+ async list() { /* ... */ }
87
+
88
+ @Post({ statusCode: 201, body: ['name|string', 'email|string'] })
89
+ async create(request: { data: any }) { /* ... */ }
90
+
91
+ @Put({ params: ['id|string'], body: ['name|string'] })
92
+ async update(request: { data: any }) { /* ... */ }
93
+
94
+ @Delete({ params: ['id|string'] })
95
+ async remove(request: { data: any }) { /* ... */ }
96
+ }
97
+ ```
98
+
99
+ Decorators disponíveis:
100
+
101
+ - `@Controller(basePath?: string)`
102
+ - `@Get`, `@Post`, `@Put`, `@Patch`, `@Delete`
103
+
104
+ Opções de rota (comuns):
105
+
106
+ - `path`: string
107
+ - `statusCode`: number
108
+ - `params`, `query`, `headers`, `body`: array de strings no formato `campo|tipo`
109
+ - `bodyFilter`: `"restrict" | "none"`
110
+ - `responseType`: `"json" | "text" | "html"`
111
+ - `validator`: pode ser uma classe com decorators do `class-validator` **ou** um objeto com `validate(data)` (sync/async)
112
+
113
+ ### Input, params e filtros
114
+
115
+ O framework compõe um objeto `request.data` a partir de:
116
+
117
+ 1) headers filtrados
118
+ 2) params filtrados
119
+ 3) query filtrada
120
+ 4) body filtrado
121
+
122
+ Quando `bodyFilter: "restrict"`, apenas os campos definidos em `body` serão usados. Caso contrário, o corpo completo é mesclado.
123
+
124
+ Tipos suportados no filtro:
125
+
126
+ - `boolean`, `integer`, `string`, `number`
127
+
128
+ Exemplos:
129
+
130
+ ```ts
131
+ @Put({
132
+ params: ['id|string'],
133
+ query: ['active|boolean'],
134
+ headers: ['x-type|string'],
135
+ body: ['name|string'],
136
+ bodyFilter: 'restrict',
137
+ validator: CreateUserDto
138
+ })
139
+ async update(request: { data: any }) {
140
+ // request.data: { id, active, 'x-type', name }
141
+ }
142
+ ```
143
+
144
+ ```ts
145
+ import 'reflect-metadata'
146
+ import { IsEmail, IsNotEmpty } from 'class-validator'
147
+
148
+ class CreateUserDto {
149
+ @IsNotEmpty({ message: 'O nome é obrigatório.' })
150
+ name!: string
151
+
152
+ @IsEmail({}, { message: 'Email inválido.' })
153
+ email?: string
154
+ }
155
+ ```
156
+
157
+ ```ts
158
+ @Put({
159
+ body: ['name|string', 'email|string'],
160
+ bodyFilter: 'restrict',
161
+ validator: CreateUserDto
162
+ })
163
+ async update(request: { data: any }) {}
164
+ ```
165
+
166
+ Obs: o framework usa `class-validator` como `peerDependency`, então o projeto que consome deve ter a mesma versão instalada.
167
+
168
+ Suporte a notacao com colchetes:
169
+
170
+ ```json
171
+ { "user[name]": "Joao", "user[email]": "a@b.com" }
172
+ ```
173
+
174
+ vira:
175
+
176
+ ```json
177
+ { "user": { "name": "Joao", "email": "a@b.com" } }
178
+ ```
179
+
180
+ ### Middlewares
181
+
182
+ Middlewares devem implementar `execute(input: Input<any>)`. Podem ser classe, factory ou instancia.
183
+
184
+ ```ts
185
+ import { Middleware, UseMiddleware, UseMiddlewares, Input } from './src';
186
+
187
+ class AuthMiddleware implements Middleware {
188
+ async execute(input: Input<any>) {
189
+ input.context.auth = { userId: 123 };
190
+ }
191
+ }
192
+
193
+ @Controller('/secure')
194
+ class SecureController {
195
+ @Get()
196
+ @UseMiddleware(AuthMiddleware)
197
+ async get(request: { data: any; context: any }) {
198
+ return { userId: request.context.auth.userId };
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### Tracing
204
+
205
+ Use `@Tracing` no controller ou em um metodo para capturar metadados de request.
206
+
207
+ ```ts
208
+ import { Tracing, Tracer, TracerData } from './src';
209
+
210
+ class ConsoleTracer implements Tracer<void> {
211
+ trace(data: TracerData) {
212
+ console.log(data);
213
+ }
214
+ }
215
+
216
+ @Tracing(new ConsoleTracer())
217
+ @Controller('/trace')
218
+ class TraceController {
219
+ @Get()
220
+ async get() { return { ok: true }; }
221
+ }
222
+ ```
223
+
224
+ ### UseCase e Decorators
225
+
226
+ UseCase provê `data` e `context` do request.
227
+
228
+ ```ts
229
+ import { UseCase } from './src';
230
+
231
+ class CreateUserUseCase extends UseCase<{ name: string }, { id: string }> {
232
+ async execute() {
233
+ return { id: 'new-id' };
234
+ }
235
+ }
236
+ ```
237
+
238
+ Decorator `@DecorateUseCase` permite executar use cases antes do principal (chaining).
239
+
240
+ ```ts
241
+ import { UseCase, DecorateUseCase } from './src';
242
+
243
+ class AuditUseCase extends UseCase<any, void> {
244
+ async execute() { /* audit */ }
245
+ }
246
+
247
+ @DecorateUseCase({ useCase: AuditUseCase })
248
+ class CreateUserUseCase extends UseCase<{ name: string }, { id: string }> {
249
+ async execute() { return { id: 'new-id' }; }
250
+ }
251
+ ```
252
+
253
+ ### Entity e Serializacao
254
+
255
+ Entities suportam:
256
+
257
+ - `toEntity()` e `toAsyncEntity()`
258
+ - `toData()` e `toAsyncData()`
259
+ - `@Serialize`, `@Restrict`, `@VirtualProperty`, `@GetterPrefix`
260
+
261
+ Obs: a serializacao fica na camada de infraestrutura, mas a API continua sendo exposta pelo framework (importe direto de `./src`).
262
+
263
+ ```ts
264
+ import { Entity, Serialize, Restrict, VirtualProperty, GetterPrefix } from './src';
265
+
266
+ class User extends Entity {
267
+ @Serialize((value: Email) => value.getValue())
268
+ private readonly email: Email;
269
+
270
+ @Restrict()
271
+ private readonly passwordHash: string;
272
+
273
+ @GetterPrefix('has')
274
+ private readonly profile?: Profile;
275
+
276
+ @VirtualProperty('displayName')
277
+ private display() {
278
+ return 'User ' + this.email.getValue();
279
+ }
280
+
281
+ constructor(email: string, passwordHash: string) {
282
+ super();
283
+ this.email = new Email(email);
284
+ this.passwordHash = passwordHash;
285
+ this.generateGetters();
286
+ }
287
+ }
288
+
289
+ const user = new User('a@b.com', 'hash');
290
+ const serialized = user.toEntity();
291
+ ```
292
+
293
+ Regras principais:
294
+
295
+ - `@Serialize` pode retornar objeto: cada chave vira uma propriedade do resultado
296
+ - `@Restrict` remove campo em `toData`
297
+ - `@VirtualProperty` mapeia metodos para campos virtuais
298
+ - `generateGetters()` cria getters para campos (ex.: `getName`, `isActive`, `hasProfile`)
299
+
300
+ ### Hooks Before/After ToEntity
301
+
302
+ Use `@BeforeToEntity()` e `@AfterToEntity()` em metodos de Entities.
303
+
304
+ - Before recebe objeto bruto (antes de serializacao)
305
+ - After recebe objeto tratado (resultado final)
306
+ - `toEntity()` executa hooks sem aguardar promessas
307
+ - `toAsyncEntity()` aguarda hooks async
308
+
309
+ ```ts
310
+ import { Entity, AfterToEntity, BeforeToEntity } from './src';
311
+
312
+ class FileEntity extends Entity {
313
+ private readonly name: string;
314
+ private readonly s3Key: string;
315
+
316
+ constructor(name: string, s3Key: string) {
317
+ super();
318
+ this.name = name;
319
+ this.s3Key = s3Key;
320
+ }
321
+
322
+ @BeforeToEntity()
323
+ private before(payload: Record<string, any>) {
324
+ payload.rawTouched = true;
325
+ }
326
+
327
+ @AfterToEntity()
328
+ private async after(payload: Record<string, any>) {
329
+ await saveToS3(payload.s3Key);
330
+ }
331
+ }
332
+ ```
333
+
334
+ ### Storage Gateway
335
+
336
+ Use `@Storage` para injetar gateways com interface `StorageGateway`.
337
+
338
+ ```ts
339
+ import { Storage, StorageGateway } from './src';
340
+
341
+ class S3Gateway implements StorageGateway {
342
+ async save(file: File, path: string) { /* ... */ }
343
+ async get(key: string) { return 'url'; }
344
+ async delete(key: string) { return true; }
345
+ }
346
+
347
+ class FileService {
348
+ @Storage('S3Gateway')
349
+ private readonly storage!: StorageGateway;
350
+ }
351
+ ```
352
+
353
+ ### DI (Container/Inject)
354
+
355
+ Registre dependencias ao iniciar o server (usando o container padrão):
356
+
357
+ ```ts
358
+ import { Dede } from './src';
359
+
360
+ class UserRepository { /* ... */ }
361
+
362
+ const app = await Dede.create({
363
+ framework: { use: 'express', port: 3000 },
364
+ registries: [
365
+ { name: 'UserRepository', classLoader: UserRepository }
366
+ ]
367
+ });
368
+ app.listen();
369
+ ```
370
+
371
+ Use `@Inject('Name')` para injetar dependencias:
372
+
373
+ ```ts
374
+ import { Inject, UseCase } from './src';
375
+
376
+ class ExampleUseCase extends UseCase<void, any> {
377
+ @Inject('UserRepository')
378
+ private readonly userRepository!: any;
379
+
380
+ async execute() {
381
+ return await this.userRepository.findById('1');
382
+ }
383
+ }
384
+ ```
385
+
386
+ ### Errors
387
+
388
+ Erros de dominio disponiveis:
389
+
390
+ - `BadRequest` (400)
391
+ - `Unauthorized` (401)
392
+ - `Forbidden` (403)
393
+ - `NotFound` (404)
394
+ - `Conflict` (409)
395
+ - `UnprocessableEntity` (422)
396
+ - `InternalServerError` (500)
397
+
398
+ Quando um erro e lancado, o handler padroniza a resposta. Erros de dominio (`AppError`) sao mapeados para HTTP. Se o erro for `CustomServerError`, o payload customizado sera retornado diretamente.
399
+
400
+ ### Protocolos de Repositorio
401
+
402
+ Interfaces tipadas para padrao de repositorio:
403
+
404
+ - `RepositoryCreate<T extends Entity>`
405
+ - `RepositoryUpdate<T extends Entity>`
406
+ - `RepositoryRemove`
407
+ - `RepositoryRestore<T extends Entity>`
408
+ - `RepositoryRemoveBy<T>`
409
+ - `RepositoryRestoreBy<T>`
410
+ - `RepositoryExistsBy<T>`
411
+ - `RepositoryNotExistsBy<T>`
412
+ - `RepositoryPagination<T>`
413
+
414
+ ## Exemplos
415
+
416
+ ### Express
417
+
418
+ ```ts
419
+ import { Dede } from './src/dede';
420
+ import { ExampleController } from './example/express_app/example.controller';
421
+
422
+ class UserRepository {
423
+ async findById(id: string) {
424
+ return { id, name: 'John Doe' };
425
+ }
426
+ }
427
+
428
+ const app = await Dede.create({
429
+ framework: { use: 'express', port: 3000 },
430
+ registries: [{ name: 'UserRepository', classLoader: UserRepository }]
431
+ });
432
+ app.registerControllers([ExampleController]);
433
+ app.listen();
434
+ ```
435
+
436
+ ### Elysia
437
+
438
+ ```ts
439
+ import { Dede } from './src/dede';
440
+ import { ExampleController } from './example/express_app/example.controller';
441
+
442
+ const app = await Dede.create({
443
+ framework: { use: 'elysia', port: 3001 },
444
+ registries: []
445
+ });
446
+ app.registerControllers([ExampleController]);
447
+ app.listen();
448
+ ```
449
+
450
+ ### Fila com AfterToEntity
451
+
452
+ ```ts
453
+ import { Entity, AfterToEntity } from './src';
454
+
455
+ type QueueJob = { type: string; payload: Record<string, any> };
456
+
457
+ type Queue = { enqueue(job: QueueJob): Promise<void> };
458
+
459
+ const queue: Queue = {
460
+ async enqueue(job) {
461
+ console.log('queued job', job);
462
+ }
463
+ };
464
+
465
+ class FileEntity extends Entity {
466
+ private readonly name: string;
467
+ private readonly s3Key: string;
468
+
469
+ private constructor({ name, s3Key }: { name: string; s3Key: string }) {
470
+ super();
471
+ this.name = name;
472
+ this.s3Key = s3Key;
473
+ }
474
+
475
+ @AfterToEntity()
476
+ private async enqueueFileSync(payload: Record<string, any>) {
477
+ await queue.enqueue({
478
+ type: 'files.create',
479
+ payload: {
480
+ name: payload.name,
481
+ s3Key: payload.s3Key
482
+ }
483
+ });
484
+ }
485
+
486
+ static create(input: { name: string; s3Key: string }) {
487
+ return new FileEntity(input);
488
+ }
489
+ }
490
+
491
+ const entity = FileEntity.create({ name: 'report', s3Key: 's3://bucket/report.pdf' });
492
+ const serialized = entity.toEntity();
493
+ ```
494
+
495
+ ## Testes
496
+
497
+ ```bash
498
+ npm test -- tests/src/application/entity.spec.ts --runInBand
499
+ ```
500
+
501
+ Testes de integração (exemplos):
10
502
 
11
503
  ```bash
12
- bun run index.ts
504
+ RUN_EXAMPLE_TESTS=true npm test -- example/tests/main.test.ts
13
505
  ```
14
506
 
15
- This project was created using `bun init` in bun v1.1.42. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
507
+ Obs: os testes de Elysia rodam no runtime do Bun (em Node eles são ignorados).
508
+
509
+ ## Benchmark
510
+
511
+ Resultados locais ficam em `bench/results.md`. Para rodar:
512
+
513
+ ```bash
514
+ npm run bench:compare
515
+ ```
516
+
517
+ Parâmetros (opcional):
518
+
519
+ ```bash
520
+ BENCH_REQUESTS=5000 BENCH_CONCURRENCY=50 BENCH_WARMUP=200 npm run bench:compare
521
+ ```
522
+
523
+ Resumo (média de 3 rodadas locais, 5000 req / conc 50 / warmup 200):
524
+
525
+ - Express: avg 3.34 ms, p50 2.45 ms, p95 8.54 ms, 8298.61 req/s
526
+ - Elysia: avg 3.16 ms, p50 2.63 ms, p95 6.98 ms, 8765.78 req/s
@@ -1,4 +1,5 @@
1
1
  import 'reflect-metadata';
2
+ import type { ValidatorLike } from "../interface/validation/validator";
2
3
  export interface Middleware {
3
4
  execute(input: Input<any>): Promise<any>;
4
5
  }
@@ -28,8 +29,6 @@ export interface Input<T, K = any> {
28
29
  type BodyFilter = "restrict" | "none";
29
30
  export declare function Controller(basePath?: string): (target: any) => void;
30
31
  export declare function Tracing<R>(tracer: Tracer<R>): (target: any, propertyKey?: string) => void;
31
- export declare function getControllers(): any[];
32
- export declare function flushControllers(): void;
33
32
  export declare function UseMiddleware(middlewareClass: MiddlewareDefinition): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
34
33
  export declare function UseMiddlewares(middlewareClasses: MiddlewareDefinition[]): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
35
34
  export declare function Post(config?: {
@@ -41,6 +40,7 @@ export declare function Post(config?: {
41
40
  body?: string[];
42
41
  bodyFilter?: BodyFilter;
43
42
  responseType?: 'json' | 'text' | 'html';
43
+ validator?: ValidatorLike;
44
44
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
45
45
  export declare function Get(config?: {
46
46
  path?: string;
@@ -49,6 +49,7 @@ export declare function Get(config?: {
49
49
  query?: string[];
50
50
  headers?: string[];
51
51
  responseType?: 'json' | 'text' | 'html';
52
+ validator?: ValidatorLike;
52
53
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
53
54
  export declare function Put(config?: {
54
55
  path?: string;
@@ -59,6 +60,7 @@ export declare function Put(config?: {
59
60
  body?: string[];
60
61
  bodyFilter?: BodyFilter;
61
62
  responseType?: 'json' | 'text' | 'html';
63
+ validator?: ValidatorLike;
62
64
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
63
65
  export declare function Patch(config?: {
64
66
  path?: string;
@@ -69,6 +71,7 @@ export declare function Patch(config?: {
69
71
  body?: string[];
70
72
  bodyFilter?: BodyFilter;
71
73
  responseType?: 'json' | 'text' | 'html';
74
+ validator?: ValidatorLike;
72
75
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
73
76
  export declare function Delete(config?: {
74
77
  path?: string;
@@ -79,5 +82,6 @@ export declare function Delete(config?: {
79
82
  body?: string[];
80
83
  bodyFilter?: BodyFilter;
81
84
  responseType?: 'json' | 'text' | 'html';
85
+ validator?: ValidatorLike;
82
86
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
83
87
  export {};
@@ -1,14 +1,10 @@
1
1
  import 'reflect-metadata';
2
2
  import { FrameworkError } from "../http/errors/framework";
3
- import { Registry } from "../infra/di/registry";
4
- let controllers = [];
5
3
  export function Controller(basePath = '/') {
6
4
  return function (target) {
7
5
  if (!basePath)
8
6
  throw new FrameworkError('basePath cannot be empty');
9
7
  Reflect.defineMetadata('basePath', basePath, target);
10
- controllers.push(target.name);
11
- Registry.load(target.name, target);
12
8
  };
13
9
  }
14
10
  export function Tracing(tracer) {
@@ -21,15 +17,6 @@ export function Tracing(tracer) {
21
17
  }
22
18
  };
23
19
  }
24
- export function getControllers() {
25
- return controllers.map((controller) => Registry.inject(controller));
26
- }
27
- export function flushControllers() {
28
- controllers.map((controller) => {
29
- Registry.remove(controller);
30
- });
31
- controllers = [];
32
- }
33
20
  function isClass(fn) {
34
21
  return /^\s*class\s/.test(Function.prototype.toString.call(fn));
35
22
  }
@@ -78,7 +65,8 @@ export function Post(config = {}) {
78
65
  body: config.body,
79
66
  bodyFilter: config.bodyFilter || 'none',
80
67
  statusCode: config.statusCode || 200,
81
- responseType: config.responseType || 'json'
68
+ responseType: config.responseType || 'json',
69
+ validator: config.validator
82
70
  }, target, propertyKey);
83
71
  };
84
72
  }
@@ -91,7 +79,8 @@ export function Get(config = {}) {
91
79
  query: config.query,
92
80
  headers: config.headers,
93
81
  statusCode: config.statusCode || 200,
94
- responseType: config.responseType || 'json'
82
+ responseType: config.responseType || 'json',
83
+ validator: config.validator
95
84
  }, target, propertyKey);
96
85
  };
97
86
  }
@@ -106,7 +95,8 @@ export function Put(config = {}) {
106
95
  body: config.body,
107
96
  bodyFilter: config.bodyFilter || 'none',
108
97
  statusCode: config.statusCode || 200,
109
- responseType: config.responseType || 'json'
98
+ responseType: config.responseType || 'json',
99
+ validator: config.validator
110
100
  }, target, propertyKey);
111
101
  };
112
102
  }
@@ -121,7 +111,8 @@ export function Patch(config = {}) {
121
111
  body: config.body,
122
112
  bodyFilter: config.bodyFilter || 'none',
123
113
  statusCode: config.statusCode || 200,
124
- responseType: config.responseType || 'json'
114
+ responseType: config.responseType || 'json',
115
+ validator: config.validator
125
116
  }, target, propertyKey);
126
117
  };
127
118
  }
@@ -136,7 +127,8 @@ export function Delete(config = {}) {
136
127
  body: config.body,
137
128
  bodyFilter: config.bodyFilter || 'none',
138
129
  statusCode: config.statusCode || 200,
139
- responseType: config.responseType || 'json'
130
+ responseType: config.responseType || 'json',
131
+ validator: config.validator
140
132
  }, target, propertyKey);
141
133
  };
142
134
  }
@@ -1,6 +1,6 @@
1
1
  import { Controller, Post, Get, Put, Delete, Patch, UseMiddleware, UseMiddlewares, Tracing, type Middleware, type Input, type Tracer, type TracerData } from './controller';
2
- import { Entity, Restrict, VirtualProperty, Serialize, GetterPrefix } from './entity';
2
+ import { Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, BeforeToEntity, AfterToEntity } from '../infra/serialization/entity';
3
3
  import { DecorateUseCase, UseCase } from './usecase';
4
4
  import { Storage, type StorageGateway } from './services';
5
- export { Controller, UseMiddleware, UseMiddlewares, Post, Get, Put, Delete, Patch, Tracing, DecorateUseCase, UseCase, Storage, Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, };
5
+ export { Controller, UseMiddleware, UseMiddlewares, Post, Get, Put, Delete, Patch, Tracing, DecorateUseCase, UseCase, Storage, Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, BeforeToEntity, AfterToEntity, };
6
6
  export type { Middleware, Input, StorageGateway, Tracer, TracerData };
@@ -1,5 +1,5 @@
1
1
  import { Controller, Post, Get, Put, Delete, Patch, UseMiddleware, UseMiddlewares, Tracing } from './controller';
2
- import { Entity, Restrict, VirtualProperty, Serialize, GetterPrefix } from './entity';
2
+ import { Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, BeforeToEntity, AfterToEntity } from '../infra/serialization/entity';
3
3
  import { DecorateUseCase, UseCase } from './usecase';
4
4
  import { Storage } from './services';
5
- export { Controller, UseMiddleware, UseMiddlewares, Post, Get, Put, Delete, Patch, Tracing, DecorateUseCase, UseCase, Storage, Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, };
5
+ export { Controller, UseMiddleware, UseMiddlewares, Post, Get, Put, Delete, Patch, Tracing, DecorateUseCase, UseCase, Storage, Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, BeforeToEntity, AfterToEntity, };
@@ -1,7 +1,8 @@
1
+ import { Container } from "../infra/di/registry";
1
2
  import 'reflect-metadata';
2
3
  export interface StorageGateway {
3
4
  save(file: File, path: string): Promise<void>;
4
5
  get(key: string): Promise<string>;
5
6
  delete(key: string): Promise<boolean>;
6
7
  }
7
- export declare function Storage(gatewayName: string): (target: any, propertyKey: string) => void;
8
+ export declare function Storage(gatewayName: string, container?: Container): (target: any, propertyKey: string) => void;
@@ -1,12 +1,12 @@
1
- import { Registry } from "../infra/di/registry";
1
+ import { DefaultContainer } from "../infra/di/registry";
2
2
  import 'reflect-metadata';
3
- export function Storage(gatewayName) {
3
+ export function Storage(gatewayName, container = DefaultContainer) {
4
4
  return function (target, propertyKey) {
5
5
  let dependency;
6
6
  Object.defineProperty(target, propertyKey, {
7
7
  get: function () {
8
8
  if (!dependency) {
9
- dependency = Registry.inject(gatewayName);
9
+ dependency = container.inject(gatewayName);
10
10
  }
11
11
  if (!dependency.save || !dependency.get || !dependency.delete) {
12
12
  throw new Error(`${gatewayName} is not a valid StorageGateway`);