framework-do-dede 6.1.0 → 6.2.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 CHANGED
@@ -111,9 +111,26 @@ Opções de rota (comuns):
111
111
  - `statusCode`: number
112
112
  - `params`, `query`, `headers`, `body`: array de strings no formato `campo|tipo`
113
113
  - `bodyFilter`: `"restrict" | "none"`
114
- - `responseType`: `"json" | "text" | "html"`
114
+ - `responseType`: `"json" | "text" | "html" | "<mime>/<subtype>"`
115
+ - `useHeaders`: objeto com headers de resposta (`Record<string, string>`)
115
116
  - `validator`: pode ser uma classe com decorators do `class-validator` **ou** um objeto com `validate(data)` (sync/async)
116
117
 
118
+ Exemplo de resposta binária com headers customizados:
119
+
120
+ ```ts
121
+ @Get({
122
+ path: '/download',
123
+ responseType: 'application/octet-stream',
124
+ useHeaders: {
125
+ 'Content-Disposition': 'attachment; filename="report.bin"',
126
+ 'Cache-Control': 'public, max-age=31536000'
127
+ }
128
+ })
129
+ async download() {
130
+ return Buffer.from([0x01, 0x02, 0x03]);
131
+ }
132
+ ```
133
+
117
134
  Nota sobre `validator`:
118
135
  - Apenas propriedades com decorators do `class-validator` são transformadas pelo `class-transformer`.
119
136
  - Propriedades sem decorators têm o valor original preservado.
