sashiko 0.1.0 → 0.3.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 (67) hide show
  1. package/README.md +132 -27
  2. package/dist/core/app.d.ts +30 -6
  3. package/dist/core/app.d.ts.map +1 -1
  4. package/dist/core/app.js +73 -18
  5. package/dist/core/app.js.map +1 -1
  6. package/dist/core/compiler.d.ts +34 -10
  7. package/dist/core/compiler.d.ts.map +1 -1
  8. package/dist/core/compiler.js +60 -36
  9. package/dist/core/compiler.js.map +1 -1
  10. package/dist/core/context.d.ts +43 -7
  11. package/dist/core/context.d.ts.map +1 -1
  12. package/dist/core/context.js +41 -11
  13. package/dist/core/context.js.map +1 -1
  14. package/dist/core/index.d.ts +6 -3
  15. package/dist/core/index.d.ts.map +1 -1
  16. package/dist/core/index.js +2 -1
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/core/infer.d.ts +9 -1
  19. package/dist/core/infer.d.ts.map +1 -1
  20. package/dist/core/metadata.d.ts +21 -5
  21. package/dist/core/metadata.d.ts.map +1 -1
  22. package/dist/core/metadata.js +6 -0
  23. package/dist/core/metadata.js.map +1 -1
  24. package/dist/core/mounted-group.d.ts +41 -0
  25. package/dist/core/mounted-group.d.ts.map +1 -0
  26. package/dist/core/mounted-group.js +43 -0
  27. package/dist/core/mounted-group.js.map +1 -0
  28. package/dist/core/response.d.ts +22 -8
  29. package/dist/core/response.d.ts.map +1 -1
  30. package/dist/core/response.js +93 -15
  31. package/dist/core/response.js.map +1 -1
  32. package/dist/core/shutdown.d.ts +36 -0
  33. package/dist/core/shutdown.d.ts.map +1 -0
  34. package/dist/core/shutdown.js +53 -0
  35. package/dist/core/shutdown.js.map +1 -0
  36. package/dist/decorators/index.d.ts +1 -1
  37. package/dist/decorators/index.d.ts.map +1 -1
  38. package/dist/decorators/index.js +1 -1
  39. package/dist/decorators/index.js.map +1 -1
  40. package/dist/decorators/middlewares.d.ts +28 -0
  41. package/dist/decorators/middlewares.d.ts.map +1 -0
  42. package/dist/decorators/middlewares.js +40 -0
  43. package/dist/decorators/middlewares.js.map +1 -0
  44. package/dist/middlewares/index.d.ts +1 -1
  45. package/dist/middlewares/index.d.ts.map +1 -1
  46. package/dist/middlewares/index.js.map +1 -1
  47. package/dist/middlewares/security-headers.d.ts +4 -2
  48. package/dist/middlewares/security-headers.d.ts.map +1 -1
  49. package/dist/middlewares/security-headers.js +10 -2
  50. package/dist/middlewares/security-headers.js.map +1 -1
  51. package/dist/schema/validation.d.ts +11 -0
  52. package/dist/schema/validation.d.ts.map +1 -1
  53. package/dist/schema/validation.js +18 -0
  54. package/dist/schema/validation.js.map +1 -1
  55. package/dist/utils/content-type.d.ts +8 -0
  56. package/dist/utils/content-type.d.ts.map +1 -1
  57. package/dist/utils/content-type.js +42 -0
  58. package/dist/utils/content-type.js.map +1 -1
  59. package/dist/utils/index.d.ts +1 -1
  60. package/dist/utils/index.d.ts.map +1 -1
  61. package/dist/utils/index.js +1 -1
  62. package/dist/utils/index.js.map +1 -1
  63. package/package.json +1 -1
  64. package/dist/decorators/use.d.ts +0 -26
  65. package/dist/decorators/use.d.ts.map +0 -1
  66. package/dist/decorators/use.js +0 -38
  67. package/dist/decorators/use.js.map +0 -1
