flowingts 0.0.0-stage → 0.19.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/LICENSE +15 -0
- package/README.md +75 -2
- package/dist/core/abort.d.ts +23 -0
- package/dist/core/abort.d.ts.map +1 -0
- package/dist/core/abort.js +23 -0
- package/dist/core/abort.js.map +1 -0
- package/dist/core/app/core.d.ts +35 -0
- package/dist/core/app/core.d.ts.map +1 -0
- package/dist/core/app/core.js +2 -0
- package/dist/core/app/core.js.map +1 -0
- package/dist/core/app/options.d.ts +178 -0
- package/dist/core/app/options.d.ts.map +1 -0
- package/dist/core/app/options.js +2 -0
- package/dist/core/app/options.js.map +1 -0
- package/dist/core/app.d.ts +143 -0
- package/dist/core/app.d.ts.map +1 -0
- package/dist/core/app.js +283 -0
- package/dist/core/app.js.map +1 -0
- package/dist/core/body-parser.d.ts +14 -0
- package/dist/core/body-parser.d.ts.map +1 -0
- package/dist/core/body-parser.js +76 -0
- package/dist/core/body-parser.js.map +1 -0
- package/dist/core/compiler.d.ts +104 -0
- package/dist/core/compiler.d.ts.map +1 -0
- package/dist/core/compiler.js +151 -0
- package/dist/core/compiler.js.map +1 -0
- package/dist/core/compose.d.ts +16 -0
- package/dist/core/compose.d.ts.map +1 -0
- package/dist/core/compose.js +41 -0
- package/dist/core/compose.js.map +1 -0
- package/dist/core/context.d.ts +290 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +460 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/contracts.d.ts +76 -0
- package/dist/core/contracts.d.ts.map +1 -0
- package/dist/core/contracts.js +87 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/errors.d.ts +50 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +25 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/guard.d.ts +10 -0
- package/dist/core/guard.d.ts.map +1 -0
- package/dist/core/guard.js +14 -0
- package/dist/core/guard.js.map +1 -0
- package/dist/core/health.d.ts +37 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +68 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/hooks.d.ts +34 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +27 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/index.d.ts +30 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +11 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/infer.d.ts +68 -0
- package/dist/core/infer.d.ts.map +1 -0
- package/dist/core/infer.js +2 -0
- package/dist/core/infer.js.map +1 -0
- package/dist/core/inject.d.ts +27 -0
- package/dist/core/inject.d.ts.map +1 -0
- package/dist/core/inject.js +85 -0
- package/dist/core/inject.js.map +1 -0
- package/dist/core/logger.d.ts +57 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +134 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/metadata.d.ts +83 -0
- package/dist/core/metadata.d.ts.map +1 -0
- package/dist/core/metadata.js +91 -0
- package/dist/core/metadata.js.map +1 -0
- package/dist/core/mounted-group.d.ts +77 -0
- package/dist/core/mounted-group.d.ts.map +1 -0
- package/dist/core/mounted-group.js +131 -0
- package/dist/core/mounted-group.js.map +1 -0
- package/dist/core/registry/framework-routes.d.ts +28 -0
- package/dist/core/registry/framework-routes.d.ts.map +1 -0
- package/dist/core/registry/framework-routes.js +83 -0
- package/dist/core/registry/framework-routes.js.map +1 -0
- package/dist/core/registry/registry.d.ts +58 -0
- package/dist/core/registry/registry.d.ts.map +1 -0
- package/dist/core/registry/registry.js +208 -0
- package/dist/core/registry/registry.js.map +1 -0
- package/dist/core/registry/route-program.d.ts +16 -0
- package/dist/core/registry/route-program.d.ts.map +1 -0
- package/dist/core/registry/route-program.js +62 -0
- package/dist/core/registry/route-program.js.map +1 -0
- package/dist/core/request-validation.d.ts +15 -0
- package/dist/core/request-validation.d.ts.map +1 -0
- package/dist/core/request-validation.js +67 -0
- package/dist/core/request-validation.js.map +1 -0
- package/dist/core/response/pipeline.d.ts +51 -0
- package/dist/core/response/pipeline.d.ts.map +1 -0
- package/dist/core/response/pipeline.js +169 -0
- package/dist/core/response/pipeline.js.map +1 -0
- package/dist/core/route-table.d.ts +58 -0
- package/dist/core/route-table.d.ts.map +1 -0
- package/dist/core/route-table.js +108 -0
- package/dist/core/route-table.js.map +1 -0
- package/dist/core/runtime/observer.d.ts +47 -0
- package/dist/core/runtime/observer.d.ts.map +1 -0
- package/dist/core/runtime/observer.js +125 -0
- package/dist/core/runtime/observer.js.map +1 -0
- package/dist/core/runtime/request-handler.d.ts +62 -0
- package/dist/core/runtime/request-handler.d.ts.map +1 -0
- package/dist/core/runtime/request-handler.js +256 -0
- package/dist/core/runtime/request-handler.js.map +1 -0
- package/dist/core/runtime/server.d.ts +59 -0
- package/dist/core/runtime/server.d.ts.map +1 -0
- package/dist/core/runtime/server.js +180 -0
- package/dist/core/runtime/server.js.map +1 -0
- package/dist/core/shutdown.d.ts +41 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +53 -0
- package/dist/core/shutdown.js.map +1 -0
- package/dist/core/streaming/sse.d.ts +35 -0
- package/dist/core/streaming/sse.d.ts.map +1 -0
- package/dist/core/streaming/sse.js +121 -0
- package/dist/core/streaming/sse.js.map +1 -0
- package/dist/core/symbols.d.ts +28 -0
- package/dist/core/symbols.d.ts.map +1 -0
- package/dist/core/symbols.js +28 -0
- package/dist/core/symbols.js.map +1 -0
- package/dist/core/telemetry.d.ts +83 -0
- package/dist/core/telemetry.d.ts.map +1 -0
- package/dist/core/telemetry.js +96 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/uploads/receive.d.ts +63 -0
- package/dist/core/uploads/receive.d.ts.map +1 -0
- package/dist/core/uploads/receive.js +342 -0
- package/dist/core/uploads/receive.js.map +1 -0
- package/dist/core/uploads/types.d.ts +79 -0
- package/dist/core/uploads/types.d.ts.map +1 -0
- package/dist/core/uploads/types.js +2 -0
- package/dist/core/uploads/types.js.map +1 -0
- package/dist/decorators/hooks.d.ts +25 -0
- package/dist/decorators/hooks.d.ts.map +1 -0
- package/dist/decorators/hooks.js +36 -0
- package/dist/decorators/hooks.js.map +1 -0
- package/dist/decorators/http.d.ts +21 -0
- package/dist/decorators/http.d.ts.map +1 -0
- package/dist/decorators/http.js +21 -0
- package/dist/decorators/http.js.map +1 -0
- package/dist/decorators/index.d.ts +7 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +7 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/log.d.ts +10 -0
- package/dist/decorators/log.d.ts.map +1 -0
- package/dist/decorators/log.js +32 -0
- package/dist/decorators/log.js.map +1 -0
- package/dist/decorators/middlewares.d.ts +28 -0
- package/dist/decorators/middlewares.d.ts.map +1 -0
- package/dist/decorators/middlewares.js +40 -0
- package/dist/decorators/middlewares.js.map +1 -0
- package/dist/decorators/router.d.ts +12 -0
- package/dist/decorators/router.d.ts.map +1 -0
- package/dist/decorators/router.js +20 -0
- package/dist/decorators/router.js.map +1 -0
- package/dist/decorators/schema.d.ts +8 -0
- package/dist/decorators/schema.d.ts.map +1 -0
- package/dist/decorators/schema.js +14 -0
- package/dist/decorators/schema.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/middlewares/auth.d.ts +41 -0
- package/dist/middlewares/auth.d.ts.map +1 -0
- package/dist/middlewares/auth.js +74 -0
- package/dist/middlewares/auth.js.map +1 -0
- package/dist/middlewares/cors.d.ts +30 -0
- package/dist/middlewares/cors.d.ts.map +1 -0
- package/dist/middlewares/cors.js +69 -0
- package/dist/middlewares/cors.js.map +1 -0
- package/dist/middlewares/csrf.d.ts +42 -0
- package/dist/middlewares/csrf.d.ts.map +1 -0
- package/dist/middlewares/csrf.js +70 -0
- package/dist/middlewares/csrf.js.map +1 -0
- package/dist/middlewares/index.d.ts +6 -0
- package/dist/middlewares/index.d.ts.map +1 -0
- package/dist/middlewares/index.js +6 -0
- package/dist/middlewares/index.js.map +1 -0
- package/dist/middlewares/rate-limit.d.ts +35 -0
- package/dist/middlewares/rate-limit.d.ts.map +1 -0
- package/dist/middlewares/rate-limit.js +45 -0
- package/dist/middlewares/rate-limit.js.map +1 -0
- package/dist/middlewares/security-headers.d.ts +28 -0
- package/dist/middlewares/security-headers.d.ts.map +1 -0
- package/dist/middlewares/security-headers.js +42 -0
- package/dist/middlewares/security-headers.js.map +1 -0
- package/dist/openapi/generate.d.ts +51 -0
- package/dist/openapi/generate.d.ts.map +1 -0
- package/dist/openapi/generate.js +175 -0
- package/dist/openapi/generate.js.map +1 -0
- package/dist/schema/serialization.d.ts +2 -0
- package/dist/schema/serialization.d.ts.map +1 -0
- package/dist/schema/serialization.js +11 -0
- package/dist/schema/serialization.js.map +1 -0
- package/dist/schema/validation.d.ts +22 -0
- package/dist/schema/validation.d.ts.map +1 -0
- package/dist/schema/validation.js +68 -0
- package/dist/schema/validation.js.map +1 -0
- package/dist/server/static.d.ts +45 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +204 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/tls.d.ts +48 -0
- package/dist/server/tls.d.ts.map +1 -0
- package/dist/server/tls.js +60 -0
- package/dist/server/tls.js.map +1 -0
- package/dist/shims/symbol-metadata.d.ts +2 -0
- package/dist/shims/symbol-metadata.d.ts.map +1 -0
- package/dist/shims/symbol-metadata.js +8 -0
- package/dist/shims/symbol-metadata.js.map +1 -0
- package/dist/utils/content-type.d.ts +13 -0
- package/dist/utils/content-type.d.ts.map +1 -0
- package/dist/utils/content-type.js +75 -0
- package/dist/utils/content-type.js.map +1 -0
- package/dist/utils/cookie.d.ts +34 -0
- package/dist/utils/cookie.d.ts.map +1 -0
- package/dist/utils/cookie.js +94 -0
- package/dist/utils/cookie.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/mime.d.ts +2 -0
- package/dist/utils/mime.d.ts.map +1 -0
- package/dist/utils/mime.js +52 -0
- package/dist/utils/mime.js.map +1 -0
- package/dist/utils/origin-match.d.ts +58 -0
- package/dist/utils/origin-match.d.ts.map +1 -0
- package/dist/utils/origin-match.js +194 -0
- package/dist/utils/origin-match.js.map +1 -0
- package/dist/utils/path.d.ts +10 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +60 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/query.d.ts +28 -0
- package/dist/utils/query.d.ts.map +1 -0
- package/dist/utils/query.js +67 -0
- package/dist/utils/query.js.map +1 -0
- package/dist/utils/request-target.d.ts +31 -0
- package/dist/utils/request-target.d.ts.map +1 -0
- package/dist/utils/request-target.js +80 -0
- package/dist/utils/request-target.js.map +1 -0
- package/dist/utils/safe-json.d.ts +24 -0
- package/dist/utils/safe-json.d.ts.map +1 -0
- package/dist/utils/safe-json.js +83 -0
- package/dist/utils/safe-json.js.map +1 -0
- package/dist/utils/trace-context.d.ts +17 -0
- package/dist/utils/trace-context.d.ts.map +1 -0
- package/dist/utils/trace-context.js +30 -0
- package/dist/utils/trace-context.js.map +1 -0
- package/package.json +85 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Caio Fernandes
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# FlowingTS
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Micro-framework HTTP para Node.js com **decorators nativos (TC39)**, **schema-first** e **seguro por padrão**.
|
|
4
|
+
|
|
5
|
+
> **Por que FlowingTS:** num projeto Express que passa por muitas mãos, cada pessoa declara rotas do seu jeito, e quem chega precisa reaprender cada pedaço. O NestJS resolve a padronização, mas traz junto uma estrutura que muitos projetos não precisam. O FlowingTS dá **uma forma só de declarar rotas**, que qualquer pessoa da equipe reconhece, com o contrato de cada rota validado como no Fastify e sem tirar a liberdade do Express. O código flui igual, não importa quem o escreveu.
|
|
6
|
+
|
|
7
|
+
FlowingTS combina a flexibilidade do **Fastify** e do **Express** (middlewares simples, partes plugáveis, pouca cerimônia) com a organização em classes do **NestJS**, mantendo o núcleo pequeno: `node:http` direto e sete dependências de runtime.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { type Context, createApp, Post, Router, type RouteSchemas, Type } from 'flowingts';
|
|
11
|
+
|
|
12
|
+
const User = Type.Object({ id: Type.String(), name: Type.String() });
|
|
13
|
+
const create = {
|
|
14
|
+
body: Type.Object({ name: Type.String({ minLength: 1 }) }, { additionalProperties: false }),
|
|
15
|
+
response: { 201: User },
|
|
16
|
+
} satisfies RouteSchemas;
|
|
17
|
+
|
|
18
|
+
@Router('/users')
|
|
19
|
+
class UserRoutes {
|
|
20
|
+
@Post('/', create)
|
|
21
|
+
create(ctx: Context<typeof create>) {
|
|
22
|
+
return { id: crypto.randomUUID(), name: ctx.body.name }; // ctx.body tipado; responde 201
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const app = createApp().mount(UserRoutes);
|
|
27
|
+
await app.listen({ port: 3000 });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Documentação
|
|
31
|
+
|
|
32
|
+
**[code-cfernandes.github.io/flowing](https://code-cfernandes.github.io/flowing/)**
|
|
33
|
+
|
|
34
|
+
| | |
|
|
35
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
36
|
+
| [Introdução](https://code-cfernandes.github.io/flowing/guia/introducao) e [instalação](https://code-cfernandes.github.io/flowing/guia/instalacao) | Princípios, requisitos e o primeiro app |
|
|
37
|
+
| [Rotas e composição](https://code-cfernandes.github.io/flowing/guia/rotas) | `@Router`, `app.mount()`, `app.group()`, virtual hosts e mapeamento manual |
|
|
38
|
+
| [Schemas](https://code-cfernandes.github.io/flowing/guia/schemas) e [respostas](https://code-cfernandes.github.io/flowing/guia/respostas) | Validação, serialização, tipos do `ctx` e outros formatos |
|
|
39
|
+
| [Middlewares e guards](https://code-cfernandes.github.io/flowing/guia/middlewares-e-guards), [hooks](https://code-cfernandes.github.io/flowing/guia/hooks) e [erros](https://code-cfernandes.github.io/flowing/guia/erros) | O pipeline da requisição |
|
|
40
|
+
| [Estáticos e streaming](https://code-cfernandes.github.io/flowing/guia/estaticos-e-streaming) | Arquivos, SSE, NDJSON e `ctx.signal` |
|
|
41
|
+
| [OpenAPI](https://code-cfernandes.github.io/flowing/guia/openapi), [logs](https://code-cfernandes.github.io/flowing/guia/logs) e [observabilidade](https://code-cfernandes.github.io/flowing/guia/observabilidade) | Documentação da API, logs correlacionados e OpenTelemetry |
|
|
42
|
+
| [Segurança](https://code-cfernandes.github.io/flowing/guia/seguranca), [ciclo de vida](https://code-cfernandes.github.io/flowing/guia/ciclo-de-vida) e [deploy](https://code-cfernandes.github.io/flowing/guia/deploy) | Defaults seguros, HTTPS, healthchecks, shutdown e produção |
|
|
43
|
+
| [Referência](https://code-cfernandes.github.io/flowing/referencia/create-app) | `createApp(options)` e a API completa, gerada do código |
|
|
44
|
+
|
|
45
|
+
## Instalação
|
|
46
|
+
|
|
47
|
+
- **Node.js 22+**
|
|
48
|
+
- **TypeScript 5.2+**, com decorators **padrão**. **Não** use `experimentalDecorators` nem `emitDecoratorMetadata`: se estiverem ligados, o FlowingTS lança um erro dizendo como corrigir. Por isso ele não convive no mesmo projeto com bibliotecas que exigem decorators legados (NestJS, TypeORM…).
|
|
49
|
+
- ESM (`"type": "module"`).
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm add flowingts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
O `tsconfig.json` mínimo e os detalhes estão em [Instalação](https://code-cfernandes.github.io/flowing/guia/instalacao).
|
|
56
|
+
|
|
57
|
+
## Desenvolvimento
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pnpm install
|
|
61
|
+
pnpm test # node:test + tsx
|
|
62
|
+
pnpm test:coverage # com limites mínimos de cobertura
|
|
63
|
+
pnpm typecheck
|
|
64
|
+
pnpm check # lint + formatação (Biome); pnpm format corrige a formatação
|
|
65
|
+
pnpm build # dist/
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Para o `git blame` ignorar o commit de formatação: `git config blame.ignoreRevsFile .git-blame-ignore-revs`.
|
|
69
|
+
|
|
70
|
+
**Publicação:** um push de tag de versão (`git tag v1.2.3 && git push origin v1.2.3`) dispara o workflow `release.yml`. Ele confere que a tag bate com o `package.json`, que o commit está na `main` e que o CHANGELOG tem a seção da versão. Depois roda as mesmas verificações do CI e envia o pacote ao npm em estágio, por Trusted Publishing, sem token e com provenance, além de criar a GitHub Release com as notas do CHANGELOG. A versão só fica pública depois da aprovação no npmjs.com, com o 2FA da conta.
|
|
71
|
+
|
|
72
|
+
Mudanças notáveis estão no [CHANGELOG](CHANGELOG.md). Para reportar uma vulnerabilidade e conhecer a política de dependências, veja o [SECURITY.md](SECURITY.md).
|
|
73
|
+
|
|
74
|
+
## Licença
|
|
75
|
+
|
|
76
|
+
[ISC](LICENSE)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Etapa em que a requisição estava quando um erro chegou ao `onError` (F-38). No modelo onion, um erro
|
|
3
|
+
* lançado depois do `await next()` de um middleware aparece com a etapa mais recente (`handler`, `response`).
|
|
4
|
+
* `validation` inclui a leitura do body (JSON inválido, limite de tamanho).
|
|
5
|
+
*/
|
|
6
|
+
export type ErrorPhase = 'middleware' | 'guard' | 'validation' | 'handler' | 'response';
|
|
7
|
+
/** Por que o `ctx.signal` de uma requisição foi cancelado (F-38). */
|
|
8
|
+
export type AbortCode = 'CLIENT_DISCONNECTED' | 'SHUTDOWN' | 'TIMEOUT';
|
|
9
|
+
/**
|
|
10
|
+
* O `ctx.signal.reason` de uma requisição cancelada. Use o `code` para decidir em código:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* if (ctx.signal.reason?.code === 'SHUTDOWN') return; // o app está encerrando, não é erro
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* `TIMEOUT` está reservado para um futuro timeout por rota.
|
|
17
|
+
*/
|
|
18
|
+
export declare class RequestAbortedError extends Error {
|
|
19
|
+
readonly code: AbortCode;
|
|
20
|
+
readonly name = "RequestAbortedError";
|
|
21
|
+
constructor(code: AbortCode);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=abort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/core/abort.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC;AAExF,qEAAqE;AACrE,MAAM,MAAM,SAAS,GAAG,qBAAqB,GAAG,UAAU,GAAG,SAAS,CAAC;AAQvE;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAEhC,QAAQ,CAAC,IAAI,EAAE,SAAS;IADpC,SAAkB,IAAI,yBAAyB;IAC/C,YAAqB,IAAI,EAAE,SAAS,EAEnC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const MESSAGES = {
|
|
2
|
+
CLIENT_DISCONNECTED: 'client disconnected',
|
|
3
|
+
SHUTDOWN: 'server shutting down',
|
|
4
|
+
TIMEOUT: 'request timed out',
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* O `ctx.signal.reason` de uma requisição cancelada. Use o `code` para decidir em código:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* if (ctx.signal.reason?.code === 'SHUTDOWN') return; // o app está encerrando, não é erro
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* `TIMEOUT` está reservado para um futuro timeout por rota.
|
|
14
|
+
*/
|
|
15
|
+
export class RequestAbortedError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
name = 'RequestAbortedError';
|
|
18
|
+
constructor(code) {
|
|
19
|
+
super(MESSAGES[code]);
|
|
20
|
+
this.code = code;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=abort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/core/abort.ts"],"names":[],"mappings":"AAUA,MAAM,QAAQ,GAA8B;IAC1C,mBAAmB,EAAE,qBAAqB;IAC1C,QAAQ,EAAE,sBAAsB;IAChC,OAAO,EAAE,mBAAmB;CAC7B,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAEvB,IAAI;IADP,IAAI,GAAG,qBAAqB,CAAC;IAC/C,YAAqB,IAAe;QAClC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;oBADH,IAAI;IAEzB,CAAC;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { UploadSettings } from '../uploads/receive.ts';
|
|
2
|
+
import type http from 'node:http';
|
|
3
|
+
import type { Middleware } from '../compose.ts';
|
|
4
|
+
import type { OnSendHook } from '../context.ts';
|
|
5
|
+
import type { Logger } from '../logger.ts';
|
|
6
|
+
import type { Telemetry } from '../telemetry.ts';
|
|
7
|
+
import type { Serializer } from '../response/pipeline.ts';
|
|
8
|
+
import type { AppOptions, OnCloseHook, OnErrorHook, OnRequestHook, OnResponseHook } from './options.ts';
|
|
9
|
+
import type { Contracts } from '../contracts.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Estado compartilhado do app entre a fachada (`FlowingApp`) e os componentes internos (registro,
|
|
12
|
+
* servidor, pipeline da requisição). Só configuração e listas de hooks; nada por requisição.
|
|
13
|
+
*/
|
|
14
|
+
export interface AppCore {
|
|
15
|
+
readonly opts: AppOptions;
|
|
16
|
+
readonly log: Logger;
|
|
17
|
+
readonly middlewares: Middleware[];
|
|
18
|
+
readonly errorHandlers: OnErrorHook[];
|
|
19
|
+
readonly onSendHooks: OnSendHook[];
|
|
20
|
+
readonly onRequestHooks: OnRequestHook[];
|
|
21
|
+
/** O motor dos contratos (F-27): o do app, ou o nativo. */
|
|
22
|
+
readonly contracts: Contracts;
|
|
23
|
+
/** OpenTelemetry, quando `createApp({ telemetry })` (F-38). */
|
|
24
|
+
readonly telemetry: Telemetry | undefined;
|
|
25
|
+
readonly onResponseHooks: OnResponseHook[];
|
|
26
|
+
/** Rotas com `onResponse` de escopo (F-43): basta uma para o observer acompanhar o fim das requisições. */
|
|
27
|
+
scopedOnResponse: number;
|
|
28
|
+
readonly onCloseHooks: OnCloseHook[];
|
|
29
|
+
readonly serializers: ReadonlyMap<string, Serializer>;
|
|
30
|
+
/** Configuração de upload (F-46), resolvida na criação do app. */
|
|
31
|
+
readonly uploads: UploadSettings;
|
|
32
|
+
readonly allowedHosts: ((hostname: string, host: string) => boolean) | undefined;
|
|
33
|
+
readonly applySecurityHeaders: ((res: http.ServerResponse) => void) | undefined;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../src/core/app/core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,UAAU,EAAE,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,aAAa,EAAE,CAAC;IACzC,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,eAAe,EAAE,cAAc,EAAE,CAAC;IAC3C,2GAA2G;IAC3G,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;IACjF,QAAQ,CAAC,oBAAoB,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACjF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/core/app/core.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { UploadsOptions } from '../uploads/types.ts';
|
|
2
|
+
import type { AbortCode, ErrorPhase } from '../abort.ts';
|
|
3
|
+
import type { TelemetryApi } from '../telemetry.ts';
|
|
4
|
+
import type { Context } from '../context.ts';
|
|
5
|
+
import type { RouteInfo } from '../route-table.ts';
|
|
6
|
+
import type { ErrorFormatter } from '../errors.ts';
|
|
7
|
+
import type { Logger } from '../logger.ts';
|
|
8
|
+
import type { Serializer } from '../response/pipeline.ts';
|
|
9
|
+
import type { ShutdownOptions } from '../shutdown.ts';
|
|
10
|
+
import type { TlsOptions } from '../../server/tls.ts';
|
|
11
|
+
import type { ProtoPoisoning } from '../../utils/safe-json.ts';
|
|
12
|
+
import type { SecurityHeadersOptions, SecurityPreset } from '../../middlewares/security-headers.ts';
|
|
13
|
+
import type { QueryParser } from '../../utils/query.ts';
|
|
14
|
+
import type { BodyParser } from '../body-parser.ts';
|
|
15
|
+
import type { Contracts } from '../contracts.ts';
|
|
16
|
+
/**
|
|
17
|
+
* Tratamento de erros (F-04). `phase` diz em que etapa a requisição estava: `middleware`, `guard`,
|
|
18
|
+
* `validation` (inclui a leitura do body), `handler` ou `response`.
|
|
19
|
+
*/
|
|
20
|
+
export type OnErrorHook = (err: unknown, ctx: Context, phase: ErrorPhase) => void | Promise<void>;
|
|
21
|
+
/** Como a requisição terminou, no `onResponse` (F-04, F-38). */
|
|
22
|
+
export interface ResponseInfo {
|
|
23
|
+
/** Status enviado (num cancelamento, o que estava definido quando a conexão caiu). */
|
|
24
|
+
status: number;
|
|
25
|
+
/** Duração total em milissegundos, até o fim da resposta ou o cancelamento. */
|
|
26
|
+
ms: number;
|
|
27
|
+
/** `completed`, `aborted` (cancelada antes do fim) ou `error` (respondida com 5xx). */
|
|
28
|
+
outcome: 'completed' | 'aborted' | 'error';
|
|
29
|
+
/** Por que foi cancelada, quando `outcome` é `aborted`. */
|
|
30
|
+
abortReason?: AbortCode;
|
|
31
|
+
/**
|
|
32
|
+
* Milissegundos até os headers saírem. Em SSE e NDJSON, é a latência real; `ms` mede a conexão inteira.
|
|
33
|
+
* Medido nos servidores do `listen()` e no `inject()`; ausente num servidor próprio (`callback()`).
|
|
34
|
+
*/
|
|
35
|
+
ttfbMs?: number;
|
|
36
|
+
/** Resposta em stream: SSE, NDJSON, `Readable` ou arquivo. */
|
|
37
|
+
streaming: boolean;
|
|
38
|
+
/** A rota que atendeu; ausente em 404, 405, healthcheck e OpenAPI. */
|
|
39
|
+
route?: RouteInfo;
|
|
40
|
+
/** O erro lançado, quando a resposta veio do tratamento de erros. */
|
|
41
|
+
error?: unknown;
|
|
42
|
+
/** A etapa em que o erro chegou (a mesma do 3º argumento do `onError`). */
|
|
43
|
+
errorPhase?: ErrorPhase;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Depois que a requisição termina (F-04). Roda **exatamente uma vez** por requisição, em todos os caminhos:
|
|
47
|
+
* sucesso, erro, 404/405, `Host` inválido, cancelamento pelo cliente, shutdown, streams e `inject()`.
|
|
48
|
+
*/
|
|
49
|
+
export type OnResponseHook = (ctx: Context, info: ResponseInfo) => void | Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* No início de cada requisição, antes da validação do `Host` e de qualquer middleware (F-38). É síncrono:
|
|
52
|
+
* uma Promise devolvida não é esperada. Erros são logados e não interrompem a requisição.
|
|
53
|
+
*/
|
|
54
|
+
export type OnRequestHook = (ctx: Context) => void;
|
|
55
|
+
/** No `close()` do app (F-04). */
|
|
56
|
+
export type OnCloseHook = () => void | Promise<void>;
|
|
57
|
+
export interface ListenOptions {
|
|
58
|
+
/** Padrão: 3000. Use 0 para uma porta livre aleatória. */
|
|
59
|
+
port?: number;
|
|
60
|
+
/** Padrão: `127.0.0.1`. Use `0.0.0.0` para aceitar conexões externas (ex.: em containers). */
|
|
61
|
+
host?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Sobe em HTTPS, com os mesmos timeouts, encerramento gracioso e `close()` do HTTP. Atrás de um
|
|
64
|
+
* proxy que termina o TLS (Caddy, Nginx, load balancer), não use: fique em HTTP com `trustProxy`.
|
|
65
|
+
*/
|
|
66
|
+
tls?: TlsOptions;
|
|
67
|
+
}
|
|
68
|
+
export interface ServerTimeouts {
|
|
69
|
+
/** Tempo máximo para receber a requisição inteira. Padrão: 30s. */
|
|
70
|
+
requestTimeout?: number;
|
|
71
|
+
/** Tempo máximo para receber os headers (mitiga Slowloris). Padrão: 10s. */
|
|
72
|
+
headersTimeout?: number;
|
|
73
|
+
/** Tempo de conexão keep-alive ociosa. Padrão: 5s. */
|
|
74
|
+
keepAliveTimeout?: number;
|
|
75
|
+
}
|
|
76
|
+
export interface AppOptions {
|
|
77
|
+
/**
|
|
78
|
+
* OpenTelemetry nativo (F-38): passe o `@opentelemetry/api` que a aplicação usa (`import * as otel`).
|
|
79
|
+
* Enriquece o span da auto-instrumentação HTTP (ou cria o span SERVER, se não houver), com o template da
|
|
80
|
+
* rota e os erros com a fase, e gera as métricas HTTP. Sem esta opção, nada é carregado.
|
|
81
|
+
*/
|
|
82
|
+
telemetry?: TelemetryApi;
|
|
83
|
+
/** Tamanho máximo do body JSON em bytes. Padrão: 1 MiB. */
|
|
84
|
+
bodyLimit?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Headers de segurança aplicados a toda resposta (S-04). Padrão: `'api'`.
|
|
87
|
+
* `'web'` libera recursos do próprio site (CSP `'self'`) para quem serve páginas; `false` desliga.
|
|
88
|
+
*/
|
|
89
|
+
securityHeaders?: SecurityHeadersOptions | SecurityPreset | false;
|
|
90
|
+
/**
|
|
91
|
+
* Serializadores por content type, para responder em outros formatos (F-31):
|
|
92
|
+
* `{ 'application/yaml': (data) => YAML.stringify(data) }`. JSON e valores crus já são nativos.
|
|
93
|
+
*/
|
|
94
|
+
serializers?: Record<string, Serializer>;
|
|
95
|
+
/** Upload de arquivos (F-46): limites globais, pasta dos temporários e detector de conteúdo. */
|
|
96
|
+
uploads?: UploadsOptions;
|
|
97
|
+
/** Timeouts do servidor HTTP (S-06). */
|
|
98
|
+
timeouts?: ServerTimeouts;
|
|
99
|
+
/**
|
|
100
|
+
* Modo estrito (S-07): exige schema de `body` em POST/PUT/PATCH e schema de `response`
|
|
101
|
+
* em toda rota, falhando no `app.mount()`. Padrão: `false`.
|
|
102
|
+
*/
|
|
103
|
+
requireSchemas?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Body JSON com `__proto__` ou `constructor.prototype` (S-08): `'error'` responde 400
|
|
106
|
+
* (`PROTO_POISONING`); `'remove'` descarta as chaves e segue. Padrão: `'error'`.
|
|
107
|
+
*/
|
|
108
|
+
protoPoisoning?: ProtoPoisoning;
|
|
109
|
+
/** Máximo de pares chave/valor lidos da query string. Padrão: 1000 (S-06). */
|
|
110
|
+
queryMaxKeys?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Parser de query próprio (F-26), para sintaxes aninhadas como `?filter[status]=ativo`:
|
|
113
|
+
* `createApp({ queryParser: (raw) => qs.parse(raw, { depth: 3, arrayLimit: 50, parameterLimit: 1000 }) })`.
|
|
114
|
+
* O retorno passa pelo filtro de chaves perigosas; se o parser lançar, a resposta é 400. Os limites ficam com o
|
|
115
|
+
* parser: o `queryMaxKeys` vale só para o padrão.
|
|
116
|
+
*/
|
|
117
|
+
queryParser?: QueryParser;
|
|
118
|
+
/**
|
|
119
|
+
* O motor dos contratos das rotas (F-27), um para o app inteiro. Sem a opção, `contracts.native()` (schemas
|
|
120
|
+
* TypeBox). Para Zod, Valibot ou ArkType: `contracts.standard()`. Um schema de outro motor é recusado no registro.
|
|
121
|
+
*/
|
|
122
|
+
contracts?: Contracts;
|
|
123
|
+
/**
|
|
124
|
+
* Parsers de body por tipo de mídia (F-12), além do JSON nativo:
|
|
125
|
+
* `createApp({ parsers: { 'application/yaml': (raw) => YAML.parse(raw.toString('utf8')) } })`.
|
|
126
|
+
* O `bodyLimit` e a proteção contra prototype pollution valem para todos, e o contrato da rota valida o resultado.
|
|
127
|
+
* Atenção: tipos que o navegador envia sem preflight (`application/x-www-form-urlencoded`, `text/plain`) abrem
|
|
128
|
+
* espaço para CSRF; use o `csrf()` em rotas com sessão por cookie.
|
|
129
|
+
*/
|
|
130
|
+
parsers?: Readonly<Record<string, BodyParser>>;
|
|
131
|
+
/** Logger compatível com pino. Padrão: `consoleLogger()`; `false` desliga (F-06). */
|
|
132
|
+
logger?: Logger | false;
|
|
133
|
+
/** Loga cada requisição concluída (método, path, status, duração) em nível `info`. Padrão: `false`. */
|
|
134
|
+
logRequests?: boolean;
|
|
135
|
+
/** Request id (F-06). */
|
|
136
|
+
requestId?: RequestIdOptions;
|
|
137
|
+
/**
|
|
138
|
+
* Encerramento gracioso nos sinais do processo, ativado pelo `listen()` (F-07).
|
|
139
|
+
* Ligado por padrão (SIGINT/SIGTERM, 10s); `false` desliga.
|
|
140
|
+
*/
|
|
141
|
+
shutdown?: ShutdownOptions | false;
|
|
142
|
+
/**
|
|
143
|
+
* Confia nos headers `X-Forwarded-For`, `-Host` e `-Proto` para `ctx.ip`, `ctx.host` e `ctx.protocol`
|
|
144
|
+
* (S-09). Padrão: `false`. Use `true` para confiar em toda a cadeia, ou o número de proxies à frente
|
|
145
|
+
* do app (ex.: `1` para um load balancer). Só ligue atrás de um proxy que reescreve esses headers.
|
|
146
|
+
*/
|
|
147
|
+
trustProxy?: boolean | number;
|
|
148
|
+
/**
|
|
149
|
+
* Hosts aceitos (S-09). Requisições com outro `Host` (ou `X-Forwarded-Host` confiável) recebem 400.
|
|
150
|
+
* Protege contra host header injection e DNS rebinding. Padrão: qualquer host válido.
|
|
151
|
+
* - `'api.exemplo.com'` casa com o hostname (qualquer porta); `'api.exemplo.com:8443'` exige a porta;
|
|
152
|
+
* - `'*.exemplo.dev'` aceita só subdomínios;
|
|
153
|
+
* - uma RegExp precisa casar o hostname **inteiro** (como se tivesse `^…$`).
|
|
154
|
+
*/
|
|
155
|
+
allowedHosts?: readonly (string | RegExp)[];
|
|
156
|
+
/**
|
|
157
|
+
* Rotas com `host` (virtual hosts) fora do `allowedHosts` nunca são alcançadas (F-35). `'error'` (padrão)
|
|
158
|
+
* falha no registro; `'warn'` só registra um aviso no log; `'off'` não confere.
|
|
159
|
+
*/
|
|
160
|
+
unreachableHosts?: 'error' | 'warn' | 'off';
|
|
161
|
+
/**
|
|
162
|
+
* Personaliza o corpo de todos os erros do framework e dos `HttpError` (F-15). Padrão: Problem
|
|
163
|
+
* Details (RFC 9457) em `application/problem+json`. O retorno é enviado como `application/json`.
|
|
164
|
+
*/
|
|
165
|
+
formatError?: ErrorFormatter;
|
|
166
|
+
}
|
|
167
|
+
export interface RequestIdOptions {
|
|
168
|
+
/** Header lido da requisição e devolvido na resposta. Padrão: `x-request-id`. `false` não envia. */
|
|
169
|
+
header?: string | false;
|
|
170
|
+
/**
|
|
171
|
+
* Reaproveita o id recebido no header (útil atrás de um gateway). Padrão: `true`.
|
|
172
|
+
* O valor só é aceito se tiver até 128 caracteres de `[A-Za-z0-9._:-]`, para evitar log injection.
|
|
173
|
+
*/
|
|
174
|
+
trustIncoming?: boolean;
|
|
175
|
+
/** Gerador de ids. Padrão: `crypto.randomUUID()`. */
|
|
176
|
+
generate?: () => string;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/core/app/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/core/app/options.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type http from 'node:http';
|
|
2
|
+
import type https from 'node:https';
|
|
3
|
+
import '../shims/symbol-metadata.ts';
|
|
4
|
+
import type { TlsOptions } from '../server/tls.ts';
|
|
5
|
+
import { type GroupOptions, type MountOptions, type RouteScope, type RouterRef } from './compiler.ts';
|
|
6
|
+
import { MountedGroup, RouteGroupScope } from './mounted-group.ts';
|
|
7
|
+
import type { Middleware } from './compose.ts';
|
|
8
|
+
import type { Context, OnSendHook } from './context.ts';
|
|
9
|
+
import { type StaticOptions } from '../server/static.ts';
|
|
10
|
+
import { type OpenAPIOptions } from '../openapi/generate.ts';
|
|
11
|
+
import { type HealthOptions } from './health.ts';
|
|
12
|
+
import { type InjectOptions, type InjectResponse } from './inject.ts';
|
|
13
|
+
import { type Logger } from './logger.ts';
|
|
14
|
+
import type { AppOptions, ListenOptions, OnCloseHook, OnErrorHook, OnRequestHook, OnResponseHook } from './app/options.ts';
|
|
15
|
+
export type { AppOptions, ListenOptions, OnCloseHook, OnErrorHook, OnRequestHook, OnResponseHook, RequestIdOptions, ResponseInfo, ServerTimeouts, } from './app/options.ts';
|
|
16
|
+
/**
|
|
17
|
+
* O app. É uma fachada (Facade) sobre o registro de rotas, as rotas do próprio framework (health, OpenAPI),
|
|
18
|
+
* o servidor e o pipeline da requisição: a API pública fica aqui, e o trabalho fica nesses componentes.
|
|
19
|
+
*/
|
|
20
|
+
export declare class FlowingApp {
|
|
21
|
+
readonly log: Logger;
|
|
22
|
+
private readonly core;
|
|
23
|
+
private readonly registry;
|
|
24
|
+
private readonly framework;
|
|
25
|
+
private readonly runtime;
|
|
26
|
+
private readonly requests;
|
|
27
|
+
constructor(opts?: AppOptions);
|
|
28
|
+
use(mw: Middleware): this;
|
|
29
|
+
onError(h: OnErrorHook): this;
|
|
30
|
+
/**
|
|
31
|
+
* Roda imediatamente antes de escrever uma resposta com corpo (exceto streams), inclusive em erros
|
|
32
|
+
* e 404. É síncrono: pode alterar headers (ETag, assinatura) ou devolver `{ body, contentType }`
|
|
33
|
+
* novos. Se lançar, o erro é logado e a resposta original é enviada (F-04).
|
|
34
|
+
*/
|
|
35
|
+
onSend(hook: OnSendHook): this;
|
|
36
|
+
/**
|
|
37
|
+
* Roda no início de cada requisição, antes da validação do `Host` e dos middlewares: abrir um span, contar
|
|
38
|
+
* requisições ativas. Toda requisição que passa por aqui também passa pelo `onResponse`. É síncrono; erros
|
|
39
|
+
* são só logados (F-38).
|
|
40
|
+
*/
|
|
41
|
+
onRequest(hook: OnRequestHook): this;
|
|
42
|
+
/**
|
|
43
|
+
* Roda exatamente uma vez quando a requisição termina, com status, duração, `outcome` (`completed`,
|
|
44
|
+
* `aborted`, `error`), motivo do cancelamento, tempo até os headers, rota e se foi stream: métricas,
|
|
45
|
+
* tracing, auditoria. Pode ser assíncrono; erros são só logados (F-04, F-38).
|
|
46
|
+
*/
|
|
47
|
+
onResponse(hook: OnResponseHook): this;
|
|
48
|
+
/**
|
|
49
|
+
* Roda no `close()`, depois que o servidor fecha, qualquer que seja a origem (sinal, teste, código),
|
|
50
|
+
* e antes do `shutdown.onShutdown`. Erros são logados e fazem o `close()` rejeitar ao final (F-04).
|
|
51
|
+
*/
|
|
52
|
+
onClose(hook: OnCloseHook): this;
|
|
53
|
+
/**
|
|
54
|
+
* Registra uma classe com `@Router` (F-28). Aceita a classe (construtor sem argumentos) ou uma
|
|
55
|
+
* instância pronta (B-10), e opções aplicadas no ponto de composição:
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* app
|
|
59
|
+
* .mount(HealthRoutes)
|
|
60
|
+
* .mount(new UserRoutes(repo), { prefix: '/v2/users', middlewares: [auth], except: (r) => [r.remove] })
|
|
61
|
+
* .get('/legacy/:id', (r) => r.getUser); // mapeamento manual (F-30)
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
mount<T extends object>(ref: new () => T, options?: MountOptions<T>): MountedGroup<T>;
|
|
65
|
+
mount<T extends object>(ref: T extends abstract new (...args: never) => unknown ? never : T, options?: MountOptions<T>): MountedGroup<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Agrupa `mount()`s sob um prefixo, middlewares e guards comuns. Não é um container: é só composição,
|
|
68
|
+
* com a mesma semântica do `mount()`. Grupos podem ser aninhados.
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* app.group({ prefix: '/v2', middlewares: [authenticate], guards: [isAdmin] }, (g) =>
|
|
72
|
+
* g.mount(UserRoutes).mount(new OrderRoutes(db)),
|
|
73
|
+
* );
|
|
74
|
+
* ```
|
|
75
|
+
* Ordem de execução: middlewares dos grupos (de fora para dentro) → do `mount()` → da classe → do método.
|
|
76
|
+
*/
|
|
77
|
+
group(options: GroupOptions, define: (group: RouteGroupScope) => unknown): this;
|
|
78
|
+
/** @internal */
|
|
79
|
+
groupIn(parent: RouteScope, options: GroupOptions, define: (group: RouteGroupScope) => unknown): void;
|
|
80
|
+
/**
|
|
81
|
+
* Serve os arquivos de `root` em `GET prefix/*` (F-13). É uma rota como as outras: vale `group()`
|
|
82
|
+
* (host, guards, middlewares), 404/405 e a checagem de conflitos. Não entra no OpenAPI.
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* app.static('/assets', './public', { maxAge: '7d', immutable: true });
|
|
86
|
+
* app.static('/', './dist', { fallback: 'index.html' }); // SPA
|
|
87
|
+
* ```
|
|
88
|
+
* Caminhos que saem da raiz (`..`, symlinks), arquivos ocultos e diretórios sem índice respondem 404.
|
|
89
|
+
*/
|
|
90
|
+
static(prefix: string, root: string, opts?: StaticOptions): this;
|
|
91
|
+
/** @internal */
|
|
92
|
+
staticIn(scope: RouteScope, prefix: string, root: string, opts: StaticOptions): void;
|
|
93
|
+
/** @internal */
|
|
94
|
+
mountIn(scope: RouteScope, ref: RouterRef, options?: MountOptions): MountedGroup<object>;
|
|
95
|
+
/**
|
|
96
|
+
* Serve o documento OpenAPI 3.1 gerado das rotas (`/openapi.json`) e, com `docs`, o Swagger UI (F-16).
|
|
97
|
+
* As duas rotas passam pelos middlewares globais e pelos `middlewares`/`guards` informados aqui:
|
|
98
|
+
* `app.openapi({ guards: [isAdmin] })`. O documento é gerado na primeira requisição, depois dos `mount()`.
|
|
99
|
+
*/
|
|
100
|
+
openapi(opts?: OpenAPIOptions): this;
|
|
101
|
+
/**
|
|
102
|
+
* Healthchecks para load balancers e orquestradores (Kubernetes, ECS…).
|
|
103
|
+
* - `GET /livez`: o processo está de pé (200 sempre).
|
|
104
|
+
* - `GET /readyz`: pronto para receber tráfego. Roda os `checks` (banco, fila…) e responde 503 se algum
|
|
105
|
+
* falhar ou estourar o tempo, e também durante o encerramento.
|
|
106
|
+
*
|
|
107
|
+
* As rotas são servidas **antes** dos middlewares globais (sondas não têm credenciais nem devem
|
|
108
|
+
* consumir rate limit); use `guards`/`middlewares` aqui para restringir.
|
|
109
|
+
*/
|
|
110
|
+
health(opts?: HealthOptions): this;
|
|
111
|
+
/**
|
|
112
|
+
* Documento OpenAPI 3.1 das rotas registradas até agora, para gravar em arquivo ou testar (F-16).
|
|
113
|
+
* Com virtual hosts, é por host (F-35): `{ host: 'api.exemplo.com' }` traz as rotas desse host e as sem
|
|
114
|
+
* host; sem `host`, só as sem host. Domínios diferentes nunca se misturam num mesmo documento.
|
|
115
|
+
*/
|
|
116
|
+
openapiDocument(opts?: OpenAPIOptions): Record<string, unknown>;
|
|
117
|
+
/** Executa o pipeline completo para um contexto já criado. */
|
|
118
|
+
handle(ctx: Context): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Listener `(req, res)` do app, para usar com qualquer servidor do Node:
|
|
121
|
+
* `https.createServer(opts, app.callback())`, `http2` em modo compat, testes etc. (F-09).
|
|
122
|
+
*/
|
|
123
|
+
callback(): (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
124
|
+
/** Sobe o servidor HTTP (ou HTTPS, com `tls`) e resolve quando ele está aceitando conexões (F-09, F-33). */
|
|
125
|
+
listen(options: ListenOptions & {
|
|
126
|
+
tls: TlsOptions;
|
|
127
|
+
}): Promise<https.Server>;
|
|
128
|
+
listen(options?: number | ListenOptions): Promise<http.Server>;
|
|
129
|
+
/**
|
|
130
|
+
* Troca os certificados sem reiniciar nem derrubar conexões (renovação, ACME externo). Conexões novas
|
|
131
|
+
* usam os novos; as abertas seguem com os antigos. Um certificado inválido lança erro e mantém os atuais.
|
|
132
|
+
*/
|
|
133
|
+
reloadTLS(tls: TlsOptions): void;
|
|
134
|
+
/**
|
|
135
|
+
* Para de aceitar conexões, encerra as ociosas e espera as requisições em andamento terminarem.
|
|
136
|
+
* Depois de `graceMs`, força o fechamento das restantes.
|
|
137
|
+
*/
|
|
138
|
+
close(graceMs?: number): Promise<void>;
|
|
139
|
+
/** Executa uma requisição em memória, sem rede. Ideal para testes (F-09). */
|
|
140
|
+
inject(opts: InjectOptions | string): Promise<InjectResponse>;
|
|
141
|
+
}
|
|
142
|
+
export declare function createApp(opts?: AppOptions): FlowingApp;
|
|
143
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"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"}
|