@@ -253,6 +270,8 @@ class SecureController {
253
270
 
254
271
  Use `@Tracing` no controller ou em um metodo para capturar metadados de request.
255
272
 
273
+ Modo explicito (legado, continua suportado):
274
+
256
275
  ```ts
257
276
  import { Tracing, Tracer, TracerData } from './src';
258
277
 
@@ -270,6 +289,34 @@ class TraceController {
270
289
  }
271
290
  ```
272
291
 
292
+ Modo via container:
293
+
294
+ ```ts
295
+ import { Tracing } from './src';
296
+
297
+ @Controller('/trace')
298
+ class TraceController {
299
+ @Tracing()
300
+ @Get()
301
+ async get() { return { ok: true }; }
302
+ }
303
+ ```
304
+
305
+ Quando usar `@Tracing()`, o framework tenta resolver `Tracer` no container (`registries`).
306
+ Se nao encontrar, lança exceção: `Tracer not found in container: Tracer`.
307
+
308
+ Tracing global sem decorator:
309
+
310
+ ```ts
311
+ const app = await Dede.create({
312
+ framework: { use: 'express', port: 3000, tracer: true },
313
+ registries: [{ name: 'Tracer', classLoader: new ConsoleTracer() }]
314
+ });
315
+ ```
316
+
317
+ Com `framework.tracer: true`, todos os controllers/metodos passam a usar tracer por padrao.
318
+ `@Tracing(new MeuTracer())` ainda sobrescreve por controller/metodo.
319
+
273
320
  ### UseCase e Decorators
274
321
 
275
322
  UseCase provê `data` e `context` do request.
@@ -464,13 +511,20 @@ class QueueService {
464
511
  Registre dependencias ao iniciar o server (usando o container padrão):
465
512
 
466
513
  ```ts
467
- import { Dede } from './src';
514
+ import { Dede, Tracer, TracerData } from './src';
515
+
516
+ class ConsoleTracer implements Tracer<void> {
517
+ trace(data: TracerData) {
518
+ console.log(data);
519
+ }
520
+ }
468
521
 
469
522
  class UserRepository { /* ... */ }
470
523
 
471
524
  const app = await Dede.create({
472
- framework: { use: 'express', port: 3000 },
525
+ framework: { use: 'express', port: 3000, tracer: true },
473
526
  registries: [
527
+ { name: 'Tracer', classLoader: new ConsoleTracer() },
474
528
  { name: 'UserRepository', classLoader: UserRepository }
475
529
  ]
476
530
  });
@@ -1,4 +1,5 @@
1
1
  import 'reflect-metadata';
2
+ import type { HttpResponseHeaders, HttpResponseType } from "../http/http-server";
2
3
  import type { ValidatorDefinition } from "../interface/validation/validator";
3
4
  export interface Middleware {
4
5
  execute(input: Input<any>): Promise<any>;
@@ -22,13 +23,18 @@ export interface TracerData {
22
23
  export interface Tracer<R> {
23
24
  trace(data: TracerData): R;
24
25
  }
26
+ export type TracerFromContainer = {
27
+ fromContainer: true;
28
+ token: string;
29
+ };
30
+ export declare const DEFAULT_TRACER_TOKEN = "Tracer";
25
31
  export interface Input<T, K = any> {
26
32
  data: T;
27
33
  context?: K;
28
34
  }
29
35
  type BodyFilter = "restrict" | "none";
30
36
  export declare function Controller(basePath?: string): (target: any) => void;
31
- export declare function Tracing<R>(tracer: Tracer<R>): (target: any, propertyKey?: string) => void;
37
+ export declare function Tracing<R>(tracer?: Tracer<R>): (target: any, propertyKey?: string) => void;
32
38
  export declare function Version(version: number): (target: any, propertyKey?: string) => void;
33
39
  export declare function PresetIgnore(ignorePrefix?: boolean, ignoreVersion?: boolean): (target: any, propertyKey?: string) => void;
34
40
  export declare function UseMiddleware(middlewareClass: MiddlewareDefinition): (target: any, propertyKey?: string, descriptor?: PropertyDescriptor) => void;
@@ -39,9 +45,10 @@ export declare function Post(config?: {
39
45
  params?: string[];
40
46
  query?: string[];
41
47
  headers?: string[];
48
+ useHeaders?: HttpResponseHeaders;
42
49
  body?: string[];
43
50
  bodyFilter?: BodyFilter;
44
- responseType?: 'json' | 'text' | 'html';
51
+ responseType?: HttpResponseType;
45
52
  validator?: ValidatorDefinition;
46
53
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
47
54
  export declare function Get(config?: {
@@ -50,7 +57,8 @@ export declare function Get(config?: {
50
57
  params?: string[];
51
58
  query?: string[];
52
59
  headers?: string[];
53
- responseType?: 'json' | 'text' | 'html';
60
+ useHeaders?: HttpResponseHeaders;
61
+ responseType?: HttpResponseType;
54
62
  validator?: ValidatorDefinition;
55
63
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
56
64
  export declare function Put(config?: {
@@ -59,9 +67,10 @@ export declare function Put(config?: {
59
67
  params?: string[];
60
68
  query?: string[];
61
69
  headers?: string[];
70
+ useHeaders?: HttpResponseHeaders;
62
71
  body?: string[];
63
72
  bodyFilter?: BodyFilter;
64
- responseType?: 'json' | 'text' | 'html';
73
+ responseType?: HttpResponseType;
65
74
  validator?: ValidatorDefinition;
66
75
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
67
76
  export declare function Patch(config?: {
@@ -70,9 +79,10 @@ export declare function Patch(config?: {
70
79
  params?: string[];
71
80
  query?: string[];
72
81
  headers?: string[];
82
+ useHeaders?: HttpResponseHeaders;
73
83
  body?: string[];
74
84
  bodyFilter?: BodyFilter;
75
- responseType?: 'json' | 'text' | 'html';
85
+ responseType?: HttpResponseType;
76
86
  validator?: ValidatorDefinition;
77
87
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
78
88
  export declare function Delete(config?: {
@@ -81,9 +91,10 @@ export declare function Delete(config?: {
81
91
  params?: string[];
82
92
  query?: string[];
83
93
  headers?: string[];
94
+ useHeaders?: HttpResponseHeaders;
84
95
  body?: string[];
85
96
  bodyFilter?: BodyFilter;
86
- responseType?: 'json' | 'text' | 'html';
97
+ responseType?: HttpResponseType;
87
98
  validator?: ValidatorDefinition;
88
99
  }): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
89
100
  export {};
@@ -1,5 +1,6 @@
1
1
  import 'reflect-metadata';
2
2
  import { FrameworkError } from "../http/errors/framework";
3
+ export const DEFAULT_TRACER_TOKEN = 'Tracer';
3
4
  export function Controller(basePath = '/') {
4
5
  return function (target) {
5
6
  if (!basePath)
@@ -9,11 +10,15 @@ export function Controller(basePath = '/') {
9
10
  }
10
11
  export function Tracing(tracer) {
11
12
  return function (target, propertyKey) {
13
+ const tracerMetadata = tracer ?? {
14
+ fromContainer: true,
15
+ token: DEFAULT_TRACER_TOKEN
16
+ };
12
17
  if (!propertyKey) {
13
- Reflect.defineMetadata('tracer', tracer, target);
18
+ Reflect.defineMetadata('tracer', tracerMetadata, target);
14
19
  }
15
20
  else {
16
- Reflect.defineMetadata('tracer', tracer, target, propertyKey);
21
+ Reflect.defineMetadata('tracer', tracerMetadata, target, propertyKey);
17
22
  }
18
23
  };
19
24
  }
@@ -111,6 +116,7 @@ export function Post(config = {}) {
111
116
  params: config.params,
112
117
  query: config.query,
113
118
  headers: config.headers,
119
+ useHeaders: config.useHeaders,
114
120
  body: config.body,
115
121
  bodyFilter: config.bodyFilter || 'none',
116
122
  statusCode: config.statusCode || 200,
@@ -127,6 +133,7 @@ export function Get(config = {}) {
127
133
  params: config.params,
128
134
  query: config.query,
129
135
  headers: config.headers,
136
+ useHeaders: config.useHeaders,
130
137
  statusCode: config.statusCode || 200,
131
138
  responseType: config.responseType || 'json',
132
139
  validator: config.validator
@@ -141,6 +148,7 @@ export function Put(config = {}) {
141
148
  params: config.params,
142
149
  query: config.query,
143
150
  headers: config.headers,
151
+ useHeaders: config.useHeaders,
144
152
  body: config.body,
145
153
  bodyFilter: config.bodyFilter || 'none',
146
154
  statusCode: config.statusCode || 200,
@@ -157,6 +165,7 @@ export function Patch(config = {}) {
157
165
  params: config.params,
158
166
  query: config.query,
159
167
  headers: config.headers,
168
+ useHeaders: config.useHeaders,
160
169
  body: config.body,
161
170
  bodyFilter: config.bodyFilter || 'none',
162
171
  statusCode: config.statusCode || 200,
@@ -173,6 +182,7 @@ export function Delete(config = {}) {
173
182
  params: config.params,
174
183
  query: config.query,
175
184
  headers: config.headers,
185
+ useHeaders: config.useHeaders,
176
186
  body: config.body,
177
187
  bodyFilter: config.bodyFilter || 'none',
178
188
  statusCode: config.statusCode || 200,
package/dist/dede.d.ts CHANGED
@@ -8,6 +8,7 @@ export type Options = {
8
8
  use: 'elysia' | 'express';
9
9
  port?: number;
10
10
  middlewares?: CallableFunction[];
11
+ tracer?: boolean;
11
12
  };
12
13
  controllers?: any[];
13
14
  registries: Register[];
package/dist/dede.js CHANGED
@@ -42,7 +42,11 @@ export class Dede {
42
42
  registerControllers(controllers) {
43
43
  if (this.controllersRegistered)
44
44
  return;
45
- new ControllerHandler(this.httpServer, controllers, { prefix: this.prefix, version: this.version });
45
+ new ControllerHandler(this.httpServer, controllers, {
46
+ prefix: this.prefix,
47
+ version: this.version,
48
+ tracer: this.framework.tracer
49
+ });
46
50
  this.controllersRegistered = true;
47
51
  }
48
52
  listen(port) {
@@ -5,12 +5,16 @@ export default class ControllerHandler {
5
5
  private readonly errorMapper;
6
6
  private readonly prefix?;
7
7
  private readonly version?;
8
+ private readonly tracerEnabled;
8
9
  constructor(httpServer: HttpServer, controllers?: any[], options?: {
9
10
  prefix?: string;
10
11
  version?: number;
12
+ tracer?: boolean;
11
13
  });
12
14
  private registryControllers;
13
15
  private resolveMiddleware;
16
+ private resolveTracer;
17
+ private isTracerFromContainer;
14
18
  private buildRoute;
15
19
  private joinSegments;
16
20
  private trimLeadingSlash;
@@ -1,8 +1,10 @@
1
+ import { DEFAULT_TRACER_TOKEN } from "../application/controller";
1
2
  import { FrameworkError } from "../http/errors/framework";
2
3
  import { HttpRequestMapper } from "../interface/http/request-mapper";
3
4
  import { MiddlewareExecutor } from "../interface/http/middleware-executor";
4
5
  import { HttpErrorMapper } from "../interface/errors/http-error-mapper";
5
6
  import { validateWithClassValidator } from "../interface/validation/class-validator";
7
+ import { DefaultContainer } from "../infra/di/registry";
6
8
  export default class ControllerHandler {
7
9
  constructor(httpServer, controllers = [], options = {}) {
8
10
  this.requestMapper = new HttpRequestMapper();
@@ -10,7 +12,8 @@ export default class ControllerHandler {
10
12
  this.errorMapper = new HttpErrorMapper();
11
13
  this.prefix = options.prefix;
12
14
  this.version = options.version;
13
- for (const { handler, middlewares, validator, method, route, statusCode, params, query, headers, body, bodyFilter, responseType } of this.registryControllers(controllers)) {
15
+ this.tracerEnabled = options.tracer === true;
16
+ for (const { handler, middlewares, validator, method, route, statusCode, params, query, headers, body, bodyFilter, responseType, useHeaders } of this.registryControllers(controllers)) {
14
17
  httpServer.register({
15
18
  method,
16
19
  route,
@@ -21,7 +24,8 @@ export default class ControllerHandler {
21
24
  headers,
22
25
  middlewares,
23
26
  validator,
24
- responseType
27
+ responseType,
28
+ useHeaders
25
29
  }, async (input) => {
26
30
  let requestedAt = new Date();
27
31
  let startTime = 0;
@@ -95,7 +99,8 @@ export default class ControllerHandler {
95
99
  for (const controller of controllersList) {
96
100
  const basePath = Reflect.getMetadata('basePath', controller);
97
101
  const methodNames = Object.getOwnPropertyNames(controller.prototype).filter(method => method !== 'constructor');
98
- let tracer = Reflect.getMetadata('tracer', controller) || null;
102
+ const controllerTracerMetadata = Reflect.getMetadata('tracer', controller)
103
+ || (this.tracerEnabled ? { fromContainer: true, token: DEFAULT_TRACER_TOKEN } : null);
99
104
  const controllerVersion = Reflect.getMetadata('version', controller);
100
105
  const controllerPresetIgnore = Reflect.getMetadata('presetIgnore', controller);
101
106
  const controllerMiddlewares = Reflect.getMetadata('middlewares', controller) || [];
@@ -104,8 +109,10 @@ export default class ControllerHandler {
104
109
  const routeConfig = Reflect.getMetadata('route', controller.prototype, methodName);
105
110
  const methodMiddlewares = Reflect.getMetadata('middlewares', controller.prototype, methodName) || [];
106
111
  const middlewares = [...controllerMiddlewares, ...methodMiddlewares];
107
- const responseType = Reflect.getMetadata('responseType', controller.prototype, methodName) || 'json';
108
- tracer = Reflect.getMetadata('tracer', controller.prototype, methodName) || tracer;
112
+ const responseType = routeConfig.responseType || 'json';
113
+ const methodTracerMetadata = Reflect.getMetadata('tracer', controller.prototype, methodName);
114
+ const tracerMetadata = methodTracerMetadata || controllerTracerMetadata;
115
+ const tracer = this.resolveTracer(tracerMetadata);
109
116
  const methodVersion = Reflect.getMetadata('version', controller.prototype, methodName);
110
117
  const methodPresetIgnore = Reflect.getMetadata('presetIgnore', controller.prototype, methodName);
111
118
  const presetIgnore = methodPresetIgnore ?? controllerPresetIgnore;
@@ -120,6 +127,7 @@ export default class ControllerHandler {
120
127
  params: routeConfig.params,
121
128
  query: routeConfig.query,
122
129
  headers: routeConfig.headers,
130
+ useHeaders: routeConfig.useHeaders,
123
131
  body: routeConfig.body,
124
132
  bodyFilter: routeConfig.bodyFilter,
125
133
  statusCode: routeConfig.statusCode,
@@ -157,6 +165,34 @@ export default class ControllerHandler {
157
165
  }
158
166
  return middleware;
159
167
  }
168
+ resolveTracer(tracer) {
169
+ if (!tracer)
170
+ return undefined;
171
+ if (this.isTracerFromContainer(tracer)) {
172
+ const token = tracer.token || DEFAULT_TRACER_TOKEN;
173
+ try {
174
+ const tracerDependency = DefaultContainer.inject(token);
175
+ if (!tracerDependency?.trace || typeof tracerDependency.trace !== 'function') {
176
+ throw new FrameworkError('Tracer must implement trace()');
177
+ }
178
+ return tracerDependency;
179
+ }
180
+ catch (error) {
181
+ throw new FrameworkError(`Tracer not found in container: ${token}`);
182
+ }
183
+ }
184
+ if (!tracer?.trace || typeof tracer.trace !== 'function') {
185
+ throw new FrameworkError('Tracer must implement trace()');
186
+ }
187
+ return tracer;
188
+ }
189
+ isTracerFromContainer(value) {
190
+ return !!value
191
+ && typeof value === 'object'
192
+ && 'fromContainer' in value
193
+ && value.fromContainer === true
194
+ && 'token' in value;
195
+ }
160
196
  buildRoute(baseRoute, version, prefix) {
161
197
  let route = baseRoute;
162
198
  if (version !== undefined) {
@@ -6,6 +6,8 @@ export type Request = {
6
6
  };
7
7
  export type HttpStatusCode = 200 | 201 | 204 | 401 | 403 | 404 | 409 | 422 | 500;
8
8
  export type AllowedMethods = 'get' | 'post' | 'put' | 'delete' | 'patch';
9
+ export type HttpResponseType = 'json' | 'text' | 'html' | `${string}/${string}`;
10
+ export type HttpResponseHeaders = Record<string, string>;
9
11
  export type HttpServerParams = {
10
12
  method: AllowedMethods;
11
13
  route: string;
@@ -14,7 +16,7 @@ export type HttpServerParams = {
14
16
  methodName: string;
15
17
  tracer?: Tracer<any>;
16
18
  };
17
- responseType: 'json' | 'text' | 'html';
19
+ responseType: HttpResponseType;
18
20
  middlewares?: Middleware[];
19
21
  validator?: ValidatorDefinition;
20
22
  statusCode?: number;
@@ -23,6 +25,7 @@ export type HttpServerParams = {
23
25
  body?: string[];
24
26
  bodyFilter?: 'none' | 'restrict';
25
27
  headers?: string[];
28
+ useHeaders?: HttpResponseHeaders;
26
29
  };
27
30
  type FrameworkWeb = {
28
31
  listen(port: number): void;
@@ -49,6 +49,20 @@ export default class HttpServer {
49
49
  params,
50
50
  body
51
51
  });
52
+ if (httpServerParams.useHeaders) {
53
+ for (const [headerName, headerValue] of Object.entries(httpServerParams.useHeaders)) {
54
+ set.headers[headerName] = headerValue;
55
+ }
56
+ }
57
+ if (httpServerParams.responseType === 'html') {
58
+ set.headers['content-type'] = 'text/html; charset=utf-8';
59
+ }
60
+ else if (httpServerParams.responseType === 'text') {
61
+ set.headers['content-type'] = 'text/plain; charset=utf-8';
62
+ }
63
+ else if (httpServerParams.responseType !== 'json') {
64
+ set.headers['content-type'] = httpServerParams.responseType;
65
+ }
52
66
  return output;
53
67
  });
54
68
  }
@@ -64,8 +78,18 @@ export default class HttpServer {
64
78
  params: request.params,
65
79
  body: method !== 'get' ? request.body : {}
66
80
  });
67
- const type = httpServerParams.responseType === 'html' ? 'text' : httpServerParams.responseType;
68
- return res[type](output);
81
+ if (httpServerParams.useHeaders) {
82
+ for (const [headerName, headerValue] of Object.entries(httpServerParams.useHeaders)) {
83
+ res.set(headerName, headerValue);
84
+ }
85
+ }
86
+ if (httpServerParams.responseType === 'json')
87
+ return res.json(output);
88
+ if (httpServerParams.responseType === 'html')
89
+ return res.type('text/html').send(output);
90
+ if (httpServerParams.responseType === 'text')
91
+ return res.type('text/plain').send(output);
92
+ return res.type(httpServerParams.responseType).send(output);
69
93
  });
70
94
  }
71
95
  ;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framework-do-dede",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",