framework-do-dede 3.4.0 → 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.
- package/README.md +77 -14
- package/dist/application/controller.d.ts +6 -2
- package/dist/application/controller.js +10 -18
- package/dist/application/index.d.ts +1 -1
- package/dist/application/index.js +1 -1
- package/dist/application/services.d.ts +2 -1
- package/dist/application/services.js +3 -3
- package/dist/dede.d.ts +10 -1
- package/dist/dede.js +30 -9
- package/dist/domain/entity.d.ts +4 -0
- package/dist/domain/entity.js +25 -0
- package/dist/domain/errors/app-error.d.ts +12 -0
- package/dist/domain/errors/app-error.js +14 -0
- package/dist/domain/errors/http-errors.d.ts +42 -0
- package/dist/domain/errors/http-errors.js +40 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.js +2 -0
- package/dist/http/controller.handler.d.ts +4 -5
- package/dist/http/controller.handler.js +27 -119
- package/dist/http/errors/server.d.ts +2 -28
- package/dist/http/errors/server.js +2 -49
- package/dist/http/http-server.d.ts +2 -0
- package/dist/http/http-server.js +1 -1
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.js +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/infra/di/registry.d.ts +4 -6
- package/dist/infra/di/registry.js +7 -10
- package/dist/{application → infra/serialization}/entity.d.ts +3 -1
- package/dist/{application → infra/serialization}/entity.js +7 -25
- package/dist/interface/errors/http-error-mapper.d.ts +10 -0
- package/dist/interface/errors/http-error-mapper.js +31 -0
- package/dist/interface/http/middleware-executor.d.ts +10 -0
- package/dist/interface/http/middleware-executor.js +33 -0
- package/dist/interface/http/request-mapper.d.ts +21 -0
- package/dist/interface/http/request-mapper.js +55 -0
- package/dist/interface/validation/class-validator.d.ts +6 -0
- package/dist/interface/validation/class-validator.js +28 -0
- package/dist/interface/validation/validator.d.ts +5 -0
- package/dist/interface/validation/validator.js +1 -0
- package/dist/protocols/repository.d.ts +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Um framework TypeScript simples para construir APIs HTTP com controllers, use ca
|
|
|
15
15
|
- Entity e Serialização
|
|
16
16
|
- Hooks Before/After ToEntity
|
|
17
17
|
- Storage Gateway
|
|
18
|
-
- DI (
|
|
18
|
+
- DI (Container/Inject)
|
|
19
19
|
- Errors
|
|
20
20
|
- Protocolos de Repositório
|
|
21
21
|
- Exemplos
|
|
@@ -23,6 +23,7 @@ Um framework TypeScript simples para construir APIs HTTP com controllers, use ca
|
|
|
23
23
|
- Elysia
|
|
24
24
|
- Fila com AfterToEntity
|
|
25
25
|
- Testes
|
|
26
|
+
- Benchmark
|
|
26
27
|
|
|
27
28
|
## Instalação
|
|
28
29
|
|
|
@@ -62,17 +63,19 @@ class HelloUseCase extends UseCase<{ name?: string }, { message: string }> {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
await Dede.
|
|
66
|
+
const app = await Dede.create({
|
|
66
67
|
framework: { use: 'express', port: 3000 },
|
|
67
68
|
registries: []
|
|
68
69
|
});
|
|
70
|
+
app.registerControllers([HelloController]);
|
|
71
|
+
app.listen();
|
|
69
72
|
```
|
|
70
73
|
|
|
71
74
|
## Conceitos
|
|
72
75
|
|
|
73
76
|
### Controllers e Rotas
|
|
74
77
|
|
|
75
|
-
Use decorators para expor métodos como rotas HTTP. O Controller
|
|
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(...)`.
|
|
76
79
|
|
|
77
80
|
```ts
|
|
78
81
|
import { Controller, Get, Post, Put, Delete, Patch } from './src';
|
|
@@ -105,6 +108,7 @@ Opções de rota (comuns):
|
|
|
105
108
|
- `params`, `query`, `headers`, `body`: array de strings no formato `campo|tipo`
|
|
106
109
|
- `bodyFilter`: `"restrict" | "none"`
|
|
107
110
|
- `responseType`: `"json" | "text" | "html"`
|
|
111
|
+
- `validator`: pode ser uma classe com decorators do `class-validator` **ou** um objeto com `validate(data)` (sync/async)
|
|
108
112
|
|
|
109
113
|
### Input, params e filtros
|
|
110
114
|
|
|
@@ -121,7 +125,7 @@ Tipos suportados no filtro:
|
|
|
121
125
|
|
|
122
126
|
- `boolean`, `integer`, `string`, `number`
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
Exemplos:
|
|
125
129
|
|
|
126
130
|
```ts
|
|
127
131
|
@Put({
|
|
@@ -129,13 +133,38 @@ Exemplo:
|
|
|
129
133
|
query: ['active|boolean'],
|
|
130
134
|
headers: ['x-type|string'],
|
|
131
135
|
body: ['name|string'],
|
|
132
|
-
bodyFilter: 'restrict'
|
|
136
|
+
bodyFilter: 'restrict',
|
|
137
|
+
validator: CreateUserDto
|
|
133
138
|
})
|
|
134
139
|
async update(request: { data: any }) {
|
|
135
140
|
// request.data: { id, active, 'x-type', name }
|
|
136
141
|
}
|
|
137
142
|
```
|
|
138
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
|
+
|
|
139
168
|
Suporte a notacao com colchetes:
|
|
140
169
|
|
|
141
170
|
```json
|
|
@@ -229,6 +258,8 @@ Entities suportam:
|
|
|
229
258
|
- `toData()` e `toAsyncData()`
|
|
230
259
|
- `@Serialize`, `@Restrict`, `@VirtualProperty`, `@GetterPrefix`
|
|
231
260
|
|
|
261
|
+
Obs: a serializacao fica na camada de infraestrutura, mas a API continua sendo exposta pelo framework (importe direto de `./src`).
|
|
262
|
+
|
|
232
263
|
```ts
|
|
233
264
|
import { Entity, Serialize, Restrict, VirtualProperty, GetterPrefix } from './src';
|
|
234
265
|
|
|
@@ -319,21 +350,22 @@ class FileService {
|
|
|
319
350
|
}
|
|
320
351
|
```
|
|
321
352
|
|
|
322
|
-
### DI (
|
|
353
|
+
### DI (Container/Inject)
|
|
323
354
|
|
|
324
|
-
Registre dependencias ao iniciar o server:
|
|
355
|
+
Registre dependencias ao iniciar o server (usando o container padrão):
|
|
325
356
|
|
|
326
357
|
```ts
|
|
327
358
|
import { Dede } from './src';
|
|
328
359
|
|
|
329
360
|
class UserRepository { /* ... */ }
|
|
330
361
|
|
|
331
|
-
await Dede.
|
|
362
|
+
const app = await Dede.create({
|
|
332
363
|
framework: { use: 'express', port: 3000 },
|
|
333
364
|
registries: [
|
|
334
365
|
{ name: 'UserRepository', classLoader: UserRepository }
|
|
335
366
|
]
|
|
336
367
|
});
|
|
368
|
+
app.listen();
|
|
337
369
|
```
|
|
338
370
|
|
|
339
371
|
Use `@Inject('Name')` para injetar dependencias:
|
|
@@ -363,7 +395,7 @@ Erros de dominio disponiveis:
|
|
|
363
395
|
- `UnprocessableEntity` (422)
|
|
364
396
|
- `InternalServerError` (500)
|
|
365
397
|
|
|
366
|
-
Quando um erro e lancado, o handler padroniza a resposta. Se o erro for `CustomServerError`, o payload customizado sera retornado diretamente.
|
|
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.
|
|
367
399
|
|
|
368
400
|
### Protocolos de Repositorio
|
|
369
401
|
|
|
@@ -385,7 +417,7 @@ Interfaces tipadas para padrao de repositorio:
|
|
|
385
417
|
|
|
386
418
|
```ts
|
|
387
419
|
import { Dede } from './src/dede';
|
|
388
|
-
import './example/express_app/example.controller';
|
|
420
|
+
import { ExampleController } from './example/express_app/example.controller';
|
|
389
421
|
|
|
390
422
|
class UserRepository {
|
|
391
423
|
async findById(id: string) {
|
|
@@ -393,28 +425,32 @@ class UserRepository {
|
|
|
393
425
|
}
|
|
394
426
|
}
|
|
395
427
|
|
|
396
|
-
await Dede.
|
|
428
|
+
const app = await Dede.create({
|
|
397
429
|
framework: { use: 'express', port: 3000 },
|
|
398
430
|
registries: [{ name: 'UserRepository', classLoader: UserRepository }]
|
|
399
431
|
});
|
|
432
|
+
app.registerControllers([ExampleController]);
|
|
433
|
+
app.listen();
|
|
400
434
|
```
|
|
401
435
|
|
|
402
436
|
### Elysia
|
|
403
437
|
|
|
404
438
|
```ts
|
|
405
439
|
import { Dede } from './src/dede';
|
|
406
|
-
import './example/express_app/example.controller';
|
|
440
|
+
import { ExampleController } from './example/express_app/example.controller';
|
|
407
441
|
|
|
408
|
-
await Dede.
|
|
442
|
+
const app = await Dede.create({
|
|
409
443
|
framework: { use: 'elysia', port: 3001 },
|
|
410
444
|
registries: []
|
|
411
445
|
});
|
|
446
|
+
app.registerControllers([ExampleController]);
|
|
447
|
+
app.listen();
|
|
412
448
|
```
|
|
413
449
|
|
|
414
450
|
### Fila com AfterToEntity
|
|
415
451
|
|
|
416
452
|
```ts
|
|
417
|
-
import { Entity, AfterToEntity } from './src
|
|
453
|
+
import { Entity, AfterToEntity } from './src';
|
|
418
454
|
|
|
419
455
|
type QueueJob = { type: string; payload: Record<string, any> };
|
|
420
456
|
|
|
@@ -461,3 +497,30 @@ const serialized = entity.toEntity();
|
|
|
461
497
|
```bash
|
|
462
498
|
npm test -- tests/src/application/entity.spec.ts --runInBand
|
|
463
499
|
```
|
|
500
|
+
|
|
501
|
+
Testes de integração (exemplos):
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
RUN_EXAMPLE_TESTS=true npm test -- example/tests/main.test.ts
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Obs: os testes de Elysia só 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,5 +1,5 @@
|
|
|
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, BeforeToEntity, AfterToEntity } from '
|
|
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
5
|
export { Controller, UseMiddleware, UseMiddlewares, Post, Get, Put, Delete, Patch, Tracing, DecorateUseCase, UseCase, Storage, Entity, Restrict, VirtualProperty, Serialize, GetterPrefix, BeforeToEntity, AfterToEntity, };
|
|
@@ -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, BeforeToEntity, AfterToEntity } from '
|
|
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
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 {
|
|
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 =
|
|
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`);
|
package/dist/dede.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Container } from "./infra/di/registry";
|
|
1
2
|
export type Register = {
|
|
2
3
|
name: string;
|
|
3
4
|
classLoader: any;
|
|
@@ -8,15 +9,23 @@ export type Options = {
|
|
|
8
9
|
port?: number;
|
|
9
10
|
middlewares?: CallableFunction[];
|
|
10
11
|
};
|
|
12
|
+
controllers?: any[];
|
|
11
13
|
registries: Register[];
|
|
12
14
|
defaultServerError?: string;
|
|
15
|
+
container?: Container;
|
|
13
16
|
};
|
|
14
17
|
export declare class Dede {
|
|
15
18
|
private readonly framework;
|
|
16
19
|
private readonly defaultServerError?;
|
|
20
|
+
private readonly container;
|
|
17
21
|
private readonly httpServer;
|
|
22
|
+
private readonly port?;
|
|
23
|
+
private controllersRegistered;
|
|
18
24
|
private constructor();
|
|
19
|
-
static
|
|
25
|
+
static create({ framework, registries, defaultServerError, container }: Options): Promise<Dede>;
|
|
26
|
+
static start({ framework, registries, defaultServerError, container, controllers }: Options): Promise<Dede>;
|
|
20
27
|
stop(): Promise<void>;
|
|
28
|
+
registerControllers(controllers: any[]): void;
|
|
29
|
+
listen(port?: number): void;
|
|
21
30
|
private static loadRegistries;
|
|
22
31
|
}
|
package/dist/dede.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import ControllerHandler from "./http/controller.handler";
|
|
2
2
|
import { ElysiaServerAdapter } from "./http/elysia-server.adapter";
|
|
3
3
|
import { ExpressServerAdapter } from "./http/express-server.adapter";
|
|
4
|
-
import {
|
|
4
|
+
import { Container, DefaultContainer, setDefaultContainer } from "./infra/di/registry";
|
|
5
5
|
export class Dede {
|
|
6
|
-
constructor(framework, defaultServerError) {
|
|
6
|
+
constructor(framework, defaultServerError, container = DefaultContainer) {
|
|
7
7
|
this.framework = framework;
|
|
8
8
|
this.defaultServerError = defaultServerError;
|
|
9
|
+
this.container = container;
|
|
10
|
+
this.controllersRegistered = false;
|
|
11
|
+
this.port = framework.port;
|
|
9
12
|
if (framework.use === 'elysia') {
|
|
10
13
|
this.httpServer = new ElysiaServerAdapter(framework.middlewares || []);
|
|
11
14
|
}
|
|
@@ -14,19 +17,37 @@ export class Dede {
|
|
|
14
17
|
}
|
|
15
18
|
if (defaultServerError)
|
|
16
19
|
this.httpServer.setDefaultMessageError(defaultServerError);
|
|
17
|
-
new ControllerHandler(this.httpServer, framework.port || 80);
|
|
18
20
|
}
|
|
19
|
-
static async
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
static async create({ framework, registries, defaultServerError, container }) {
|
|
22
|
+
const appContainer = container ?? new Container();
|
|
23
|
+
setDefaultContainer(appContainer);
|
|
24
|
+
await this.loadRegistries(appContainer, registries);
|
|
25
|
+
return new Dede(framework, defaultServerError, appContainer);
|
|
26
|
+
}
|
|
27
|
+
static async start({ framework, registries, defaultServerError, container, controllers }) {
|
|
28
|
+
const app = await Dede.create({ framework, registries, defaultServerError, container, controllers });
|
|
29
|
+
if (controllers && controllers.length > 0) {
|
|
30
|
+
app.registerControllers(controllers);
|
|
31
|
+
}
|
|
32
|
+
app.listen();
|
|
33
|
+
return app;
|
|
22
34
|
}
|
|
23
35
|
async stop() {
|
|
24
36
|
await this.httpServer.close();
|
|
25
37
|
}
|
|
26
|
-
|
|
38
|
+
registerControllers(controllers) {
|
|
39
|
+
if (this.controllersRegistered)
|
|
40
|
+
return;
|
|
41
|
+
new ControllerHandler(this.httpServer, controllers);
|
|
42
|
+
this.controllersRegistered = true;
|
|
43
|
+
}
|
|
44
|
+
listen(port) {
|
|
45
|
+
const resolvedPort = port ?? this.port ?? 80;
|
|
46
|
+
this.httpServer.listen(resolvedPort);
|
|
47
|
+
}
|
|
48
|
+
static async loadRegistries(container, registries) {
|
|
27
49
|
registries.forEach(({ classLoader, name }) => {
|
|
28
|
-
|
|
50
|
+
container.load(name, classLoader);
|
|
29
51
|
});
|
|
30
|
-
return new Promise(resolve => setTimeout(resolve, 500));
|
|
31
52
|
}
|
|
32
53
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class Entity {
|
|
2
|
+
generateGetters() {
|
|
3
|
+
for (const property of Object.keys(this)) {
|
|
4
|
+
if (typeof this[property] === 'function')
|
|
5
|
+
continue;
|
|
6
|
+
let prefixName = null;
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
if (this.constructor.propertiesConfigs && this.constructor.propertiesConfigs[property] && this.constructor.propertiesConfigs[property].prefix) {
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
prefixName = this.constructor.propertiesConfigs[property].prefix;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const isBoolean = this[property] ? typeof this[property] === 'boolean' : false;
|
|
14
|
+
prefixName = isBoolean ? 'is' : 'get';
|
|
15
|
+
}
|
|
16
|
+
let getterName = null;
|
|
17
|
+
if (property[0]) {
|
|
18
|
+
getterName = `${prefixName}${property[0].toUpperCase()}${property.slice(1)}`;
|
|
19
|
+
if (this[getterName])
|
|
20
|
+
continue;
|
|
21
|
+
this[getterName] = () => this[property];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type AppErrorData = {
|
|
2
|
+
message: string;
|
|
3
|
+
code?: string;
|
|
4
|
+
details?: Record<string, any>;
|
|
5
|
+
};
|
|
6
|
+
export declare abstract class AppError extends Error {
|
|
7
|
+
private readonly statusCode;
|
|
8
|
+
private readonly data?;
|
|
9
|
+
constructor(message: string, statusCode: number, data?: AppErrorData);
|
|
10
|
+
getStatusCode(): number;
|
|
11
|
+
getData(): AppErrorData | undefined;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
constructor(message, statusCode, data) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = this.constructor.name;
|
|
5
|
+
this.statusCode = statusCode;
|
|
6
|
+
this.data = data;
|
|
7
|
+
}
|
|
8
|
+
getStatusCode() {
|
|
9
|
+
return this.statusCode;
|
|
10
|
+
}
|
|
11
|
+
getData() {
|
|
12
|
+
return this.data;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AppError } from './app-error';
|
|
2
|
+
export declare class BadRequest extends AppError {
|
|
3
|
+
constructor(message: string, data?: {
|
|
4
|
+
code?: string;
|
|
5
|
+
details?: Record<string, any>;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
export declare class Unauthorized extends AppError {
|
|
9
|
+
constructor(message: string, data?: {
|
|
10
|
+
code?: string;
|
|
11
|
+
details?: Record<string, any>;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export declare class Forbidden extends AppError {
|
|
15
|
+
constructor(message: string, data?: {
|
|
16
|
+
code?: string;
|
|
17
|
+
details?: Record<string, any>;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export declare class NotFound extends AppError {
|
|
21
|
+
constructor(message: string, data?: {
|
|
22
|
+
code?: string;
|
|
23
|
+
details?: Record<string, any>;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export declare class Conflict extends AppError {
|
|
27
|
+
constructor(message: string, data?: {
|
|
28
|
+
code?: string;
|
|
29
|
+
details?: Record<string, any>;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export declare class UnprocessableEntity extends AppError {
|
|
33
|
+
constructor(message: string, data?: {
|
|
34
|
+
code?: string;
|
|
35
|
+
details?: Record<string, any>;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export declare class InternalServerError extends AppError {
|
|
39
|
+
private readonly unexpectedError;
|
|
40
|
+
constructor(unexpectedError: string, defaultMessage?: string);
|
|
41
|
+
getUnexpectedError(): string;
|
|
42
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AppError } from './app-error';
|
|
2
|
+
export class BadRequest extends AppError {
|
|
3
|
+
constructor(message, data) {
|
|
4
|
+
super(message, 400, data ? { message, ...data } : undefined);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class Unauthorized extends AppError {
|
|
8
|
+
constructor(message, data) {
|
|
9
|
+
super(message, 401, data ? { message, ...data } : undefined);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class Forbidden extends AppError {
|
|
13
|
+
constructor(message, data) {
|
|
14
|
+
super(message, 403, data ? { message, ...data } : undefined);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class NotFound extends AppError {
|
|
18
|
+
constructor(message, data) {
|
|
19
|
+
super(message, 404, data ? { message, ...data } : undefined);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class Conflict extends AppError {
|
|
23
|
+
constructor(message, data) {
|
|
24
|
+
super(message, 409, data ? { message, ...data } : undefined);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class UnprocessableEntity extends AppError {
|
|
28
|
+
constructor(message, data) {
|
|
29
|
+
super(message, 422, data ? { message, ...data } : undefined);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class InternalServerError extends AppError {
|
|
33
|
+
constructor(unexpectedError, defaultMessage = 'Ops, An unexpected error occurred') {
|
|
34
|
+
super(defaultMessage, 500, { message: defaultMessage });
|
|
35
|
+
this.unexpectedError = unexpectedError;
|
|
36
|
+
}
|
|
37
|
+
getUnexpectedError() {
|
|
38
|
+
return this.unexpectedError;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import HttpServer from "../http/http-server";
|
|
2
2
|
export default class ControllerHandler {
|
|
3
|
-
|
|
4
|
-
private
|
|
3
|
+
private readonly requestMapper;
|
|
4
|
+
private readonly middlewareExecutor;
|
|
5
|
+
private readonly errorMapper;
|
|
6
|
+
constructor(httpServer: HttpServer, controllers?: any[]);
|
|
5
7
|
private registryControllers;
|
|
6
8
|
private resolveMiddleware;
|
|
7
|
-
private filter;
|
|
8
|
-
private extractError;
|
|
9
|
-
private normalizeBracketNotation;
|
|
10
9
|
}
|