flowingts 0.0.0 → 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 +74 -3
- 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 -6
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { kContracts, kRawSend } from '../symbols.js';
|
|
2
|
+
import { STATUS_CODES } from 'node:http';
|
|
3
|
+
import { HttpError } from '../errors.js';
|
|
4
|
+
import { isResponseContent } from '../metadata.js';
|
|
5
|
+
import { Readable } from 'node:stream';
|
|
6
|
+
import { isJsonContentType, mediaType, negotiate, withCharset } from '../../utils/content-type.js';
|
|
7
|
+
const JSON_TYPE = 'application/json';
|
|
8
|
+
/** Status padrão de uma rota (B-05): com um único 2xx declarado e sem 200, é esse 2xx. */
|
|
9
|
+
export function defaultStatusFor(response) {
|
|
10
|
+
if (!response)
|
|
11
|
+
return undefined;
|
|
12
|
+
const success = Object.keys(response)
|
|
13
|
+
.map(Number)
|
|
14
|
+
.filter((st) => st >= 200 && st < 300);
|
|
15
|
+
return success.length === 1 && response[200] === undefined ? success[0] : undefined;
|
|
16
|
+
}
|
|
17
|
+
export function responsePlan(response, serializers) {
|
|
18
|
+
return { response, serializers, defaultStatus: defaultStatusFor(response) };
|
|
19
|
+
}
|
|
20
|
+
const EMPTY_SERIALIZERS = new Map();
|
|
21
|
+
export function sendResult(ctx, response, result, serializers = EMPTY_SERIALIZERS, mode = 'auto', defaultStatus = defaultStatusFor(response)) {
|
|
22
|
+
if (defaultStatus !== undefined && !ctx.statusExplicit)
|
|
23
|
+
ctx.res.statusCode = defaultStatus;
|
|
24
|
+
if (result === undefined)
|
|
25
|
+
return void ctx.res.end();
|
|
26
|
+
const status = ctx.res.statusCode;
|
|
27
|
+
const entry = response?.[status];
|
|
28
|
+
if (!entry && response && Object.keys(response).length > 0) {
|
|
29
|
+
throw new Error(`[flowing] ${ctx.method} ${ctx.path}: no response schema for status ${status}`);
|
|
30
|
+
}
|
|
31
|
+
const headerType = ctx.res.getHeader('Content-Type');
|
|
32
|
+
const explicit = mode === 'json' ? JSON_TYPE : mode === 'text' ? 'text/plain' : typeof headerType === 'string' ? headerType : undefined;
|
|
33
|
+
let type;
|
|
34
|
+
let schema;
|
|
35
|
+
let jsonSchemaDeclared = false;
|
|
36
|
+
if (entry && isResponseContent(entry)) {
|
|
37
|
+
schema = entry.schema;
|
|
38
|
+
const offered = typeof entry.contentType === 'string' ? [entry.contentType] : [...entry.contentType];
|
|
39
|
+
if (offered.length > 1)
|
|
40
|
+
ctx.res.appendHeader('Vary', 'Accept');
|
|
41
|
+
const chosen = explicit ?? negotiate(ctx.get('accept'), offered);
|
|
42
|
+
if (!chosen) {
|
|
43
|
+
throw new HttpError(406, 'Not Acceptable', 'NOT_ACCEPTABLE', { details: { available: offered } });
|
|
44
|
+
}
|
|
45
|
+
type = chosen;
|
|
46
|
+
}
|
|
47
|
+
else if (entry) {
|
|
48
|
+
schema = entry;
|
|
49
|
+
jsonSchemaDeclared = true;
|
|
50
|
+
type = explicit ?? JSON_TYPE;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
type = explicit ?? inferType(result);
|
|
54
|
+
}
|
|
55
|
+
write(ctx, type, result, schema, mode === 'json' || (jsonSchemaDeclared && !explicit), serializers);
|
|
56
|
+
}
|
|
57
|
+
const NDJSON = 'application/x-ndjson';
|
|
58
|
+
/** Async iterable que não é um stream (async generator, cursor de banco…) (F-13). */
|
|
59
|
+
function isAsyncIterable(value) {
|
|
60
|
+
return typeof value === 'object' && value !== null && !(value instanceof Readable) && Symbol.asyncIterator in value;
|
|
61
|
+
}
|
|
62
|
+
function inferType(value) {
|
|
63
|
+
if (typeof value === 'string')
|
|
64
|
+
return 'text/plain';
|
|
65
|
+
if (value instanceof Uint8Array || value instanceof Readable)
|
|
66
|
+
return 'application/octet-stream';
|
|
67
|
+
if (isAsyncIterable(value))
|
|
68
|
+
return NDJSON;
|
|
69
|
+
return JSON_TYPE;
|
|
70
|
+
}
|
|
71
|
+
/** Cada item vira uma linha JSON (NDJSON). Com schema, cada item é validado e filtrado como uma resposta. */
|
|
72
|
+
function ndjson(ctx, items, schema) {
|
|
73
|
+
const signal = ctx.signal;
|
|
74
|
+
return Readable.from((async function* () {
|
|
75
|
+
for await (const item of items) {
|
|
76
|
+
if (signal.aborted)
|
|
77
|
+
return; // cliente desconectou: para de consumir a fonte
|
|
78
|
+
yield `${schema ? ctx[kContracts].serialize(schema, item) : JSON.stringify(item)}\n`;
|
|
79
|
+
}
|
|
80
|
+
})());
|
|
81
|
+
}
|
|
82
|
+
function write(ctx, type, value, schema, forceJson, serializers) {
|
|
83
|
+
const header = withCharset(type);
|
|
84
|
+
const base = mediaType(type);
|
|
85
|
+
const json = isJsonContentType(base);
|
|
86
|
+
if (value instanceof Readable)
|
|
87
|
+
return void ctx[kRawSend](value, header);
|
|
88
|
+
if (isAsyncIterable(value)) {
|
|
89
|
+
if (base !== NDJSON) {
|
|
90
|
+
throw new Error(`[flowing] ${ctx.method} ${ctx.path}: an async iterable is streamed as NDJSON; declare ` +
|
|
91
|
+
`response: { 200: { contentType: '${NDJSON}', schema: Item } } (the schema of each item), or return a Readable.`);
|
|
92
|
+
}
|
|
93
|
+
return void ctx[kRawSend](ndjson(ctx, value, schema), header);
|
|
94
|
+
}
|
|
95
|
+
// Valores crus (string/Buffer) vão como estão, exceto quando a rota declarou um schema JSON:
|
|
96
|
+
// nesse caso `"olá"` continua sendo serializado como JSON, como sempre.
|
|
97
|
+
if ((typeof value === 'string' || value instanceof Uint8Array) && !forceJson) {
|
|
98
|
+
if (schema && typeof value === 'string')
|
|
99
|
+
ensureValid(ctx, schema, value);
|
|
100
|
+
return void ctx[kRawSend](value, header);
|
|
101
|
+
}
|
|
102
|
+
if (json) {
|
|
103
|
+
// Pelo ponto único de escrita (F-04): onSend e sentBody valem também para JSON.
|
|
104
|
+
return void ctx[kRawSend](schema ? ctx[kContracts].serialize(schema, value) : JSON.stringify(value), header);
|
|
105
|
+
}
|
|
106
|
+
const serializer = serializers.get(base);
|
|
107
|
+
if (!serializer) {
|
|
108
|
+
throw new Error(`[flowing] ${ctx.method} ${ctx.path}: no serializer registered for "${base}". ` +
|
|
109
|
+
`Add it to createApp({ serializers: { '${base}': (data) => ... } }).`);
|
|
110
|
+
}
|
|
111
|
+
const data = schema ? ensureValid(ctx, schema, value) : value;
|
|
112
|
+
ctx[kRawSend](Buffer.from(serializer(data)), header);
|
|
113
|
+
}
|
|
114
|
+
/** Valida a saída e devolve uma cópia sem campos não declarados (S-07 em qualquer formato). */
|
|
115
|
+
function ensureValid(ctx, schema, value) {
|
|
116
|
+
const checked = ctx[kContracts].strip(schema, value);
|
|
117
|
+
if (!checked.ok) {
|
|
118
|
+
throw new Error(`[flowing] ${ctx.method} ${ctx.path}: response does not match its schema: ` +
|
|
119
|
+
checked.issues.map((i) => `${i.path} ${i.message}`).join('; '));
|
|
120
|
+
}
|
|
121
|
+
return checked.value;
|
|
122
|
+
}
|
|
123
|
+
/** Monta um Problem Details (RFC 9457). Sem `type`, usa `about:blank` e a frase do status como título. */
|
|
124
|
+
export function problem(status, detail, extra = {}) {
|
|
125
|
+
const out = { type: extra.type ?? 'about:blank', title: STATUS_CODES[status] ?? 'Error', status };
|
|
126
|
+
if (detail !== undefined && detail !== out.title)
|
|
127
|
+
out.detail = detail;
|
|
128
|
+
if (extra.code !== undefined)
|
|
129
|
+
out.code = extra.code;
|
|
130
|
+
if (extra.details !== undefined)
|
|
131
|
+
out.details = extra.details;
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
/** Escreve um problema (status + corpo), sem passar pelo schema da rota (F-32). */
|
|
135
|
+
export function sendProblem(ctx, body, format) {
|
|
136
|
+
ctx.res.statusCode = body.status;
|
|
137
|
+
if (format) {
|
|
138
|
+
let custom;
|
|
139
|
+
try {
|
|
140
|
+
custom = format(body, ctx);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
ctx.log.error({ err, method: ctx.method, path: ctx.path }, 'formatError failed; sending the default problem');
|
|
144
|
+
return void ctx[kRawSend](JSON.stringify(body), 'application/problem+json; charset=utf-8');
|
|
145
|
+
}
|
|
146
|
+
return void ctx[kRawSend](JSON.stringify(custom), 'application/json; charset=utf-8');
|
|
147
|
+
}
|
|
148
|
+
ctx[kRawSend](JSON.stringify(body), 'application/problem+json; charset=utf-8');
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resposta de erro padrão, no formato Problem Details (F-15). Só mensagens marcadas como `expose`
|
|
152
|
+
* chegam ao cliente; o resto vira 500 genérico e o erro original é logado no servidor (S-05).
|
|
153
|
+
*/
|
|
154
|
+
export function sendError(ctx, err, format) {
|
|
155
|
+
const httpErr = err instanceof HttpError ? err : undefined;
|
|
156
|
+
const status = httpErr?.status ?? 500;
|
|
157
|
+
const exposed = httpErr?.expose === true;
|
|
158
|
+
if (!exposed)
|
|
159
|
+
ctx.log.error({ err, method: ctx.method, path: ctx.path, status }, 'request failed');
|
|
160
|
+
if (ctx.res.writableEnded)
|
|
161
|
+
return;
|
|
162
|
+
if (ctx.res.headersSent)
|
|
163
|
+
return void ctx.res.end();
|
|
164
|
+
const body = exposed
|
|
165
|
+
? problem(status, httpErr.message, { code: httpErr.code, details: httpErr.details, type: httpErr.type })
|
|
166
|
+
: problem(status);
|
|
167
|
+
sendProblem(ctx, body, format);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../../src/core/response/pipeline.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,SAAS,EAA4C,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAqB,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAWnG,MAAM,SAAS,GAAG,kBAAkB,CAAC;AA0BrC,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,QAAkC;IACjE,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;SAClC,GAAG,CAAC,MAAM,CAAC;SACX,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC;IACzC,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAkC,EAAE,WAA4C;IAC3G,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,MAAM,iBAAiB,GAAoC,IAAI,GAAG,EAAE,CAAC;AAErE,MAAM,UAAU,UAAU,CACxB,GAAY,EACZ,QAAkC,EAClC,MAAe,EACf,WAAW,GAAoC,iBAAiB,EAChE,IAAI,GAAa,MAAM,EACvB,aAAa,GAAuB,gBAAgB,CAAC,QAAQ,CAAC;IAE9D,IAAI,aAAa,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,cAAc;QAAE,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,aAAa,CAAC;IAE3F,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAEpD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC;IAClC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,IAAI,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,mCAAmC,MAAM,EAAE,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACxI,IAAI,IAAY,CAAC;IACjB,IAAI,MAA0B,CAAC;IAC/B,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAE/B,IAAI,KAAK,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QACtB,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;QACrG,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,GAAG,MAAM,CAAC;IAChB,CAAC;SAAM,IAAI,KAAK,EAAE,CAAC;QACjB,MAAM,GAAG,KAAK,CAAC;QACf,kBAAkB,GAAG,IAAI,CAAC;QAC1B,IAAI,GAAG,QAAQ,IAAI,SAAS,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,kBAAkB,IAAI,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,MAAM,GAAG,sBAAsB,CAAC;AAEtC,qFAAqF;AACrF,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,YAAY,QAAQ,CAAC,IAAI,MAAM,CAAC,aAAa,IAAI,KAAK,CAAC;AACtH,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC;IACnD,IAAI,KAAK,YAAY,UAAU,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,0BAA0B,CAAC;IAChG,IAAI,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,6GAA6G;AAC7G,SAAS,MAAM,CAAC,GAAY,EAAE,KAA6B,EAAE,MAA0B;IACrF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,OAAO,QAAQ,CAAC,IAAI,CAClB,CAAC,KAAK,SAAS,CAAC;QACd,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,CAAC,gDAAgD;YAC5E,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QACvF,CAAC;IACH,CAAC,CAAC,EAAE,CACL,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CACZ,GAAY,EACZ,IAAY,EACZ,KAAc,EACd,MAA0B,EAC1B,SAAkB,EAClB,WAA4C;IAE5C,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAErC,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAExE,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,qDAAqD;gBACtF,oCAAoC,MAAM,sEAAsE,CACnH,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,6FAA6F;IAC7F,wEAAwE;IACxE,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7E,IAAI,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACzE,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,IAAI,EAAE,CAAC;QACT,gFAAgF;QAChF,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;IAC/G,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,mCAAmC,IAAI,KAAK;YAC7E,yCAAyC,IAAI,wBAAwB,CACxE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9D,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,+FAA+F;AAC/F,SAAS,WAAW,CAAC,GAAY,EAAE,MAAc,EAAE,KAAc;IAC/D,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,wCAAwC;YACzE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC;AACvB,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,OAAO,CACrB,MAAc,EACd,MAAe,EACf,KAAK,GAAgF,EAAE;IAEvF,MAAM,GAAG,GAAmB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,aAAa,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;IAClH,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,GAAG,CAAC,KAAK;QAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IACtE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC7D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,WAAW,CAAC,GAAY,EAAE,IAAoB,EAAE,MAAuB;IACrF,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,iDAAiD,CAAC,CAAC;YAC9G,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,yCAAyC,CAAC,CAAC;QAC7F,CAAC;QACD,OAAO,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,iCAAiC,CAAC,CAAC;IACvF,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,yCAAyC,CAAC,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,GAAY,EAAE,MAAuB;IAC3E,MAAM,OAAO,GAAG,GAAG,YAAY,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,GAAG,CAAC;IACtC,MAAM,OAAO,GAAG,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAEzC,IAAI,CAAC,OAAO;QAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;IACnG,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa;QAAE,OAAO;IAClC,IAAI,GAAG,CAAC,GAAG,CAAC,WAAW;QAAE,OAAO,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAEnD,MAAM,IAAI,GAAG,OAAO;QAClB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,OAAQ,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,OAAQ,CAAC,IAAI,EAAE,CAAC;QAC5G,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACpB,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Middleware } from './compose.ts';
|
|
2
|
+
import type { Context } from './context.ts';
|
|
3
|
+
import type { Guard } from './guard.ts';
|
|
4
|
+
import type { RouteSchemas } from './metadata.ts';
|
|
5
|
+
import type { RouteHooks } from './hooks.ts';
|
|
6
|
+
import type { kHooks } from './symbols.ts';
|
|
7
|
+
export type Handler = (ctx: Context) => unknown | Promise<unknown>;
|
|
8
|
+
/** Tudo o que o app precisa para executar uma rota. */
|
|
9
|
+
export interface RouteDefinition {
|
|
10
|
+
handler: Handler;
|
|
11
|
+
schemas: RouteSchemas;
|
|
12
|
+
middlewares: readonly Middleware[];
|
|
13
|
+
guards: readonly Guard[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A rota que atendeu a requisição, em `ctx.route` (F-38). `path` é o template com o prefixo (`/users/:id`),
|
|
17
|
+
* nunca o path recebido: é seguro como rótulo de métrica. `host` é a regra que venceu (`*.exemplo.com`),
|
|
18
|
+
* não o hostname do cliente.
|
|
19
|
+
*/
|
|
20
|
+
export interface RouteInfo {
|
|
21
|
+
readonly method: string;
|
|
22
|
+
readonly path: string;
|
|
23
|
+
/** Classe de origem (ou `static`). */
|
|
24
|
+
readonly owner: string;
|
|
25
|
+
/** Método da classe que trata a rota. */
|
|
26
|
+
readonly handler: string;
|
|
27
|
+
readonly host?: string;
|
|
28
|
+
/** @internal Hooks com escopo (F-43), montados no registro. */
|
|
29
|
+
readonly [kHooks]?: RouteHooks;
|
|
30
|
+
}
|
|
31
|
+
/** Resultado do `find()`: a rota registrada (o mesmo objeto, sem cópia) e os parâmetros desta requisição. */
|
|
32
|
+
export interface RouteMatch {
|
|
33
|
+
route: RouteDefinition & {
|
|
34
|
+
run?: (ctx: Context) => Promise<void> | undefined;
|
|
35
|
+
info?: RouteInfo;
|
|
36
|
+
};
|
|
37
|
+
params: Record<string, string>;
|
|
38
|
+
}
|
|
39
|
+
/** Tabela de rotas interna sobre o find-my-way. */
|
|
40
|
+
export declare class RouteTable {
|
|
41
|
+
private inner;
|
|
42
|
+
/** Com `host` (chave da regra), a rota só casa naquele host (F-35); sem, vale para qualquer um (fallback). */
|
|
43
|
+
on(method: string, path: string, route: RouteDefinition, host?: string): void;
|
|
44
|
+
/** Desfaz um `on()`, para o registro em duas fases. */
|
|
45
|
+
off(method: string, path: string, host?: string): void;
|
|
46
|
+
/** Já existe uma rota idêntica (método, path e host)? */
|
|
47
|
+
has(method: string, path: string, host?: string): boolean;
|
|
48
|
+
/** `hostname` é o validado pelo app (`ctx.hostname`), nunca o header cru. */
|
|
49
|
+
find(method: string, path: string, hostname?: string): RouteMatch | null;
|
|
50
|
+
/**
|
|
51
|
+
* Descobre quais métodos existem para o path informado.
|
|
52
|
+
* Estratégia: pergunta ao find-my-way método por método.
|
|
53
|
+
* O custo é O(número de métodos registrados) por lookup, o que é trivial
|
|
54
|
+
* (no máximo 7 find() O(log n) cada).
|
|
55
|
+
*/
|
|
56
|
+
allowedMethods(path: string, hostname?: string): string[];
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=route-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-table.d.ts","sourceRoot":"","sources":["../../src/core/route-table.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnE,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;IACnC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC;CAChC;AAED,6GAA6G;AAC7G,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,eAAe,GAAG;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC;IACjG,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAkDD,mDAAmD;AACnD,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAuF;IAEpG,8GAA8G;IAC9G,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAG5E;IAED,uDAAuD;IACvD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAGrD;IAED,yDAAyD;IACzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAKxD;IAED,6EAA6E;IAC7E,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,UAAU,GAAG,IAAI,CAOnE;IAED;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,EAAE,CAUpD;CACF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/core/route-table.ts
|
|
2
|
+
import Router from 'find-my-way';
|
|
3
|
+
import { compileRouteHost, hostRank } from '../utils/origin-match.js';
|
|
4
|
+
// O find-my-way exige um handler próprio; usamos só o `store`, então ele nunca é chamado.
|
|
5
|
+
const NOOP = () => { };
|
|
6
|
+
const PROBE_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
|
|
7
|
+
/** Converte a chave da restrição de volta na regra (`re:fonte/flags` → RegExp). */
|
|
8
|
+
function ruleFromKey(key) {
|
|
9
|
+
if (!key.startsWith('re:'))
|
|
10
|
+
return compileRouteHost(key);
|
|
11
|
+
const slash = key.lastIndexOf('/');
|
|
12
|
+
return compileRouteHost(new RegExp(key.slice(3, slash), key.slice(slash + 1)));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Estratégia de host própria (F-35), no lugar da embutida do find-my-way, que testa as RegExp na ordem de
|
|
16
|
+
* registro. Aqui a prioridade é a de {@link hostRank}: exato → curinga mais específico → RegExp.
|
|
17
|
+
*/
|
|
18
|
+
const hostStrategy = {
|
|
19
|
+
name: 'host',
|
|
20
|
+
mustMatchWhenDerived: false,
|
|
21
|
+
storage() {
|
|
22
|
+
const exact = new Map();
|
|
23
|
+
const ranked = [];
|
|
24
|
+
let seq = 0;
|
|
25
|
+
return {
|
|
26
|
+
get(hostname) {
|
|
27
|
+
const hit = exact.get(hostname);
|
|
28
|
+
if (hit !== undefined)
|
|
29
|
+
return hit;
|
|
30
|
+
for (const r of ranked)
|
|
31
|
+
if (r.rule.test(hostname))
|
|
32
|
+
return r.value;
|
|
33
|
+
return null;
|
|
34
|
+
},
|
|
35
|
+
set(key, value) {
|
|
36
|
+
const rule = ruleFromKey(key);
|
|
37
|
+
if (rule.kind === 'exact')
|
|
38
|
+
return void exact.set(rule.base, value);
|
|
39
|
+
const existing = ranked.find((r) => r.rule.key === key);
|
|
40
|
+
if (existing)
|
|
41
|
+
return void (existing.value = value);
|
|
42
|
+
ranked.push({ rule, rank: hostRank(rule, seq++), value });
|
|
43
|
+
ranked.sort((a, b) => a.rank - b.rank);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
validate(value) {
|
|
48
|
+
if (typeof value !== 'string')
|
|
49
|
+
throw new Error('host constraint must be a compiled host key');
|
|
50
|
+
},
|
|
51
|
+
// O app sempre informa o host validado no `find()`; o header cru nunca é usado.
|
|
52
|
+
deriveConstraint: () => '',
|
|
53
|
+
};
|
|
54
|
+
/** Tabela de rotas interna sobre o find-my-way. */
|
|
55
|
+
export class RouteTable {
|
|
56
|
+
inner = Router({ ignoreTrailingSlash: true, constraints: { host: hostStrategy } });
|
|
57
|
+
/** Com `host` (chave da regra), a rota só casa naquele host (F-35); sem, vale para qualquer um (fallback). */
|
|
58
|
+
on(method, path, route, host) {
|
|
59
|
+
if (host === undefined)
|
|
60
|
+
this.inner.on(method, path, NOOP, route);
|
|
61
|
+
else
|
|
62
|
+
this.inner.on(method, path, { constraints: { host } }, NOOP, route);
|
|
63
|
+
}
|
|
64
|
+
/** Desfaz um `on()`, para o registro em duas fases. */
|
|
65
|
+
off(method, path, host) {
|
|
66
|
+
if (host === undefined)
|
|
67
|
+
this.inner.off(method, path);
|
|
68
|
+
else
|
|
69
|
+
this.inner.off(method, path, { host });
|
|
70
|
+
}
|
|
71
|
+
/** Já existe uma rota idêntica (método, path e host)? */
|
|
72
|
+
has(method, path, host) {
|
|
73
|
+
// Com `ignoreTrailingSlash`, `/users` e `/users/` são a mesma rota: confere as duas formas.
|
|
74
|
+
const alt = path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : `${path}/`;
|
|
75
|
+
const c = host === undefined ? undefined : { host };
|
|
76
|
+
return [path, alt].some((p) => (c ? this.inner.hasRoute(method, p, c) : this.inner.hasRoute(method, p)));
|
|
77
|
+
}
|
|
78
|
+
/** `hostname` é o validado pelo app (`ctx.hostname`), nunca o header cru. */
|
|
79
|
+
find(method, path, hostname = '') {
|
|
80
|
+
const c = { host: hostname };
|
|
81
|
+
// F-08: HEAD usa o handler do GET quando não houver rota HEAD própria.
|
|
82
|
+
// O Node já descarta o corpo de respostas a HEAD.
|
|
83
|
+
const found = this.inner.find(method, path, c) ?? (method === 'HEAD' ? this.inner.find('GET', path, c) : null);
|
|
84
|
+
if (!found)
|
|
85
|
+
return null;
|
|
86
|
+
return { route: found.store, params: found.params };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Descobre quais métodos existem para o path informado.
|
|
90
|
+
* Estratégia: pergunta ao find-my-way método por método.
|
|
91
|
+
* O custo é O(número de métodos registrados) por lookup, o que é trivial
|
|
92
|
+
* (no máximo 7 find() O(log n) cada).
|
|
93
|
+
*/
|
|
94
|
+
allowedMethods(path, hostname = '') {
|
|
95
|
+
const methods = new Set();
|
|
96
|
+
for (const method of PROBE_METHODS) {
|
|
97
|
+
if (this.inner.find(method, path, { host: hostname })) {
|
|
98
|
+
methods.add(method);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (methods.has('GET'))
|
|
102
|
+
methods.add('HEAD');
|
|
103
|
+
if (methods.size > 0)
|
|
104
|
+
methods.add('OPTIONS');
|
|
105
|
+
return [...methods].sort();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=route-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-table.js","sourceRoot":"","sources":["../../src/core/route-table.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type http from 'node:http';
|
|
2
|
+
import type { AppCore } from '../app/core.ts';
|
|
3
|
+
import { type Step } from '../compose.ts';
|
|
4
|
+
import type { Context } from '../context.ts';
|
|
5
|
+
import type { RouteInfo } from '../route-table.ts';
|
|
6
|
+
/** Grava o instante em que os headers da resposta saíram (a primeira vez), para o `ttfbMs`. */
|
|
7
|
+
export declare function markHeadersSent(res: http.ServerResponse): void;
|
|
8
|
+
/**
|
|
9
|
+
* Observer (F-38): tudo o que acompanha a requisição sem participar dela. Os hooks `onRequest` e
|
|
10
|
+
* `onResponse`, o `logRequests` e o `telemetry` são observadores; o pipeline só avisa os momentos (início,
|
|
11
|
+
* execução, rota resolvida, erro), e o fim é detectado aqui.
|
|
12
|
+
*
|
|
13
|
+
* Também é a única fonte da regra "há quem observe o fim" (`watching`), usada pelo pipeline e pelas
|
|
14
|
+
* respostas do `listen()`. Sem observadores, nada é criado por requisição: o custo é zero.
|
|
15
|
+
*/
|
|
16
|
+
export declare class RequestObserver {
|
|
17
|
+
private readonly core;
|
|
18
|
+
constructor(core: AppCore);
|
|
19
|
+
/**
|
|
20
|
+
* Há quem observe o fim da requisição: `onResponse` (do app ou de alguma rota), `logRequests` ou `telemetry`.
|
|
21
|
+
* A requisição ainda não foi roteada quando isso é decidido, então um `onResponse` de rota liga a observação
|
|
22
|
+
* do app inteiro; sem nenhum, o custo continua zero.
|
|
23
|
+
*/
|
|
24
|
+
get watching(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Início da requisição, logo depois de criar o `Context` e antes da validação do `Host`: toda requisição
|
|
27
|
+
* observada no início também é observada no fim. `started` é o instante de chegada, ou `-1` se ninguém
|
|
28
|
+
* observa o fim.
|
|
29
|
+
*/
|
|
30
|
+
start(ctx: Context, started: number): void;
|
|
31
|
+
/** O pipeline, dentro do contexto do span criado pelo `telemetry` (se houver). */
|
|
32
|
+
run(ctx: Context, fn: () => Step): Step;
|
|
33
|
+
/** A rota foi resolvida: o `telemetry` nomeia o span antes de a resposta sair. */
|
|
34
|
+
routed(ctx: Context, route: RouteInfo): void;
|
|
35
|
+
/** Um erro chegou ao pipeline de erros: guardado para o `onResponse` e registrado no span antes da resposta. */
|
|
36
|
+
failed(ctx: Context, err: unknown): void;
|
|
37
|
+
/**
|
|
38
|
+
* Fim da requisição: o evento `close` dispara em todos os caminhos (inclusive cancelamento, que não tem
|
|
39
|
+
* `finish`), e a trava garante uma única execução.
|
|
40
|
+
*/
|
|
41
|
+
private watchEnd;
|
|
42
|
+
private endInfo;
|
|
43
|
+
private runOnResponse;
|
|
44
|
+
/** `onRequest` é síncrono: uma Promise devolvida não é esperada, mas a rejeição é logada. */
|
|
45
|
+
private runOnRequest;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=observer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAiB,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAE9D;AAMD;;;;;;;GAOG;AACH,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,OAAO,EAAI;IAE9C;;;;OAIG;IACH,IAAI,QAAQ,IAAI,OAAO,CAGtB;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAIzC;IAED,kFAAkF;IAClF,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAGtC;IAED,kFAAkF;IAClF,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAE3C;IAED,gHAAgH;IAChH,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAGvC;IAED;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,OAAO;IAqBf,OAAO,CAAC,aAAa;IAMrB,6FAA6F;IAC7F,OAAO,CAAC,YAAY;CAUrB"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
2
|
+
import { isPromiseLike } from '../compose.js';
|
|
3
|
+
import { kError, kHeadersAt, kHooks, kPhase } from '../symbols.js';
|
|
4
|
+
/** Grava o instante em que os headers da resposta saíram (a primeira vez), para o `ttfbMs`. */
|
|
5
|
+
export function markHeadersSent(res) {
|
|
6
|
+
res[kHeadersAt] ??= performance.now();
|
|
7
|
+
}
|
|
8
|
+
function headersSentAt(res) {
|
|
9
|
+
return res[kHeadersAt];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Observer (F-38): tudo o que acompanha a requisição sem participar dela. Os hooks `onRequest` e
|
|
13
|
+
* `onResponse`, o `logRequests` e o `telemetry` são observadores; o pipeline só avisa os momentos (início,
|
|
14
|
+
* execução, rota resolvida, erro), e o fim é detectado aqui.
|
|
15
|
+
*
|
|
16
|
+
* Também é a única fonte da regra "há quem observe o fim" (`watching`), usada pelo pipeline e pelas
|
|
17
|
+
* respostas do `listen()`. Sem observadores, nada é criado por requisição: o custo é zero.
|
|
18
|
+
*/
|
|
19
|
+
export class RequestObserver {
|
|
20
|
+
core;
|
|
21
|
+
constructor(core) {
|
|
22
|
+
this.core = core;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Há quem observe o fim da requisição: `onResponse` (do app ou de alguma rota), `logRequests` ou `telemetry`.
|
|
26
|
+
* A requisição ainda não foi roteada quando isso é decidido, então um `onResponse` de rota liga a observação
|
|
27
|
+
* do app inteiro; sem nenhum, o custo continua zero.
|
|
28
|
+
*/
|
|
29
|
+
get watching() {
|
|
30
|
+
const core = this.core;
|
|
31
|
+
return core.onResponseHooks.length > 0 || core.scopedOnResponse > 0 || core.telemetry !== undefined || core.opts.logRequests === true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Início da requisição, logo depois de criar o `Context` e antes da validação do `Host`: toda requisição
|
|
35
|
+
* observada no início também é observada no fim. `started` é o instante de chegada, ou `-1` se ninguém
|
|
36
|
+
* observa o fim.
|
|
37
|
+
*/
|
|
38
|
+
start(ctx, started) {
|
|
39
|
+
if (started !== -1)
|
|
40
|
+
this.watchEnd(ctx, started);
|
|
41
|
+
this.core.telemetry?.onRequest(ctx);
|
|
42
|
+
if (this.core.onRequestHooks.length > 0)
|
|
43
|
+
this.runOnRequest(ctx);
|
|
44
|
+
}
|
|
45
|
+
/** O pipeline, dentro do contexto do span criado pelo `telemetry` (se houver). */
|
|
46
|
+
run(ctx, fn) {
|
|
47
|
+
const telemetry = this.core.telemetry;
|
|
48
|
+
return telemetry ? telemetry.run(ctx, fn) : fn();
|
|
49
|
+
}
|
|
50
|
+
/** A rota foi resolvida: o `telemetry` nomeia o span antes de a resposta sair. */
|
|
51
|
+
routed(ctx, route) {
|
|
52
|
+
this.core.telemetry?.onRoute(ctx, route);
|
|
53
|
+
}
|
|
54
|
+
/** Um erro chegou ao pipeline de erros: guardado para o `onResponse` e registrado no span antes da resposta. */
|
|
55
|
+
failed(ctx, err) {
|
|
56
|
+
ctx[kError] = err;
|
|
57
|
+
this.core.telemetry?.onError(ctx, err, ctx[kPhase]);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Fim da requisição: o evento `close` dispara em todos os caminhos (inclusive cancelamento, que não tem
|
|
61
|
+
* `finish`), e a trava garante uma única execução.
|
|
62
|
+
*/
|
|
63
|
+
watchEnd(ctx, started) {
|
|
64
|
+
const res = ctx.res;
|
|
65
|
+
let done = false;
|
|
66
|
+
res.once('close', () => {
|
|
67
|
+
if (done)
|
|
68
|
+
return;
|
|
69
|
+
done = true;
|
|
70
|
+
const info = this.endInfo(ctx, started);
|
|
71
|
+
this.core.telemetry?.onResponse(ctx, info);
|
|
72
|
+
if (this.core.opts.logRequests) {
|
|
73
|
+
const { status, ms, abortReason } = info;
|
|
74
|
+
const entry = { method: ctx.method, path: ctx.path, status, ms, ...(abortReason ? { abortReason } : {}) };
|
|
75
|
+
ctx.log.info(entry, abortReason ? 'request aborted' : 'request completed');
|
|
76
|
+
}
|
|
77
|
+
// Os do app primeiro, depois os da rota (F-43), na ordem de fora para dentro.
|
|
78
|
+
for (const hook of this.core.onResponseHooks)
|
|
79
|
+
this.runOnResponse(hook, ctx, info);
|
|
80
|
+
const scoped = info.route?.[kHooks]?.onResponse;
|
|
81
|
+
if (scoped)
|
|
82
|
+
for (const hook of scoped)
|
|
83
|
+
this.runOnResponse(hook, ctx, info);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
endInfo(ctx, started) {
|
|
87
|
+
const res = ctx.res;
|
|
88
|
+
const round = (t) => Math.round((t - started) * 100) / 100;
|
|
89
|
+
const abortReason = ctx.abortReason ?? (res.writableEnded ? undefined : 'CLIENT_DISCONNECTED');
|
|
90
|
+
const status = res.statusCode;
|
|
91
|
+
const error = ctx[kError];
|
|
92
|
+
const route = ctx.route;
|
|
93
|
+
// Gravado pelas respostas do `listen()` e do `inject()`; num servidor próprio (`callback()`), fica ausente.
|
|
94
|
+
const ttfb = headersSentAt(res);
|
|
95
|
+
return {
|
|
96
|
+
status,
|
|
97
|
+
ms: round(performance.now()),
|
|
98
|
+
outcome: abortReason ? 'aborted' : status >= 500 ? 'error' : 'completed',
|
|
99
|
+
streaming: ctx.streamingResponse,
|
|
100
|
+
...(abortReason ? { abortReason } : {}),
|
|
101
|
+
...(ttfb === undefined ? {} : { ttfbMs: round(ttfb) }),
|
|
102
|
+
...(route ? { route } : {}),
|
|
103
|
+
...(error === undefined ? {} : { error, errorPhase: ctx[kPhase] }),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
runOnResponse(hook, ctx, info) {
|
|
107
|
+
Promise.resolve()
|
|
108
|
+
.then(() => hook(ctx, info))
|
|
109
|
+
.catch((err) => ctx.log.error({ err }, 'onResponse hook failed'));
|
|
110
|
+
}
|
|
111
|
+
/** `onRequest` é síncrono: uma Promise devolvida não é esperada, mas a rejeição é logada. */
|
|
112
|
+
runOnRequest(ctx) {
|
|
113
|
+
for (const hook of this.core.onRequestHooks) {
|
|
114
|
+
try {
|
|
115
|
+
const out = hook(ctx);
|
|
116
|
+
if (isPromiseLike(out))
|
|
117
|
+
out.then(undefined, (err) => ctx.log.error({ err }, 'onRequest hook failed'));
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
ctx.log.error({ err }, 'onRequest hook failed');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer.js","sourceRoot":"","sources":["../../../src/core/runtime/observer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type http from 'node:http';
|
|
2
|
+
import type { AppCore } from '../app/core.ts';
|
|
3
|
+
import { type Step } from '../compose.ts';
|
|
4
|
+
import { Context } from '../context.ts';
|
|
5
|
+
import type { FrameworkRoutes } from '../registry/framework-routes.ts';
|
|
6
|
+
import type { RouteRegistry } from '../registry/registry.ts';
|
|
7
|
+
import type { RequestObserver } from './observer.ts';
|
|
8
|
+
import type { ServerLifecycle } from './server.ts';
|
|
9
|
+
/**
|
|
10
|
+
* Pipeline da requisição: rastreamento, request id, validação do `Host`, headers de segurança, healthchecks,
|
|
11
|
+
* middlewares globais, OpenAPI, roteamento, pipeline da rota e erros.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RequestHandler {
|
|
14
|
+
private readonly core;
|
|
15
|
+
private readonly registry;
|
|
16
|
+
private readonly framework;
|
|
17
|
+
private readonly server;
|
|
18
|
+
private readonly observer;
|
|
19
|
+
constructor(core: AppCore, registry: RouteRegistry, framework: FrameworkRoutes, server: ServerLifecycle, observer: RequestObserver);
|
|
20
|
+
/**
|
|
21
|
+
* Listener `(req, res)` do app, para usar com qualquer servidor do Node:
|
|
22
|
+
* `https.createServer(opts, app.callback())`, `http2` em modo compat, testes etc. (F-09).
|
|
23
|
+
*/
|
|
24
|
+
callback(): (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
25
|
+
private fail;
|
|
26
|
+
/** Cadeia global compilada; refeita só quando `app.use()` acrescenta um middleware. */
|
|
27
|
+
private globalChain;
|
|
28
|
+
private readonly readBody;
|
|
29
|
+
/** O pipeline completo, como uma Promise (API pública: `app.handle()`). */
|
|
30
|
+
handle(ctx: Context): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* O pipeline completo. Síncrono do começo ao fim quando nada é assíncrono (sem healthcheck no path, sem
|
|
33
|
+
* body a ler, sem middlewares, handler síncrono); vira assíncrono só a partir da primeira Promise de verdade
|
|
34
|
+
* (F-41). Erros síncronos e assíncronos terminam no mesmo `runErrorHandlers`.
|
|
35
|
+
*/
|
|
36
|
+
run(ctx: Context): Step;
|
|
37
|
+
/** Liga a parte assíncrona (se houver) ao tratamento de erros. */
|
|
38
|
+
private catching;
|
|
39
|
+
private serveHealth;
|
|
40
|
+
private afterHealth;
|
|
41
|
+
private afterBody;
|
|
42
|
+
/**
|
|
43
|
+
* Um middleware global que não chama next() e não responde: a rota não roda (antes, rodava mesmo assim).
|
|
44
|
+
* Para a requisição não ficar sem resposta, 404, como no Koa.
|
|
45
|
+
*/
|
|
46
|
+
private checkRouted;
|
|
47
|
+
/**
|
|
48
|
+
* Cadeia global (Chain of Responsibility pré-compilada): os middlewares de `app.use()` envolvem o
|
|
49
|
+
* roteamento e a rota, como no Koa. Um `await next()` num middleware global inclui o handler.
|
|
50
|
+
*/
|
|
51
|
+
private chain;
|
|
52
|
+
/** Fim da cadeia global: OpenAPI, roteamento, 404/405 e o programa da rota. */
|
|
53
|
+
private route;
|
|
54
|
+
/** Roteamento, 404/405 e o programa da rota. */
|
|
55
|
+
private dispatch;
|
|
56
|
+
/**
|
|
57
|
+
* Executa os `onError` do usuário. Se um deles lançar, o erro é logado e seguimos
|
|
58
|
+
* para a resposta padrão com o erro **original** — nunca deixamos a Promise rejeitar (B-08).
|
|
59
|
+
*/
|
|
60
|
+
private runErrorHandlers;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=request-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-handler.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/request-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAgB,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAMxD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAwBnD;;;GAGG;AACH,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,YACmB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,aAAa,EACvB,SAAS,EAAE,eAAe,EAC1B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,eAAe,EAG3C;IAED;;;OAGG;IACH,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CA+DxE;IAID,OAAO,CAAC,IAAI;IAMZ,uFAAuF;IACvF,OAAO,CAAC,WAAW,CAA4D;IAC/E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8C;IAEvE,2EAA2E;IACrE,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAExC;IAED;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAWtB;IAED,kEAAkE;IAClE,OAAO,CAAC,QAAQ;YAIF,WAAW;IAKzB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,SAAS;IAQjB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAOnB;;;OAGG;IACH,OAAO,CAAC,KAAK;IAQb,+EAA+E;IAC/E,OAAO,CAAC,KAAK;IAUb,gDAAgD;IAChD,OAAO,CAAC,QAAQ;IA6BhB;;;OAGG;YACW,gBAAgB;CAe/B"}
|