sashiko 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -15
- package/dist/core/app.d.ts +35 -3
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +70 -5
- package/dist/core/app.js.map +1 -1
- package/dist/core/context.d.ts +60 -8
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +77 -16
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +4 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/infer.d.ts +9 -1
- package/dist/core/infer.d.ts.map +1 -1
- package/dist/core/metadata.d.ts +17 -1
- package/dist/core/metadata.d.ts.map +1 -1
- package/dist/core/metadata.js +6 -0
- package/dist/core/metadata.js.map +1 -1
- package/dist/core/response.d.ts +22 -8
- package/dist/core/response.d.ts.map +1 -1
- package/dist/core/response.js +93 -15
- package/dist/core/response.js.map +1 -1
- package/dist/core/shutdown.d.ts +36 -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/middlewares/index.d.ts +1 -1
- package/dist/middlewares/index.d.ts.map +1 -1
- package/dist/middlewares/index.js.map +1 -1
- package/dist/middlewares/security-headers.d.ts +4 -2
- package/dist/middlewares/security-headers.d.ts.map +1 -1
- package/dist/middlewares/security-headers.js +10 -2
- package/dist/middlewares/security-headers.js.map +1 -1
- package/dist/schema/validation.d.ts +11 -0
- package/dist/schema/validation.d.ts.map +1 -1
- package/dist/schema/validation.js +18 -0
- package/dist/schema/validation.js.map +1 -1
- package/dist/utils/content-type.d.ts +8 -0
- package/dist/utils/content-type.d.ts.map +1 -1
- package/dist/utils/content-type.js +42 -0
- package/dist/utils/content-type.js.map +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/request-target.d.ts +19 -0
- package/dist/utils/request-target.d.ts.map +1 -0
- package/dist/utils/request-target.js +49 -0
- package/dist/utils/request-target.js.map +1 -0
- package/package.json +1 -1
package/dist/core/context.js
CHANGED
|
@@ -2,7 +2,13 @@ import { Readable } from 'node:stream';
|
|
|
2
2
|
import { pipeline } from 'node:stream/promises';
|
|
3
3
|
import { HttpError } from './errors.js';
|
|
4
4
|
import { parseQuery } from '../utils/query.js';
|
|
5
|
+
import { hostnameOf, isValidHost, parseRequestTarget, pickForwarded } from '../utils/request-target.js';
|
|
5
6
|
import { silentLogger } from './logger.js';
|
|
7
|
+
import { sendResult } from './response.js';
|
|
8
|
+
/** @internal Envio cru, sem pipeline: usado pelo próprio pipeline, por erros e por 404/405. */
|
|
9
|
+
export const kRawSend = Symbol('sashiko.rawSend');
|
|
10
|
+
/** @internal Liga o contexto aos schemas de resposta da rota, antes do handler (F-32). */
|
|
11
|
+
export const kBindResponse = Symbol('sashiko.bindResponse');
|
|
6
12
|
/**
|
|
7
13
|
* Contexto da requisição. Passe os schemas da rota no genérico para tipar
|
|
8
14
|
* `params`, `query` e `body` a partir do `@Schema` (F-02):
|
|
@@ -32,20 +38,55 @@ export class Context {
|
|
|
32
38
|
/** `true` quando o handler chamou `ctx.status()`. Usado para escolher o schema de resposta (B-05). */
|
|
33
39
|
statusExplicit = false;
|
|
34
40
|
streaming = false;
|
|
41
|
+
bound;
|
|
35
42
|
constructor(req, res, opts = {}) {
|
|
36
43
|
this.req = req;
|
|
37
44
|
this.res = res;
|
|
38
45
|
this.method = req.method ?? 'GET';
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
this.trustProxy = opts.trustProxy ?? false;
|
|
47
|
+
// S-09: o path vem do request-target com base fixa; o host do cliente nunca entra no parsing.
|
|
48
|
+
const target = parseRequestTarget(req.url);
|
|
49
|
+
this.path = target.pathname;
|
|
50
|
+
this.query = parseQuery(target.searchParams, opts.queryMaxKeys);
|
|
51
|
+
this.url = new URL(`${this.protocol}://${this.host || 'localhost'}${target.pathname}${target.search}`);
|
|
42
52
|
this.id = opts.id ?? '';
|
|
43
53
|
this.log = (opts.logger ?? silentLogger).child({ reqId: this.id });
|
|
44
54
|
}
|
|
45
|
-
|
|
55
|
+
trustProxy;
|
|
56
|
+
/**
|
|
57
|
+
* IP do cliente. Por padrão, o do socket. Com `trustProxy`, vem do `X-Forwarded-For`, que só deve
|
|
58
|
+
* ser ligado atrás de um proxy que reescreve esse header (S-09).
|
|
59
|
+
*/
|
|
46
60
|
get ip() {
|
|
47
|
-
return this.req.socket.remoteAddress ?? '';
|
|
61
|
+
return pickForwarded(this.req.headers['x-forwarded-for'], this.trustProxy) ?? this.req.socket.remoteAddress ?? '';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Host da requisição (`api.exemplo.com:8080`), **validado**: um valor inválido vira `''`.
|
|
65
|
+
* Com `trustProxy`, vem do `X-Forwarded-Host`. Para links absolutos, prefira uma URL base
|
|
66
|
+
* configurada em vez do host pedido pelo cliente (S-09).
|
|
67
|
+
*/
|
|
68
|
+
get host() {
|
|
69
|
+
const forwarded = pickForwarded(this.req.headers['x-forwarded-host'], this.trustProxy);
|
|
70
|
+
if (forwarded !== undefined)
|
|
71
|
+
return isValidHost(forwarded) ? forwarded : '';
|
|
72
|
+
const host = this.req.headers.host;
|
|
73
|
+
return isValidHost(host) ? host : '';
|
|
74
|
+
}
|
|
75
|
+
/** `host` sem a porta. */
|
|
76
|
+
get hostname() {
|
|
77
|
+
return this.host ? hostnameOf(this.host) : '';
|
|
78
|
+
}
|
|
79
|
+
/** `https` se a conexão for TLS ou, com `trustProxy`, se o `X-Forwarded-Proto` disser; senão `http`. */
|
|
80
|
+
get protocol() {
|
|
81
|
+
const forwarded = pickForwarded(this.req.headers['x-forwarded-proto'], this.trustProxy)?.toLowerCase();
|
|
82
|
+
if (forwarded === 'https' || forwarded === 'http')
|
|
83
|
+
return forwarded;
|
|
84
|
+
return this.req.socket.encrypted ? 'https' : 'http';
|
|
48
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Define o status. Com schemas de resposta, aceita só os status declarados e o encadeamento tipa o
|
|
88
|
+
* corpo: `ctx.status(202).json({ jobId })` (F-32).
|
|
89
|
+
*/
|
|
49
90
|
status(code) {
|
|
50
91
|
this.res.statusCode = code;
|
|
51
92
|
this.statusExplicit = true;
|
|
@@ -68,23 +109,42 @@ export class Context {
|
|
|
68
109
|
this.res.setHeader(name, value);
|
|
69
110
|
return this;
|
|
70
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Envia JSON. No handler, passa pelo mesmo pipeline do `return`: schema do status, remoção de
|
|
114
|
+
* campos não declarados e status padrão (F-32).
|
|
115
|
+
*/
|
|
71
116
|
json(data) {
|
|
72
|
-
|
|
73
|
-
return this;
|
|
74
|
-
this.res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
75
|
-
this.res.end(JSON.stringify(data));
|
|
76
|
-
return this;
|
|
117
|
+
return this.dispatch(data, 'json');
|
|
77
118
|
}
|
|
78
|
-
/** Resposta em texto puro (`text/plain; charset=utf-8`)
|
|
119
|
+
/** Resposta em texto puro (`text/plain; charset=utf-8`), pelo mesmo pipeline do `return`. */
|
|
79
120
|
text(data) {
|
|
80
|
-
return this.
|
|
121
|
+
return this.dispatch(data, 'text');
|
|
81
122
|
}
|
|
82
123
|
/**
|
|
83
|
-
* Envia
|
|
84
|
-
* `
|
|
85
|
-
* dinâmico antes: o framework não faz isso por você (S-02).
|
|
124
|
+
* Envia o corpo pelo mesmo pipeline do `return` (F-32): com `contentType` informado, usa esse tipo;
|
|
125
|
+
* senão, a negociação do `Accept` ou o tipo do valor (string, Buffer, stream, objeto → JSON).
|
|
126
|
+
* Para HTML, faça o escape do conteúdo dinâmico antes: o framework não faz isso por você (S-02).
|
|
86
127
|
*/
|
|
87
128
|
send(body, contentType) {
|
|
129
|
+
if (this.sent)
|
|
130
|
+
return this;
|
|
131
|
+
if (contentType)
|
|
132
|
+
this.res.setHeader('Content-Type', contentType);
|
|
133
|
+
return this.dispatch(body, 'auto');
|
|
134
|
+
}
|
|
135
|
+
dispatch(body, mode) {
|
|
136
|
+
if (this.sent)
|
|
137
|
+
return this;
|
|
138
|
+
// Antes do handler (middlewares, guards) não há schema ligado: vale só o tipo do conteúdo.
|
|
139
|
+
sendResult(this, this.bound?.response, body, this.bound?.serializers, mode);
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
/** @internal Liga os schemas de resposta da rota (o app chama antes do handler). */
|
|
143
|
+
[kBindResponse](response, serializers) {
|
|
144
|
+
this.bound = { response, serializers };
|
|
145
|
+
}
|
|
146
|
+
/** @internal Envio cru, sem schema: string, bytes ou stream. */
|
|
147
|
+
[kRawSend](body, contentType) {
|
|
88
148
|
if (this.sent)
|
|
89
149
|
return this;
|
|
90
150
|
if (contentType)
|
|
@@ -109,7 +169,8 @@ export class Context {
|
|
|
109
169
|
redirect(url, status = 302) {
|
|
110
170
|
if (this.sent)
|
|
111
171
|
return this;
|
|
112
|
-
this.status
|
|
172
|
+
this.res.statusCode = status;
|
|
173
|
+
this.statusExplicit = true;
|
|
113
174
|
this.res.setHeader('Location', url);
|
|
114
175
|
this.res.end();
|
|
115
176
|
return this;
|
package/dist/core/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -6,10 +6,12 @@ export { MountedGroup } from './mounted-group.ts';
|
|
|
6
6
|
export type { Middleware } from './compose.ts';
|
|
7
7
|
export type { Guard } from './guard.ts';
|
|
8
8
|
export type { InferBody, InferHeaders, InferParams, InferQuery, InferResponse, InferSchema } from './infer.ts';
|
|
9
|
+
export type { ShutdownOptions } from './shutdown.ts';
|
|
10
|
+
export type { Serializer } from './response.ts';
|
|
9
11
|
export { consoleLogger, silentLogger, type ConsoleLoggerOptions, type Logger, type LogLevel } from './logger.ts';
|
|
10
|
-
export { Context, type ContextOptions, type ContextState } from './context.ts';
|
|
12
|
+
export { Context, type BodyFor, type ContextOptions, type ContextState, type DeclaredStatus, type StatusContext } from './context.ts';
|
|
11
13
|
export { badRequest, forbidden, HttpError, internal, isHttpError, notFound, unauthorized, type HttpErrorOptions } from './errors.ts';
|
|
12
|
-
export { ROUTER_METADATA, readOwnMetadata, type RouterMetadata, type MethodMetadata, type RouteSchemas } from './metadata.ts';
|
|
14
|
+
export { isResponseContent, ROUTER_METADATA, readOwnMetadata, type ResponseContent, type ResponseEntry, type RouterMetadata, type MethodMetadata, type RouteSchemas } from './metadata.ts';
|
|
13
15
|
export type { Handler } from './route-table.ts';
|
|
14
16
|
export type { ValidationIssue, ValidationTarget } from '../schema/validation.ts';
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,6BAA6B,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAClI,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC/G,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjH,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,6BAA6B,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAClI,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAClH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC/G,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjH,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACtI,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACrI,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3L,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export { MountedGroup } from './mounted-group.js';
|
|
|
4
4
|
export { consoleLogger, silentLogger } from './logger.js';
|
|
5
5
|
export { Context } from './context.js';
|
|
6
6
|
export { badRequest, forbidden, HttpError, internal, isHttpError, notFound, unauthorized } from './errors.js';
|
|
7
|
-
export { ROUTER_METADATA, readOwnMetadata } from './metadata.js';
|
|
7
|
+
export { isResponseContent, ROUTER_METADATA, readOwnMetadata } from './metadata.js';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,6BAA6B,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAmF,MAAM,UAAU,CAAC;AAGlI,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,6BAA6B,CAAC;AAErC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAmF,MAAM,UAAU,CAAC;AAGlI,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMlD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAyD,MAAM,aAAa,CAAC;AACjH,OAAO,EAAE,OAAO,EAAiG,MAAM,cAAc,CAAC;AACtI,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAyB,MAAM,aAAa,CAAC;AACrI,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAyG,MAAM,eAAe,CAAC"}
|
package/dist/core/infer.d.ts
CHANGED
|
@@ -31,6 +31,14 @@ export type InferHeaders<S extends RouteSchemas> = InferSchema<S['headers'], Rec
|
|
|
31
31
|
* Útil para anotar o retorno do handler: `create(ctx): InferResponse<typeof create, 201>`.
|
|
32
32
|
*/
|
|
33
33
|
export type InferResponse<S extends RouteSchemas, Status extends number = number> = S['response'] extends Record<number, unknown> ? {
|
|
34
|
-
[K in keyof S['response'] & Status]: InferSchema<S['response'][K]
|
|
34
|
+
[K in keyof S['response'] & Status]: InferSchema<UnwrapResponse<S['response'][K]>>;
|
|
35
35
|
}[keyof S['response'] & Status] : unknown;
|
|
36
|
+
/** `{ contentType, schema }` → `schema`; um schema JSON direto continua como está (F-31). */
|
|
37
|
+
type UnwrapResponse<E> = E extends {
|
|
38
|
+
contentType: unknown;
|
|
39
|
+
schema: infer S;
|
|
40
|
+
} ? S : E extends {
|
|
41
|
+
contentType: unknown;
|
|
42
|
+
} ? undefined : E;
|
|
43
|
+
export {};
|
|
36
44
|
//# sourceMappingURL=infer.d.ts.map
|
package/dist/core/infer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../src/core/infer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,IAC3C;IAAC,WAAW,CAAC,CAAC,CAAC;CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,QAAQ,GACzC,WAAW,CAAC,CAAC,CAAC,SAAS;IAAE,WAAW,EAAE;QAAE,KAAK,CAAC,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC,CAAA;SAAE,GAAG,SAAS,CAAA;KAAE,CAAA;CAAE,GAAG,CAAC,GACvF,WAAW,CAAC,CAAC,CAAC,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,EAAE,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,GACxF,QAAQ,CAAC;AAEb,gDAAgD;AAChD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACnG,+CAA+C;AAC/C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;AAC5G,8CAA8C;AAC9C,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AAChF,sDAAsD;AACtD,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;AAE5H;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,SAAS,MAAM,GAAG,MAAM,IAC9E,CAAC,CAAC,UAAU,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACzC;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../src/core/infer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,QAAQ,GAAG,OAAO,IAC3C;IAAC,WAAW,CAAC,CAAC,CAAC;CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,QAAQ,GACzC,WAAW,CAAC,CAAC,CAAC,SAAS;IAAE,WAAW,EAAE;QAAE,KAAK,CAAC,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC,CAAA;SAAE,GAAG,SAAS,CAAA;KAAE,CAAA;CAAE,GAAG,CAAC,GACvF,WAAW,CAAC,CAAC,CAAC,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,EAAE,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,GACxF,QAAQ,CAAC;AAEb,gDAAgD;AAChD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACnG,+CAA+C;AAC/C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;AAC5G,8CAA8C;AAC9C,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AAChF,sDAAsD;AACtD,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;AAE5H;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,SAAS,MAAM,GAAG,MAAM,IAC9E,CAAC,CAAC,UAAU,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACzC;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,GACpH,OAAO,CAAC;AAEd,6FAA6F;AAC7F,KAAK,cAAc,CAAC,CAAC,IACnB,CAAC,SAAS;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GACrD,CAAC,SAAS;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAC9C,CAAC,CAAC"}
|
package/dist/core/metadata.d.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import type { Middleware } from './compose.ts';
|
|
2
2
|
import type { Guard } from './guard.ts';
|
|
3
3
|
export declare const ROUTER_METADATA: unique symbol;
|
|
4
|
+
/**
|
|
5
|
+
* Resposta com tipo de conteúdo declarado (F-31). Sem `contentType`, a entrada é um schema JSON.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* response: { 200: { contentType: ['application/json', 'text/csv'], schema: Users } }
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export interface ResponseContent {
|
|
12
|
+
/** Um tipo ou a lista de tipos oferecidos; com vários, o cliente escolhe pelo `Accept`. */
|
|
13
|
+
contentType: string | readonly string[];
|
|
14
|
+
/** Schema dos dados. Em formatos não JSON, valida e remove campos antes do serializador. */
|
|
15
|
+
schema?: object;
|
|
16
|
+
}
|
|
17
|
+
/** Schema JSON (como sempre) ou `{ contentType, schema? }`. */
|
|
18
|
+
export type ResponseEntry = object | ResponseContent;
|
|
4
19
|
export interface RouteSchemas {
|
|
5
20
|
params?: object;
|
|
6
21
|
body?: object;
|
|
7
22
|
query?: object;
|
|
8
23
|
headers?: object;
|
|
9
|
-
response?: Record<number,
|
|
24
|
+
response?: Record<number, ResponseEntry>;
|
|
10
25
|
}
|
|
26
|
+
export declare function isResponseContent(entry: unknown): entry is ResponseContent;
|
|
11
27
|
export interface MethodMetadata {
|
|
12
28
|
method?: string;
|
|
13
29
|
path?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/core/metadata.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,eAAO,MAAM,eAAe,eAA+B,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/core/metadata.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,eAAO,MAAM,eAAe,eAA+B,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,WAAW,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACxC,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,+DAA+D;AAC/D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,eAAe,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC1C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAI1E;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,uCAAuC;IACvC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,kCAAkC;IAClC,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;IAC9C,4EAA4E;IAC5E,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,kCAAkC;IAClC,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,cAAc,CAoBjF;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAKzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAe/E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAW/F;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,CAO7F"}
|
package/dist/core/metadata.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export const ROUTER_METADATA = Symbol.for('sashiko:router');
|
|
2
|
+
export function isResponseContent(entry) {
|
|
3
|
+
if (typeof entry !== 'object' || entry === null || !('contentType' in entry))
|
|
4
|
+
return false;
|
|
5
|
+
const ct = entry.contentType;
|
|
6
|
+
return typeof ct === 'string' || Array.isArray(ct);
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
9
|
* Retorna a metadata **própria** da classe.
|
|
4
10
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/core/metadata.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/core/metadata.ts"],"names":[],"mappings":"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"}
|
package/dist/core/response.d.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { type Context } from './context.ts';
|
|
2
|
+
import { type RouteSchemas } from './metadata.ts';
|
|
3
|
+
/** Converte dados num corpo de resposta para um content type (F-31). */
|
|
4
|
+
export type Serializer = (data: unknown) => string | Uint8Array;
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Com schemas de resposta declarados, um status sem schema correspondente **não** cai no
|
|
7
|
-
* `JSON.stringify`, que mandaria o objeto inteiro (S-07): vira 500 e o motivo vai para o log.
|
|
8
|
-
* - Respostas sem corpo (`undefined`) dispensam schema.
|
|
6
|
+
* Como o corpo chegou ao pipeline (F-32): `auto` (`return` e `ctx.send()`), `json` (`ctx.json()`,
|
|
7
|
+
* sempre JSON) ou `text` (`ctx.text()`, sempre texto).
|
|
9
8
|
*/
|
|
10
|
-
export
|
|
9
|
+
export type SendMode = 'auto' | 'json' | 'text';
|
|
10
|
+
/**
|
|
11
|
+
* Envia o retorno do handler.
|
|
12
|
+
*
|
|
13
|
+
* Status (B-05): sem `ctx.status()` e com um único 2xx declarado, esse 2xx vira o padrão. Com schemas
|
|
14
|
+
* de resposta declarados, um status sem schema correspondente vira 500 em vez de enviar o objeto
|
|
15
|
+
* sem filtro (S-07). Respostas sem corpo (`undefined`) dispensam schema.
|
|
16
|
+
*
|
|
17
|
+
* Content type (F-31), em ordem de prioridade:
|
|
18
|
+
* 1. o `Content-Type` definido pelo handler;
|
|
19
|
+
* 2. o `contentType` declarado no schema de resposta (com negociação pelo `Accept` se houver vários);
|
|
20
|
+
* 3. schema de resposta sem `contentType` → JSON;
|
|
21
|
+
* 4. sem schema, o tipo do valor: string → `text/plain`, Buffer/Uint8Array/stream → `application/octet-stream`,
|
|
22
|
+
* o resto → JSON.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sendResult(ctx: Context, response: RouteSchemas['response'], result: unknown, serializers?: ReadonlyMap<string, Serializer>, mode?: SendMode): void;
|
|
11
25
|
/**
|
|
12
26
|
* Resposta de erro padrão. Só mensagens marcadas como `expose` chegam ao cliente;
|
|
13
27
|
* o resto vira 500 genérico e o erro original é logado no servidor (S-05).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/core/response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/core/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAY,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAQrE,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,UAAU,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAIhD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,EAClC,MAAM,EAAE,OAAO,EACf,WAAW,GAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAa,EACxD,IAAI,GAAE,QAAiB,GACtB,IAAI,CAwCN;AAyDD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAc1D"}
|
package/dist/core/response.js
CHANGED
|
@@ -1,31 +1,107 @@
|
|
|
1
|
+
import { kRawSend } from './context.js';
|
|
1
2
|
import { HttpError } from './errors.js';
|
|
3
|
+
import { isResponseContent } from './metadata.js';
|
|
4
|
+
import { Readable } from 'node:stream';
|
|
2
5
|
import { serialize } from '../schema/serialization.js';
|
|
6
|
+
import { stripToSchema } from '../schema/validation.js';
|
|
7
|
+
import { isJsonContentType, mediaType, negotiate, withCharset } from '../utils/content-type.js';
|
|
3
8
|
const GENERIC_500 = 'Internal Server Error';
|
|
9
|
+
const JSON_TYPE = 'application/json';
|
|
4
10
|
/**
|
|
5
|
-
* Envia o retorno do handler
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
* - Respostas sem corpo (`undefined`) dispensam schema.
|
|
11
|
+
* Envia o retorno do handler.
|
|
12
|
+
*
|
|
13
|
+
* Status (B-05): sem `ctx.status()` e com um único 2xx declarado, esse 2xx vira o padrão. Com schemas
|
|
14
|
+
* de resposta declarados, um status sem schema correspondente vira 500 em vez de enviar o objeto
|
|
15
|
+
* sem filtro (S-07). Respostas sem corpo (`undefined`) dispensam schema.
|
|
16
|
+
*
|
|
17
|
+
* Content type (F-31), em ordem de prioridade:
|
|
18
|
+
* 1. o `Content-Type` definido pelo handler;
|
|
19
|
+
* 2. o `contentType` declarado no schema de resposta (com negociação pelo `Accept` se houver vários);
|
|
20
|
+
* 3. schema de resposta sem `contentType` → JSON;
|
|
21
|
+
* 4. sem schema, o tipo do valor: string → `text/plain`, Buffer/Uint8Array/stream → `application/octet-stream`,
|
|
22
|
+
* o resto → JSON.
|
|
10
23
|
*/
|
|
11
|
-
export function sendResult(ctx, response, result) {
|
|
24
|
+
export function sendResult(ctx, response, result, serializers = new Map(), mode = 'auto') {
|
|
12
25
|
if (response && !ctx.statusExplicit) {
|
|
13
|
-
const success = Object.keys(response).map(Number).filter((
|
|
26
|
+
const success = Object.keys(response).map(Number).filter((st) => st >= 200 && st < 300);
|
|
14
27
|
if (success.length === 1 && response[200] === undefined)
|
|
15
28
|
ctx.res.statusCode = success[0];
|
|
16
29
|
}
|
|
17
30
|
if (result === undefined)
|
|
18
31
|
return void ctx.res.end();
|
|
19
32
|
const status = ctx.res.statusCode;
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
22
|
-
ctx.res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
23
|
-
return void ctx.res.end(serialize(schema, result));
|
|
24
|
-
}
|
|
25
|
-
if (response && Object.keys(response).length > 0) {
|
|
33
|
+
const entry = response?.[status];
|
|
34
|
+
if (!entry && response && Object.keys(response).length > 0) {
|
|
26
35
|
throw new Error(`[sashiko] ${ctx.method} ${ctx.path}: no response schema for status ${status}`);
|
|
27
36
|
}
|
|
28
|
-
ctx.
|
|
37
|
+
const headerType = ctx.res.getHeader('Content-Type');
|
|
38
|
+
const explicit = mode === 'json' ? JSON_TYPE
|
|
39
|
+
: mode === 'text' ? 'text/plain'
|
|
40
|
+
: typeof headerType === 'string' ? headerType : undefined;
|
|
41
|
+
let type;
|
|
42
|
+
let schema;
|
|
43
|
+
let jsonSchemaDeclared = false;
|
|
44
|
+
if (entry && isResponseContent(entry)) {
|
|
45
|
+
schema = entry.schema;
|
|
46
|
+
const offered = typeof entry.contentType === 'string' ? [entry.contentType] : [...entry.contentType];
|
|
47
|
+
if (offered.length > 1)
|
|
48
|
+
ctx.res.appendHeader('Vary', 'Accept');
|
|
49
|
+
const chosen = explicit ?? negotiate(ctx.get('accept'), offered);
|
|
50
|
+
if (!chosen) {
|
|
51
|
+
throw new HttpError(406, 'Not Acceptable', 'NOT_ACCEPTABLE', { details: { available: offered } });
|
|
52
|
+
}
|
|
53
|
+
type = chosen;
|
|
54
|
+
}
|
|
55
|
+
else if (entry) {
|
|
56
|
+
schema = entry;
|
|
57
|
+
jsonSchemaDeclared = true;
|
|
58
|
+
type = explicit ?? JSON_TYPE;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
type = explicit ?? inferType(result);
|
|
62
|
+
}
|
|
63
|
+
write(ctx, type, result, schema, mode === 'json' || (jsonSchemaDeclared && !explicit), serializers);
|
|
64
|
+
}
|
|
65
|
+
function inferType(value) {
|
|
66
|
+
if (typeof value === 'string')
|
|
67
|
+
return 'text/plain';
|
|
68
|
+
if (value instanceof Uint8Array || value instanceof Readable)
|
|
69
|
+
return 'application/octet-stream';
|
|
70
|
+
return JSON_TYPE;
|
|
71
|
+
}
|
|
72
|
+
function write(ctx, type, value, schema, forceJson, serializers) {
|
|
73
|
+
const header = withCharset(type);
|
|
74
|
+
const base = mediaType(type);
|
|
75
|
+
const json = isJsonContentType(base);
|
|
76
|
+
if (value instanceof Readable)
|
|
77
|
+
return void ctx[kRawSend](value, header);
|
|
78
|
+
// Valores crus (string/Buffer) vão como estão, exceto quando a rota declarou um schema JSON:
|
|
79
|
+
// nesse caso `"olá"` continua sendo serializado como JSON, como sempre.
|
|
80
|
+
if ((typeof value === 'string' || value instanceof Uint8Array) && !forceJson) {
|
|
81
|
+
if (schema && typeof value === 'string')
|
|
82
|
+
ensureValid(ctx, schema, value);
|
|
83
|
+
return void ctx[kRawSend](value, header);
|
|
84
|
+
}
|
|
85
|
+
if (json) {
|
|
86
|
+
ctx.res.setHeader('Content-Type', header);
|
|
87
|
+
return void ctx.res.end(schema ? serialize(schema, value) : JSON.stringify(value));
|
|
88
|
+
}
|
|
89
|
+
const serializer = serializers.get(base);
|
|
90
|
+
if (!serializer) {
|
|
91
|
+
throw new Error(`[sashiko] ${ctx.method} ${ctx.path}: no serializer registered for "${base}". ` +
|
|
92
|
+
`Add it to createApp({ serializers: { '${base}': (data) => ... } }).`);
|
|
93
|
+
}
|
|
94
|
+
const data = schema ? ensureValid(ctx, schema, value) : value;
|
|
95
|
+
ctx[kRawSend](Buffer.from(serializer(data)), header);
|
|
96
|
+
}
|
|
97
|
+
/** Valida a saída e devolve uma cópia sem campos não declarados (S-07 em qualquer formato). */
|
|
98
|
+
function ensureValid(ctx, schema, value) {
|
|
99
|
+
const checked = stripToSchema(schema, value);
|
|
100
|
+
if (!checked.ok) {
|
|
101
|
+
throw new Error(`[sashiko] ${ctx.method} ${ctx.path}: response does not match its schema: ` +
|
|
102
|
+
checked.issues.map((i) => `${i.path} ${i.message}`).join('; '));
|
|
103
|
+
}
|
|
104
|
+
return checked.value;
|
|
29
105
|
}
|
|
30
106
|
/**
|
|
31
107
|
* Resposta de erro padrão. Só mensagens marcadas como `expose` chegam ao cliente;
|
|
@@ -44,6 +120,8 @@ export function sendError(ctx, err) {
|
|
|
44
120
|
const body = { error: exposed ? httpErr.message : GENERIC_500 };
|
|
45
121
|
if (exposed && httpErr.details !== undefined)
|
|
46
122
|
body.details = httpErr.details;
|
|
47
|
-
|
|
123
|
+
// Erros nunca passam pelo schema da rota (F-32): envio cru.
|
|
124
|
+
ctx.res.statusCode = status;
|
|
125
|
+
ctx[kRawSend](JSON.stringify(body), 'application/json; charset=utf-8');
|
|
48
126
|
}
|
|
49
127
|
//# sourceMappingURL=response.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/core/response.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/core/response.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Logger } from './logger.ts';
|
|
2
|
+
export interface ShutdownOptions {
|
|
3
|
+
/** Sinais tratados. Padrão: `['SIGINT', 'SIGTERM']`. */
|
|
4
|
+
signals?: readonly NodeJS.Signals[];
|
|
5
|
+
/** Prazo para as requisições em andamento terminarem. Padrão: 10s. */
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
/** Executado depois que o servidor fecha (banco, filas…). Se lançar, o processo sai com código 1. */
|
|
8
|
+
onShutdown?: (signal: NodeJS.Signals) => void | Promise<void>;
|
|
9
|
+
/** Chama `process.exit` ao final. Padrão: `true`. Use `false` se outro código controla o processo. */
|
|
10
|
+
exit?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** O que o coordenador precisa de cada app que está ouvindo. */
|
|
13
|
+
export interface ShutdownTarget {
|
|
14
|
+
readonly log: Logger;
|
|
15
|
+
/** Fecha o app e roda o `onShutdown`; resolve com `false` se algo falhou. */
|
|
16
|
+
shutdown(signal: NodeJS.Signals): Promise<boolean>;
|
|
17
|
+
/** Se este app quer `process.exit` ao final. */
|
|
18
|
+
readonly exitOnShutdown: boolean;
|
|
19
|
+
readonly signals: readonly NodeJS.Signals[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Coordena o encerramento de todos os apps ouvindo no processo (F-07):
|
|
23
|
+
* um único listener por sinal, compartilhado, removido quando o último app fecha.
|
|
24
|
+
*/
|
|
25
|
+
declare class ShutdownCoordinator {
|
|
26
|
+
private readonly targets;
|
|
27
|
+
private readonly listeners;
|
|
28
|
+
private shuttingDown;
|
|
29
|
+
register(target: ShutdownTarget): void;
|
|
30
|
+
unregister(target: ShutdownTarget): void;
|
|
31
|
+
private prune;
|
|
32
|
+
private onSignal;
|
|
33
|
+
}
|
|
34
|
+
export declare const shutdownCoordinator: ShutdownCoordinator;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=shutdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../src/core/shutdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,SAAS,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qGAAqG;IACrG,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,sGAAsG;IACtG,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,CAAC,OAAO,EAAE,CAAC;CAC7C;AAED;;;GAGG;AACH,cAAM,mBAAmB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyC;IACnE,OAAO,CAAC,YAAY,CAAS;IAE7B,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAQrC;IAED,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAKvC;IAED,OAAO,CAAC,KAAK;YASC,QAAQ;CAgBvB;AAED,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordena o encerramento de todos os apps ouvindo no processo (F-07):
|
|
3
|
+
* um único listener por sinal, compartilhado, removido quando o último app fecha.
|
|
4
|
+
*/
|
|
5
|
+
class ShutdownCoordinator {
|
|
6
|
+
targets = new Set();
|
|
7
|
+
listeners = new Map();
|
|
8
|
+
shuttingDown = false;
|
|
9
|
+
register(target) {
|
|
10
|
+
this.targets.add(target);
|
|
11
|
+
for (const signal of target.signals) {
|
|
12
|
+
if (this.listeners.has(signal))
|
|
13
|
+
continue;
|
|
14
|
+
const listener = () => void this.onSignal(signal);
|
|
15
|
+
this.listeners.set(signal, listener);
|
|
16
|
+
process.on(signal, listener);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
unregister(target) {
|
|
20
|
+
this.targets.delete(target);
|
|
21
|
+
// Durante um encerramento, os listeners ficam até o fim: o segundo sinal precisa ser ouvido
|
|
22
|
+
// enquanto o onShutdown ainda roda (ex.: fechando o banco).
|
|
23
|
+
if (!this.shuttingDown)
|
|
24
|
+
this.prune();
|
|
25
|
+
}
|
|
26
|
+
prune() {
|
|
27
|
+
const stillUsed = new Set([...this.targets].flatMap((t) => t.signals));
|
|
28
|
+
for (const [signal, listener] of this.listeners) {
|
|
29
|
+
if (stillUsed.has(signal))
|
|
30
|
+
continue;
|
|
31
|
+
process.off(signal, listener);
|
|
32
|
+
this.listeners.delete(signal);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async onSignal(signal) {
|
|
36
|
+
const targets = [...this.targets].filter((t) => t.signals.includes(signal));
|
|
37
|
+
if (this.shuttingDown) {
|
|
38
|
+
// Segundo sinal durante o encerramento: o usuário quer sair já (Ctrl+C duplo).
|
|
39
|
+
targets[0]?.log.warn({ signal }, 'second signal received, forcing exit');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
this.shuttingDown = true;
|
|
43
|
+
targets[0]?.log.info({ signal }, 'shutting down gracefully');
|
|
44
|
+
const results = await Promise.all(targets.map((t) => t.shutdown(signal)));
|
|
45
|
+
const ok = results.every(Boolean);
|
|
46
|
+
this.shuttingDown = false;
|
|
47
|
+
this.prune();
|
|
48
|
+
if (targets.some((t) => t.exitOnShutdown))
|
|
49
|
+
process.exit(ok ? 0 : 1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export const shutdownCoordinator = new ShutdownCoordinator();
|
|
53
|
+
//# sourceMappingURL=shutdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/core/shutdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { cors, type CorsOptions, type CorsOrigin } from './cors.ts';
|
|
2
2
|
export { MemoryRateLimitStore, rateLimit, type RateLimitOptions, type RateLimitStore } from './rate-limit.ts';
|
|
3
|
-
export { buildSecurityHeaders, type SecurityHeadersOptions } from './security-headers.ts';
|
|
3
|
+
export { buildSecurityHeaders, type SecurityHeadersOptions, type SecurityPreset } from './security-headers.ts';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAqC,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAA8C,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAqC,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAA8C,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAoD,MAAM,uBAAuB,CAAC"}
|