package/README.md CHANGED
@@ -33,6 +33,7 @@ await app.listen({ port: 3000 });
33
33
  - [Requisitos e instalação](#requisitos-e-instalação)
34
34
  - [Rotas](#rotas)
35
35
  - [Registro e composição: `app.mount()`](#registro-e-composição-appmount)
36
+ - [Mapeamento manual](#mapeamento-manual)
36
37
  - [Schemas: validação, serialização e tipos](#schemas-validação-serialização-e-tipos)
37
38
  - [Respostas e o `ctx`](#respostas-e-o-ctx)
38
39
  - [Middlewares e guards](#middlewares-e-guards)
@@ -99,24 +100,50 @@ app
99
100
  .mount(HealthRoutes)
100
101
  .mount(userRoutes) // como declarado
101
102
  .mount(userRoutes, { prefix: '/v2/users' }) // a mesma instância em outro prefixo
102
- .mount(userRoutes, { prefix: '/public/users', only: ['list', 'get'] }) // exposição parcial
103
+ .mount(userRoutes, { prefix: '/public/users', only: (r) => [r.list, r.get] }) // exposição parcial
103
104
  .mount(new AdminRoutes(repo), { middlewares: [authenticate], guards: [isAdmin] }); // regras para o grupo
104
105
  ```
105
106
 
106
107
  | Opção | Efeito |
107
108
  |---|---|
108
109
  | `prefix` | Substitui o prefixo do `@Router` |
109
- | `only` / `except` | Registra só esses métodos, ou todos menos esses. Os nomes são checados pelo TypeScript; usar os dois juntos gera erro |
110
- | `middlewares` | Valem para todas as rotas registradas; rodam antes dos `@Use` da classe |
111
- | `guards` | Valem para todas as rotas registradas; rodam antes dos `@UseGuards` da classe |
110
+ | `only` / `except` | Registra só os métodos selecionados, ou todos menos esses: `(r) => [r.list, r.get]`. O seletor é checado pelo TypeScript; usar os dois juntos gera erro |
111
+ | `auto` | `false` registra só as [rotas manuais](#mapeamento-manual). Padrão: `true` |
112
+ | `middlewares` | Valem para todas as rotas do grupo; rodam antes dos `@Middlewares` da classe |
113
+ | `guards` | Valem para todas as rotas do grupo; rodam antes dos `@Guards` da classe |
112
114
 
113
115
  Erros de registro aparecem na inicialização, com mensagem clara:
114
116
  - classe sem `@Router`;
115
- - nome em `only`/`except` que não é método de rota;
117
+ - seletor em `only`/`except` que não aponta para um método de rota;
116
118
  - a mesma classe registrada duas vezes com o mesmo prefixo.
117
119
 
118
120
  > **Por que `mount` e não `register`?** O método monta um grupo de rotas sob um prefixo, quantas vezes for preciso, como o `koa-mount` e os routers do Express. No Fastify, `register` significa plugin encapsulado, e esse nome fica reservado para um futuro sistema de plugins.
119
121
 
122
+ ## Mapeamento manual
123
+
124
+ Depois do `mount`, os verbos `.get()`, `.post()`, `.put()`, `.patch()` e `.delete()` mapeiam um método da classe para um path que ela não declara: um alias, um endpoint legado, um export. O path é relativo ao prefixo do grupo, e o método é escolhido por um **seletor**, checado pelo TypeScript:
125
+
126
+ ```ts
127
+ @Router('/users')
128
+ class UserRoutes {
129
+ @Get('/:id', byId) getUser(ctx: Context<typeof byId>) {}
130
+ @Schema(csv) exportCsv(ctx: Context<typeof csv>) {} // sem verbo: só por mapeamento manual
131
+ private findOr404() {} // sem decorator: nunca pode ser exposto
132
+ }
133
+
134
+ app
135
+ .mount(new UserRoutes(repo))
136
+ .get('/legacy/:id', (r) => r.getUser) // GET /users/legacy/:id
137
+ .get('/export.csv', (r) => r.exportCsv, { middlewares: [cache()], guards: [isAuditor] })
138
+ .mount(HealthRoutes); // o encadeamento continua
139
+ ```
140
+
141
+ - **Seletor:** funciona com instância (inclusive inline, `mount(new X(repo))`) e com a classe. "Ir para a definição" e renomear no editor funcionam.
142
+ - **Permissão por método:** só podem ser mapeados métodos com algum decorator do Sashiko (verbo, `@Schema`, `@Middlewares` ou `@Guards`). Um método sem decorator gera erro no registro.
143
+ - **Regras herdadas:** a rota manual usa os schemas e os `@Middlewares`/`@Guards` do método, e as regras do grupo.
144
+ - **Opções da rota:** `{ middlewares, guards }` em objeto, e não uma lista de funções, para que middleware, guard e seletor nunca se confundam.
145
+ - **Só mapeamento manual:** `mount(x, { auto: false })` não registra as rotas declaradas na classe.
146
+
120
147
  ## Schemas: validação, serialização e tipos
121
148
 
122
149
  Os schemas da rota (`params`, `query`, `headers`, `body` e `response`) são escritos em [TypeBox](https://github.com/sinclairzx81/typebox) (JSON Schema) e podem ser declarados de duas formas equivalentes:
@@ -170,16 +197,80 @@ list(ctx: Context<typeof list>) {
170
197
 
171
198
  ## Respostas e o `ctx`
172
199
 
173
- O valor retornado pelo handler vira a resposta JSON. Para outros formatos:
200
+ O valor retornado pelo handler vira a resposta. Sem schema de resposta, o formato vem do tipo do valor:
201
+
202
+ | Retorno | Resposta |
203
+ |---|---|
204
+ | objeto, array, número, booleano, `null` | `application/json` |
205
+ | `string` | `text/plain; charset=utf-8`, sem aspas |
206
+ | `Buffer` / `Uint8Array` | `application/octet-stream` |
207
+ | `Readable` (stream) | enviado em stream |
208
+ | `undefined` | sem corpo |
209
+
210
+ Um `Content-Type` definido no handler (`ctx.header('Content-Type', ...)`) sempre prevalece.
211
+
212
+ ### Outros formatos: `contentType`, serializadores e `Accept`
213
+
214
+ Uma entrada de `response` pode declarar o tipo de conteúdo. Um schema sem `contentType` continua sendo JSON:
215
+
216
+ ```ts
217
+ // CSV cru: o `return` de uma string sai como está
218
+ @Get('/export.csv', { response: { 200: { contentType: 'text/csv', schema: Type.String() } } })
219
+ exportCsv() { return 'id,name\n1,Ana\n'; }
220
+
221
+ // Vários formatos: o cliente escolhe pelo Accept (q-values e curingas); sem Accept, vale o primeiro
222
+ @Get('/', { response: { 200: { contentType: ['application/json', 'application/yaml'], schema: Type.Array(User) } } })
223
+ list() { return users; }
224
+ ```
225
+
226
+ Objetos em formatos que não são JSON passam por um **serializador** registrado no app. A biblioteca é escolha sua:
227
+
228
+ ```ts
229
+ import YAML from 'yaml';
230
+ createApp({ serializers: { 'application/yaml': (data) => YAML.stringify(data) } });
231
+ ```
232
+
233
+ - **Campos não declarados nunca saem, em nenhum formato.** Antes do serializador, a saída é validada contra o schema e os campos extras são removidos (sobre uma cópia). Se não bater com o schema, a resposta é 500, com o motivo no log.
234
+ - **Sem formato aceitável:** a resposta é `406 Not Acceptable`, com os tipos disponíveis. Rotas com mais de um tipo enviam `Vary: Accept`.
235
+ - **Sem serializador:** um objeto num tipo sem serializador registrado gera 500, com a instrução no log.
236
+ - **Erros:** continuam em JSON.
237
+
238
+ ### Dois estilos, um pipeline
239
+
240
+ Dá para responder com `return` ou com envio imperativo (`ctx.json()`, `ctx.send()`, `ctx.text()`). Os dois passam pelo **mesmo** pipeline: schema do status, remoção de campos não declarados, status padrão, negociação e serializadores. A resposta é idêntica.
241
+
242
+ ```ts
243
+ const job = {
244
+ body: Type.Object({ async: Type.Boolean() }),
245
+ response: { 200: Type.Object({ result: Type.String() }), 202: Type.Object({ jobId: Type.String() }) },
246
+ } satisfies RouteSchemas;
247
+
248
+ @Post('/jobs', job)
249
+ run(ctx: Context<typeof job>) {
250
+ if (ctx.body.async) return ctx.status(202).json({ jobId: enqueue() }); // status decidido em runtime
251
+ return { result: process() }; // status padrão
252
+ }
253
+ ```
254
+
255
+ **O `status()` tipa o corpo.** Com schemas de resposta, `ctx.status()` só aceita os status declarados, e o `json()`/`send()` seguinte exige o corpo daquele status. `ctx.status(202).json({ result })` e `ctx.status(201)` são erros de compilação no exemplo acima.
256
+
257
+ Estilo recomendado:
258
+ - `return` para o caso comum;
259
+ - `ctx.status(x).json(body)` quando o status é decidido em runtime;
260
+ - `throw` para erros.
261
+
262
+ Os schemas de resposta valem para o que o **handler** envia. Middlewares e guards que respondem antes dele (manutenção, cache, 401), as respostas de erro e os 404/405 do framework não passam pelo schema da rota.
263
+
264
+ ### Helpers do `ctx`
174
265
 
175
266
  | Método | Uso |
176
267
  |---|---|
177
268
  | `ctx.status(code)` | Define o status |
178
269
  | `ctx.header(name, value)` | Define um header da resposta |
179
270
  | `ctx.get(name)` | Lê um header da requisição |
180
- | `ctx.json(data)` | JSON explícito |
181
- | `ctx.text(str)` | `text/plain; charset=utf-8` |
182
- | `ctx.send(body, contentType?)` | `string`, `Buffer`/`Uint8Array` ou `Readable` (stream) |
271
+ | `ctx.json(data)` | Envia JSON pelo pipeline (schema, filtro, status padrão) |
272
+ | `ctx.text(str)` | `text/plain; charset=utf-8`, pelo pipeline |
273
+ | `ctx.send(body, contentType?)` | Qualquer corpo pelo pipeline: string, bytes, stream ou objeto (negociado pelo `Accept`) |
183
274
  | `ctx.redirect(url, status?)` | 302 por padrão; aceita 301, 303, 307, 308 |
184
275
  | `ctx.throw(status, message)` | Lança um `HttpError` |
185
276
 
@@ -199,7 +290,7 @@ app.use(async (ctx, next) => {
199
290
  });
200
291
  ```
201
292
 
202
- Por classe ou por método, use `@Use` e `@UseGuards`; para um grupo inteiro, use as opções `middlewares` e `guards` do `app.mount()`. Um guard retorna `true` para seguir, `false` para responder 403, ou lança um `HttpError` (por exemplo, `unauthorized()` para 401).
293
+ Por classe ou por método, use `@Middlewares` e `@Guards`; para um grupo inteiro ou uma rota manual, use as opções `middlewares` e `guards`, com os mesmos nomes em todos os níveis. Um guard retorna `true` para seguir, `false` para responder 403, ou lança um `HttpError` (por exemplo, `unauthorized()` para 401).
203
294
 
204
295
  ```ts
205
296
  declare module 'sashiko' {
@@ -212,10 +303,10 @@ const authenticate: Middleware = async (ctx, next) => {
212
303
  };
213
304
  const isAdmin: Guard = (ctx) => ctx.state.user?.role === 'admin';
214
305
 
215
- @Use(authenticate)
306
+ @Middlewares(authenticate)
216
307
  @Router('/admin')
217
308
  class AdminRoutes {
218
- @UseGuards(isAdmin)
309
+ @Guards(isAdmin)
219
310
  @Delete('/users/:id')
220
311
  remove() {}
221
312
  }
@@ -224,13 +315,14 @@ class AdminRoutes {
224
315
  **Ordem de execução:**
225
316
 
226
317
  ```
227
- globais → app.mount(…, { middlewares }) → @Use (classe → método)
228
- → app.mount(…, { guards }) → @UseGuards (classe → método) → validação → handler
318
+ globais → mount.middlewares → @Middlewares (classe → método) → rota manual.middlewares
319
+ → mount.guards → @Guards (classe → método) → rota manual.guards
320
+ → validação → handler
229
321
  ```
230
322
 
231
- **Ordem dos decorators no código.** `@Router`, `@Get`/`@Post`/…, `@Schema`, `@Use` e `@UseGuards` só registram metadata, então a posição entre eles não muda o comportamento; a única regra é que vários `@Use` (ou `@UseGuards`) executam na ordem em que aparecem, de cima para baixo. Já decorators que **envolvem** o método, como `@timer()`, medem tudo o que estiver abaixo deles.
323
+ **Ordem dos decorators no código.** `@Router`, `@Get`/`@Post`/…, `@Schema`, `@Middlewares` e `@Guards` só registram metadata, então a posição entre eles não muda o comportamento; a única regra é que vários `@Middlewares` (ou `@Guards`) executam na ordem em que aparecem, de cima para baixo. Já decorators que **envolvem** o método, como `@timer()`, medem tudo o que estiver abaixo deles.
232
324
 
233
- Os guards rodam **antes** da validação: quem não tem acesso recebe 401/403, e não um 400 que revelaria o formato esperado. Vários `@Use` na mesma posição executam de cima para baixo.
325
+ Os guards rodam **antes** da validação: quem não tem acesso recebe 401/403, e não um 400 que revelaria o formato esperado.
234
326
 
235
327
  ## Erros
236
328
 
@@ -252,7 +344,7 @@ Ligado por padrão:
252
344
 
253
345
  | Proteção | Detalhe |
254
346
  |---|---|
255
- | Headers de segurança | `X-Content-Type-Options: nosniff`, `Content-Security-Policy: default-src 'none'; frame-ancestors 'none'`, `Referrer-Policy: no-referrer`, `X-Frame-Options: DENY`, `Cross-Origin-Resource-Policy: same-origin`. HSTS é opt-in. |
347
+ | Headers de segurança | `X-Content-Type-Options: nosniff`, `Content-Security-Policy: default-src 'none'; frame-ancestors 'none'`, `Referrer-Policy: no-referrer`, `X-Frame-Options: DENY`, `Cross-Origin-Resource-Policy: same-origin`. HSTS é opt-in. Para servir páginas, use `securityHeaders: 'web'` (CSP `default-src 'self'`, `X-Frame-Options: SAMEORIGIN`). |
256
348
  | Limite de body | 1 MiB (`bodyLimit`). Recusado já pelo `Content-Length`, com a conexão fechada. |
257
349
  | Timeouts | `requestTimeout` 30s, `headersTimeout` 10s, `keepAliveTimeout` 5s (contra Slowloris). |
258
350
  | Query | Até 1000 pares (`queryMaxKeys`); `__proto__`, `constructor` e `prototype` são descartados. |
@@ -296,17 +388,28 @@ createApp({ logger: pino(), logRequests: true });
296
388
  ```ts
297
389
  const app = createApp().mount(UserRoutes);
298
390
 
299
- const server = await app.listen({ port: 3000, host: '0.0.0.0' }); // resolve quando está ouvindo
300
-
301
- process.on('SIGTERM', async () => {
302
- await app.close(); // para de aceitar conexões e espera as requisições em andamento
303
- process.exit(0);
304
- });
391
+ await app.listen({ port: 3000, host: '0.0.0.0' }); // resolve quando está ouvindo; SIGINT/SIGTERM já tratados
305
392
 
306
- // Servidor próprio (HTTPS, http2 em modo compat…)
393
+ // Servidor próprio (HTTPS, http2 em modo compat…): aqui o ciclo de vida é seu
307
394
  https.createServer(tlsOptions, app.callback()).listen(443);
308
395
  ```
309
396
 
397
+ **Encerramento gracioso, sem código.** Com `listen()`, um SIGINT ou SIGTERM faz o app parar de aceitar conexões, esperar as requisições em andamento (com prazo) e sair com código 0:
398
+
399
+ ```ts
400
+ createApp({
401
+ shutdown: {
402
+ timeoutMs: 10_000, // prazo para as requisições em andamento (padrão)
403
+ onShutdown: async () => { await db.close(); }, // depois que o servidor fecha
404
+ },
405
+ });
406
+ ```
407
+
408
+ - **Segundo sinal:** força a saída com código 1 (o Ctrl+C duplo). Um erro no `onShutdown` também sai com 1.
409
+ - **Vários apps no mesmo processo:** compartilham um único listener por sinal e fecham todos juntos.
410
+ - **Limpeza:** os listeners são removidos no `close()`, então testes com vários `listen(0)` não acumulam listeners. O `inject()` e o `callback()` não registram nada.
411
+ - **Opções:** `signals` muda os sinais tratados, `exit: false` fecha sem chamar `process.exit`, e `shutdown: false` desliga tudo (por exemplo, se outra ferramenta controla o processo).
412
+
310
413
  **Testes sem rede.** `app.inject()` executa a requisição em memória:
311
414
 
312
415
  ```ts
@@ -322,13 +425,15 @@ assert.equal(res.json<{ name: string }>().name, 'Ana');
322
425
  | `bodyLimit` | `1048576` | Tamanho máximo do body JSON, em bytes |
323
426
  | `queryMaxKeys` | `1000` | Máximo de pares lidos da query string |
324
427
  | `requireSchemas` | `false` | Exige schemas de body e de resposta no registro das rotas |
325
- | `securityHeaders` | ligado | Opções dos headers de segurança; `false` desliga |
428
+ | `securityHeaders` | `'api'` | `'api'`, `'web'`, um objeto de opções, ou `false` para desligar |
429
+ | `serializers` | — | Serializadores por content type, para responder em outros formatos |
430
+ | `shutdown` | SIGINT/SIGTERM, 10s | `signals`, `timeoutMs`, `onShutdown`, `exit`; `false` desliga |
326
431
  | `timeouts` | 30s / 10s / 5s | `requestTimeout`, `headersTimeout`, `keepAliveTimeout` |
327
432
  | `logger` | `consoleLogger()` | Logger compatível com pino; `false` desliga |
328
433
  | `logRequests` | `false` | Log de cada requisição concluída |
329
434
  | `requestId` | header `x-request-id` | `header`, `trustIncoming`, `generate` |
330
435
 
331
- Métodos do app: `use()`, `mount()`, `onError()`, `listen()`, `close()`, `callback()`, `inject()`, `log`.
436
+ Métodos do app: `use()`, `mount()`, `onError()`, `listen()`, `close()`, `callback()`, `inject()`, `log`. O `mount()` devolve um `MountedGroup`, com os verbos do mapeamento manual e os mesmos métodos do app, para continuar o encadeamento.
332
437
 
333
438
  O body só é lido como JSON quando o `Content-Type` é `application/json` ou `application/*+json`.
334
439
 
@@ -347,7 +452,7 @@ O body só é lido como JSON quando o `Content-Type` é `application/json` ou `a
347
452
 
348
453
  ## Exemplo completo
349
454
 
350
- [`examples/app-users`](examples/app-users) traz CRUD, composição com `app.mount()`, guards, CORS, rate limit, logger, herança e injeção de dependências, além de um script que percorre todos os cenários:
455
+ [`examples/app-users`](examples/app-users) traz CRUD, composição com `app.mount()`, mapeamento manual, guards, CORS, rate limit, logger, herança e injeção de dependências, além de um script que percorre todos os cenários:
351
456
 
352
457
  ```bash
353
458
  pnpm dev
@@ -1,11 +1,14 @@
1
1
  import http from 'node:http';
2
2
  import { type MountOptions } from './compiler.ts';
3
+ import { MountedGroup } from './mounted-group.ts';
3
4
  import { type Middleware } from './compose.ts';
4
5
  import { Context } from './context.ts';
5
6
  import '../shims/symbol-metadata.ts';
7
+ import { type Serializer } from './response.ts';
6
8
  import { type InjectOptions, type InjectResponse } from './inject.ts';
9
+ import { type ShutdownOptions } from './shutdown.ts';
7
10
  import { type Logger } from './logger.ts';
8
- import { type SecurityHeadersOptions } from '../middlewares/security-headers.ts';
11
+ import { type SecurityHeadersOptions, type SecurityPreset } from '../middlewares/security-headers.ts';
9
12
  export interface ListenOptions {
10
13
  /** Padrão: 3000. Use 0 para uma porta livre aleatória. */
11
14
  port?: number;
@@ -23,8 +26,16 @@ export interface ServerTimeouts {
23
26
  export interface AppOptions {
24
27
  /** Tamanho máximo do body JSON em bytes. Padrão: 1 MiB. */
25
28
  bodyLimit?: number;
26
- /** Headers de segurança aplicados a toda resposta (S-04). Ligado por padrão; `false` desliga. */
27
- securityHeaders?: SecurityHeadersOptions | false;
29
+ /**
30
+ * Headers de segurança aplicados a toda resposta (S-04). Padrão: `'api'`.
31
+ * `'web'` libera recursos do próprio site (CSP `'self'`) para quem serve páginas; `false` desliga.
32
+ */
33
+ securityHeaders?: SecurityHeadersOptions | SecurityPreset | false;
34
+ /**
35
+ * Serializadores por content type, para responder em outros formatos (F-31):
36
+ * `{ 'application/yaml': (data) => YAML.stringify(data) }`. JSON e valores crus já são nativos.
37
+ */
38
+ serializers?: Record<string, Serializer>;
28
39
  /** Timeouts do servidor HTTP (S-06). */
29
40
  timeouts?: ServerTimeouts;
30
41
  /**
@@ -40,6 +51,11 @@ export interface AppOptions {
40
51
  logRequests?: boolean;
41
52
  /** Request id (F-06). */
42
53
  requestId?: RequestIdOptions;
54
+ /**
55
+ * Encerramento gracioso nos sinais do processo, ativado pelo `listen()` (F-07).
56
+ * Ligado por padrão (SIGINT/SIGTERM, 10s); `false` desliga.
57
+ */
58
+ shutdown?: ShutdownOptions | false;
43
59
  }
44
60
  export interface RequestIdOptions {
45
61
  /** Header lido da requisição e devolvido na resposta. Padrão: `x-request-id`. `false` não envia. */
@@ -58,7 +74,11 @@ export declare class SashikoApp {
58
74
  private errorHandlers;
59
75
  private applySecurityHeaders;
60
76
  private readonly opts;
77
+ private readonly serializers;
61
78
  private server;
79
+ /** Respostas em andamento, para o encerramento gracioso não travar em conexões keep-alive. */
80
+ private readonly inflight;
81
+ private closing;
62
82
  /** Logger do app (F-06). Use para logs fora de uma requisição. */
63
83
  readonly log: Logger;
64
84
  constructor(opts?: AppOptions);
@@ -71,11 +91,13 @@ export declare class SashikoApp {
71
91
  * ```ts
72
92
  * app
73
93
  * .mount(HealthRoutes)
74
- * .mount(new UserRoutes(repo), { prefix: '/v2/users', middlewares: [auth], except: ['remove'] });
94
+ * .mount(new UserRoutes(repo), { prefix: '/v2/users', middlewares: [auth], except: (r) => [r.remove] })
95
+ * .get('/legacy/:id', (r) => r.getUser); // mapeamento manual (F-30)
75
96
  * ```
76
97
  */
77
- mount<T extends object>(ref: new () => T, options?: MountOptions<T>): this;
78
- mount<T extends object>(ref: T extends abstract new (...args: never) => unknown ? never : T, options?: MountOptions<T>): this;
98
+ mount<T extends object>(ref: new () => T, options?: MountOptions<T>): MountedGroup<T>;
99
+ mount<T extends object>(ref: T extends abstract new (...args: never) => unknown ? never : T, options?: MountOptions<T>): MountedGroup<T>;
100
+ private addRoute;
79
101
  handle(ctx: Context): Promise<void>;
80
102
  /**
81
103
  * Executa os `onError` do usuário. Se um deles lançar, o erro é logado e seguimos
@@ -89,6 +111,8 @@ export declare class SashikoApp {
89
111
  callback(): (req: http.IncomingMessage, res: http.ServerResponse) => void;
90
112
  /** Sobe o servidor HTTP e resolve quando ele está aceitando conexões (F-09). */
91
113
  listen(options?: number | ListenOptions): Promise<http.Server>;
114
+ /** Ponte entre este app e o coordenador de sinais (F-07). */
115
+ private readonly shutdownTarget;
92
116
  /**
93
117
  * Para de aceitar conexões, encerra as ociosas e espera as requisições em andamento terminarem.
94
118
  * Depois de `graceMs`, força o fechamento das restantes.
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"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"}
package/dist/core/app.js CHANGED
@@ -1,16 +1,19 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import http from 'node:http';
3
3
  import { performance } from 'node:perf_hooks';
4
- import { compileRoutes } from './compiler.js';
4
+ import { compileAutoRoutes, compileManualRoute, createGroup } from './compiler.js';
5
+ import { MountedGroup } from './mounted-group.js';
5
6
  import { compose } from './compose.js';
6
- import { Context } from './context.js';
7
+ import { Context, kBindResponse, kRawSend } from './context.js';
7
8
  import '../shims/symbol-metadata.js';
8
9
  import { bodyParser } from './body-parser.js';
9
10
  import { assertSchemas, validateRequest } from './request-validation.js';
10
11
  import { sendError, sendResult } from './response.js';
11
12
  import { RouteTable } from './route-table.js';
13
+ import { mediaType } from '../utils/content-type.js';
12
14
  import { inject } from './inject.js';
13
15
  import { guardsMiddleware } from './guard.js';
16
+ import { shutdownCoordinator } from './shutdown.js';
14
17
  import { consoleLogger, silentLogger } from './logger.js';
15
18
  import { buildSecurityHeaders } from '../middlewares/security-headers.js';
16
19
  const SAFE_REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/;
@@ -20,12 +23,23 @@ export class SashikoApp {
20
23
  errorHandlers = [];
21
24
  applySecurityHeaders;
22
25
  opts;
26
+ serializers;
23
27
  server;
28
+ /** Respostas em andamento, para o encerramento gracioso não travar em conexões keep-alive. */
29
+ inflight = new Set();
30
+ closing;
24
31
  /** Logger do app (F-06). Use para logs fora de uma requisição. */
25
32
  log;
26
33
  constructor(opts = {}) {
27
34
  this.opts = opts;
35
+ this.serializers = new Map(Object.entries(opts.serializers ?? {}).map(([type, fn]) => [mediaType(type), fn]));
28
36
  this.log = opts.logger === false ? silentLogger : (opts.logger ?? consoleLogger());
37
+ const sd = opts.shutdown || {};
38
+ Object.assign(this.shutdownTarget, {
39
+ log: this.log,
40
+ signals: sd.signals ?? ['SIGINT', 'SIGTERM'],
41
+ exitOnShutdown: sd.exit ?? true,
42
+ });
29
43
  if (opts.securityHeaders !== false)
30
44
  this.applySecurityHeaders = buildSecurityHeaders(opts.securityHeaders);
31
45
  this.use(bodyParser(opts.bodyLimit ?? 1024 * 1024));
@@ -39,19 +53,21 @@ export class SashikoApp {
39
53
  return this;
40
54
  }
41
55
  mount(ref, options = {}) {
42
- const name = typeof ref === 'function' ? ref.name : ref.constructor.name;
43
- for (const route of compileRoutes(ref, options)) {
44
- if (this.opts.requireSchemas)
45
- assertSchemas(name, route);
46
- try {
47
- this.router.on(route.method, route.path, route);
48
- }
49
- catch (err) {
50
- throw new Error(`[sashiko] app.mount(${name}): ${route.method} ${route.path} is already registered. ` +
51
- `Use a different "prefix" to mount the same class twice.`, { cause: err });
52
- }
56
+ const group = createGroup(ref, options);
57
+ for (const route of compileAutoRoutes(group))
58
+ this.addRoute(group.name, route);
59
+ return new MountedGroup(this, group, (g, method, path, select, opts) => this.addRoute(g.name, compileManualRoute(g, method, path, select, opts)));
60
+ }
61
+ addRoute(owner, route) {
62
+ if (this.opts.requireSchemas)
63
+ assertSchemas(owner, route);
64
+ try {
65
+ this.router.on(route.method, route.path, route);
66
+ }
67
+ catch (err) {
68
+ throw new Error(`[sashiko] app.mount(${owner}): ${route.method} ${route.path} is already registered. ` +
69
+ `Use a different "prefix" to mount the same class twice.`, { cause: err });
53
70
  }
54
- return this;
55
71
  }
56
72
  async handle(ctx) {
57
73
  // Antes de tudo, para valer inclusive em erros, 404 e preflight (S-04).
@@ -68,12 +84,14 @@ export class SashikoApp {
68
84
  // F-08: OPTIONS automático para qualquer path com rotas.
69
85
  if (ctx.method === 'OPTIONS')
70
86
  return void ctx.status(204).res.end();
71
- return void ctx.status(405).json({ error: 'Method Not Allowed', allowed });
87
+ ctx.res.statusCode = 405;
88
+ return void ctx[kRawSend](JSON.stringify({ error: 'Method Not Allowed', allowed }), 'application/json; charset=utf-8');
72
89
  }
73
- return void ctx.status(404).json({ error: 'Not Found' });
90
+ ctx.res.statusCode = 404;
91
+ return void ctx[kRawSend](JSON.stringify({ error: 'Not Found' }), 'application/json; charset=utf-8');
74
92
  }
75
93
  ctx.params = match.params;
76
- // F-01: @Use → @UseGuards → validação → handler. Guards antes da validação, para que
94
+ // F-01: middlewares → guards → validação → handler. Guards antes da validação, para que
77
95
  // quem não tem acesso receba 401/403 e não um 400 que revele a estrutura esperada.
78
96
  const route = match;
79
97
  const pipeline = [...route.middlewares];
@@ -81,9 +99,11 @@ export class SashikoApp {
81
99
  pipeline.push(guardsMiddleware(route.guards));
82
100
  pipeline.push(async () => {
83
101
  validateRequest(ctx, route.schemas);
102
+ // F-32: a partir daqui, ctx.json/send/text passam pelo mesmo pipeline do `return`.
103
+ ctx[kBindResponse](route.schemas.response, this.serializers);
84
104
  const result = await route.handler(ctx);
85
105
  if (!ctx.sent)
86
- sendResult(ctx, route.schemas.response, result);
106
+ sendResult(ctx, route.schemas.response, result, this.serializers);
87
107
  });
88
108
  await compose(ctx, pipeline);
89
109
  }
@@ -120,6 +140,14 @@ export class SashikoApp {
120
140
  const generate = rid.generate ?? (() => randomUUID());
121
141
  return (req, res) => {
122
142
  const started = performance.now();
143
+ this.inflight.add(res);
144
+ if (this.closing)
145
+ res.setHeader('Connection', 'close');
146
+ res.once('close', () => {
147
+ this.inflight.delete(res);
148
+ // Durante o close(), cada resposta concluída libera as conexões que ficaram ociosas.
149
+ this.closing?.closeIdleConnections();
150
+ });
123
151
  const incoming = header ? req.headers[header] : undefined;
124
152
  const id = trust && typeof incoming === 'string' && SAFE_REQUEST_ID.test(incoming) ? incoming : generate();
125
153
  if (header)
@@ -165,8 +193,28 @@ export class SashikoApp {
165
193
  });
166
194
  });
167
195
  this.server = server;
196
+ if (this.opts.shutdown !== false)
197
+ shutdownCoordinator.register(this.shutdownTarget);
168
198
  return server;
169
199
  }
200
+ /** Ponte entre este app e o coordenador de sinais (F-07). */
201
+ shutdownTarget = {
202
+ log: undefined, // definido no construtor
203
+ signals: [],
204
+ exitOnShutdown: true,
205
+ shutdown: async (signal) => {
206
+ const opts = this.opts.shutdown || {};
207
+ try {
208
+ await this.close(opts.timeoutMs ?? 10_000);
209
+ await opts.onShutdown?.(signal);
210
+ return true;
211
+ }
212
+ catch (err) {
213
+ this.log.error({ err, signal }, 'error during graceful shutdown');
214
+ return false;
215
+ }
216
+ },
217
+ };
170
218
  /**
171
219
  * Para de aceitar conexões, encerra as ociosas e espera as requisições em andamento terminarem.
172
220
  * Depois de `graceMs`, força o fechamento das restantes.
@@ -176,6 +224,11 @@ export class SashikoApp {
176
224
  if (!server)
177
225
  return;
178
226
  this.server = undefined;
227
+ this.closing = server;
228
+ // Respostas que ainda não enviaram headers avisam o cliente para não reusar a conexão.
229
+ for (const res of this.inflight)
230
+ if (!res.headersSent)
231
+ res.setHeader('Connection', 'close');
179
232
  const force = setTimeout(() => server.closeAllConnections(), graceMs);
180
233
  force.unref();
181
234
  try {
@@ -186,6 +239,8 @@ export class SashikoApp {
186
239
  }
187
240
  finally {
188
241
  clearTimeout(force);
242
+ this.closing = undefined;
243
+ shutdownCoordinator.unregister(this.shutdownTarget);
189
244
  }
190
245
  }
191
246
  /** Executa uma requisição em memória, sem rede. Ideal para testes (F-09). */
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAyD,MAAM,eAAe,CAAC;AAC1I,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAmB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,6BAA6B,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAmB,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,MAAM,EAA2C,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAA6C,MAAM,eAAe,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAoD,MAAM,oCAAoC,CAAC;AAiE5H,MAAM,eAAe,GAAG,0BAA0B,CAAC;AAEnD,MAAM,OAAO,UAAU;IACb,WAAW,GAAiB,EAAE,CAAC;IAC/B,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;IAC1B,aAAa,GAA6D,EAAE,CAAC;IAC7E,oBAAoB,CAAmD;IAC9D,IAAI,CAAa;IACjB,WAAW,CAAkC;IACtD,MAAM,CAA0B;IACxC,8FAA8F;IAC7E,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IACnD,OAAO,CAA0B;IACzC,kEAAkE;IACzD,GAAG,CAAS;IAErB,YAAY,IAAI,GAAe,EAAE;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9G,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC;QACnF,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE;YACjC,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC;YAC5C,cAAc,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI;SAChC,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,eAAe,KAAK,KAAK;YAAE,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC3G,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,GAAG,CAAC,EAAc;QAChB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,CAAuD;QAC7D,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAeD,KAAK,CAAC,GAAc,EAAE,OAAO,GAAiB,EAAE;QAC9C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACxC,KAAK,MAAM,KAAK,IAAI,iBAAiB,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/E,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CACrE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CACzE,CAAC;IACJ,CAAC;IAEO,QAAQ,CAAC,KAAa,EAAE,KAAoB;QAClD,IAAI,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,uBAAuB,KAAK,MAAM,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,0BAA0B;gBACtF,yDAAyD,EACzD,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;IACH,CAAC;IAID,KAAK,CAAC,MAAM,CAAC,GAAY;QACvB,wEAAwE;QACxE,IAAI,CAAC,oBAAoB,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACrC,IAAI,GAAG,CAAC,IAAI;gBAAE,OAAO;YAErB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACrD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC/C,yDAAyD;oBACzD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;wBAAE,OAAO,KAAK,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;oBACpE,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACzB,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,CAAC,EAAE,iCAAiC,CAAC,CAAC;gBACzH,CAAC;gBACD,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACzB,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,iCAAiC,CAAC,CAAC;YACvG,CAAC;YAED,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAE1B,wFAAwF;YACxF,mFAAmF;YACnF,MAAM,KAAK,GAAG,KAAK,CAAC;YACpB,MAAM,QAAQ,GAAiB,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;YACtD,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3E,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACvB,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBACpC,mFAAmF;gBACnF,GAAG,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC7D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACxC,IAAI,CAAC,GAAG,CAAC,IAAI;oBAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACnF,CAAC,CAAC,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,gBAAgB,CAAC,GAAY,EAAE,GAAY;QACvD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACpB,CAAC;YAAC,OAAO,UAAU,EAAE,CAAC;gBACpB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,wBAAwB,CAAC,CAAC;gBACjG,MAAM;YACR,CAAC;YACD,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa;gBAAE,OAAO;QACpC,CAAC;QACD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACtB,CAAC;IAED;;;OAGG;IACH,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;QAC/F,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC;QACxC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;QAEtD,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAClB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,IAAI,CAAC,OAAO;gBAAE,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACvD,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;gBACrB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC1B,qFAAqF;gBACrF,IAAI,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC;YACvC,CAAC,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,MAAM,EAAE,GAAG,KAAK,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC3G,IAAI,MAAM;gBAAE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAEtC,2FAA2F;YAC3F,MAAM,IAAI,GAAG,CAAC,GAAY,EAAE,EAAE;gBAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,EAAE,wCAAwC,CAAC,CAAC;gBAC/G,IAAI,CAAC,GAAG,CAAC,WAAW;oBAAE,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBAC3C,IAAI,CAAC,GAAG,CAAC,aAAa;oBAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YACpC,CAAC,CAAC;YACF,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAClG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;oBAC1B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;wBACtB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;wBACjE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAC;oBACxG,CAAC,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,CAAC;YACZ,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,MAAM,CAAC,OAAO,GAA2B,EAAE;QAC/C,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QAChG,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,WAAW,EAAE,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAEtG,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QACnC,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,IAAI,MAAM,CAAC;QACnD,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,IAAI,MAAM,EAAE,MAAM,CAAC,cAAc,IAAI,QAAQ,CAAC,CAAC;QAChG,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAC,gBAAgB,IAAI,KAAK,CAAC;QAEtD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;gBAC7B,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC5B,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,KAAK;YAAE,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,6DAA6D;IAC5C,cAAc,GAAmB;QAChD,GAAG,EAAE,SAA8B,EAAE,yBAAyB;QAC9D,OAAO,EAAE,EAAE;QACX,cAAc,EAAE,IAAI;QACpB,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;gBAC3C,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC;gBAChC,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,gCAAgC,CAAC,CAAC;gBAClE,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;KACF,CAAC;IAEF;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,uFAAuF;QACvF,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,GAAG,CAAC,WAAW;gBAAE,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC5F,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE,EAAE,OAAO,CAAC,CAAC;QACtE,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,CAAC;YACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACvD,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAChC,CAAC,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,mBAAmB,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,MAAM,CAAC,IAA4B;QACjC,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC;CAEF;AAED,MAAM,UAAU,SAAS,CAAC,IAAiB;IACzC,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import type { Guard } from './guard.ts';
2
2
  import type { Middleware } from './compose.ts';
3
+ import { type RouterMetadata } from './metadata.ts';
3
4
  import type { RouteDefinition } from './route-table.ts';
4
5
  export interface CompiledRoute extends RouteDefinition {
5
6
  method: string;
@@ -7,22 +8,45 @@ export interface CompiledRoute extends RouteDefinition {
7
8
  }
8
9
  /** Classe com construtor sem argumentos **ou** instância já construída (B-10). */
9
10
  export type RouterRef = (new () => object) | object;
10
- /** Nomes dos métodos de uma instância (para `only`/`except`). */
11
- export type MethodNames<T> = {
12
- [K in keyof T]: T[K] extends (...args: never[]) => unknown ? K : never;
13
- }[keyof T] & string;
14
- /** Opções de `app.mount()`, aplicadas no ponto de composição (F-28). */
11
+ type AnyMethod = (...args: never[]) => unknown;
12
+ /** Seleciona um método da instância montada: `(r) => r.getUser` (F-30). */
13
+ export type MethodSelector<T> = (routes: T) => AnyMethod;
14
+ /** Seleciona vários métodos: `(r) => [r.list, r.getUser]` (F-30). */
15
+ export type MethodsSelector<T> = (routes: T) => readonly AnyMethod[];
16
+ /** Opções de `app.mount()`, aplicadas no ponto de composição (F-28, F-30). */
15
17
  export interface MountOptions<T = object> {
16
18
  /** Substitui o prefixo definido no `@Router`. */
17
19
  prefix?: string;
18
20
  /** Registra só estes métodos de rota. Não pode ser usado junto com `except`. */
19
- only?: readonly MethodNames<T>[];
21
+ only?: MethodsSelector<T>;
20
22
  /** Registra todos os métodos de rota, exceto estes. */
21
- except?: readonly MethodNames<T>[];
22
- /** Middlewares para todas as rotas registradas; rodam antes dos `@Use` da classe. */
23
+ except?: MethodsSelector<T>;
24
+ /** Registra as rotas declaradas na classe. Padrão: `true`. Use `false` para só mapear manualmente. */
25
+ auto?: boolean;
26
+ /** Middlewares para todas as rotas do grupo; rodam antes dos `@Middlewares` da classe. */
23
27
  middlewares?: readonly Middleware[];
24
- /** Guards para todas as rotas registradas; rodam antes dos `@UseGuards` da classe. */
28
+ /** Guards para todas as rotas do grupo; rodam antes dos `@Guards` da classe. */
25
29
  guards?: readonly Guard[];
26
30
  }
27
- export declare function compileRoutes(ref: RouterRef, opts?: MountOptions): CompiledRoute[];
31
+ /** Opções de uma rota mapeada manualmente: `.get(path, sel, { middlewares, guards })` (F-30). */
32
+ export interface ManualRouteOptions {
33
+ /** Rodam depois dos `@Middlewares` do método (são as mais específicas). */
34
+ middlewares?: readonly Middleware[];
35
+ /** Rodam depois dos `@Guards` do método. */
36
+ guards?: readonly Guard[];
37
+ }
38
+ /** Um grupo montado: a instância, sua metadata e as opções do `mount`. */
39
+ export interface RouteGroup {
40
+ name: string;
41
+ instance: object;
42
+ meta: RouterMetadata;
43
+ prefix: string;
44
+ options: MountOptions;
45
+ }
46
+ export declare function createGroup(ref: RouterRef, options?: MountOptions): RouteGroup;
47
+ /** Rotas declaradas na classe (`@Get`, `@Post`…), filtradas por `only`/`except` e `auto`. */
48
+ export declare function compileAutoRoutes(group: RouteGroup): CompiledRoute[];
49
+ /** Rota mapeada no app: `.get('/legacy/:id', (r) => r.getUser, opts)` (F-30). */
50
+ export declare function compileManualRoute(group: RouteGroup, httpMethod: string, path: string, selector: MethodSelector<object>, options?: ManualRouteOptions): CompiledRoute;
51
+ export {};
28
52
  //# sourceMappingURL=compiler.d.ts.map