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,50 @@
|
|
|
1
|
+
import type { Context } from './context.ts';
|
|
2
|
+
export interface HttpErrorOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Se a mensagem pode ser enviada ao cliente.
|
|
5
|
+
* Padrão: `true` para 4xx e `false` para 5xx, para não vazar detalhes internos (S-05).
|
|
6
|
+
*/
|
|
7
|
+
expose?: boolean;
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
/** Dados extras enviados ao cliente junto da mensagem (ex.: erros de validação). */
|
|
10
|
+
details?: unknown;
|
|
11
|
+
/** URI que identifica o tipo do problema (RFC 9457). Padrão: `'about:blank'`. */
|
|
12
|
+
type?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Corpo de erro no formato Problem Details (RFC 9457), enviado como `application/problem+json`.
|
|
16
|
+
* `code` e `details` são membros de extensão do FlowingTS.
|
|
17
|
+
*/
|
|
18
|
+
export interface ProblemDetails {
|
|
19
|
+
/** URI do tipo do problema. `'about:blank'` quando o status já diz tudo. */
|
|
20
|
+
type: string;
|
|
21
|
+
/** Resumo do tipo do problema; com `about:blank`, é a frase do status (`'Not Found'`). */
|
|
22
|
+
title: string;
|
|
23
|
+
status: number;
|
|
24
|
+
/** Explicação desta ocorrência. Omitido em 5xx não expostos. */
|
|
25
|
+
detail?: string;
|
|
26
|
+
/** Código estável para o cliente tratar o erro (`'VALIDATION_ERROR'`). */
|
|
27
|
+
code?: string;
|
|
28
|
+
/** Dados extras, como os erros de validação. */
|
|
29
|
+
details?: unknown;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Personaliza o corpo de erro. Recebe o problema já montado (e sem dados internos em 5xx) e
|
|
33
|
+
* devolve o corpo a enviar como JSON. Use para manter um formato legado ou adicionar campos.
|
|
34
|
+
*/
|
|
35
|
+
export type ErrorFormatter = (problem: ProblemDetails, ctx: Context) => unknown;
|
|
36
|
+
export declare class HttpError extends Error {
|
|
37
|
+
readonly status: number;
|
|
38
|
+
readonly code: string | undefined;
|
|
39
|
+
readonly expose: boolean;
|
|
40
|
+
readonly details: unknown;
|
|
41
|
+
readonly type: string | undefined;
|
|
42
|
+
constructor(status: number, message: string, code?: string, options?: HttpErrorOptions);
|
|
43
|
+
}
|
|
44
|
+
export declare function isHttpError(err: unknown): err is HttpError;
|
|
45
|
+
export declare const notFound: (msg?: string) => HttpError;
|
|
46
|
+
export declare const badRequest: (msg?: string) => HttpError;
|
|
47
|
+
export declare const unauthorized: (msg?: string) => HttpError;
|
|
48
|
+
export declare const forbidden: (msg?: string) => HttpError;
|
|
49
|
+
export declare const internal: (msg?: string) => HttpError;
|
|
50
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;AAEhF,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAElC,YAAY,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,EAQzF;CACF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS,CAE1D;AAED,eAAO,MAAM,QAAQ,6BAA8D,CAAC;AACpF,eAAO,MAAM,UAAU,6BAAkE,CAAC;AAC1F,eAAO,MAAM,YAAY,6BAAoE,CAAC;AAC9F,eAAO,MAAM,SAAS,6BAA8D,CAAC;AACrF,eAAO,MAAM,QAAQ,6BAAyE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class HttpError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
expose;
|
|
5
|
+
details;
|
|
6
|
+
type;
|
|
7
|
+
constructor(status, message, code, options = {}) {
|
|
8
|
+
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
9
|
+
this.name = 'HttpError';
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.expose = options.expose ?? status < 500;
|
|
13
|
+
this.details = options.details;
|
|
14
|
+
this.type = options.type;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function isHttpError(err) {
|
|
18
|
+
return err instanceof HttpError;
|
|
19
|
+
}
|
|
20
|
+
export const notFound = (msg = 'Not Found') => new HttpError(404, msg, 'NOT_FOUND');
|
|
21
|
+
export const badRequest = (msg = 'Bad Request') => new HttpError(400, msg, 'BAD_REQUEST');
|
|
22
|
+
export const unauthorized = (msg = 'Unauthorized') => new HttpError(401, msg, 'UNAUTHORIZED');
|
|
23
|
+
export const forbidden = (msg = 'Forbidden') => new HttpError(403, msg, 'FORBIDDEN');
|
|
24
|
+
export const internal = (msg = 'Internal Server Error') => new HttpError(500, msg, 'INTERNAL');
|
|
25
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAuCA,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,MAAM,CAAS;IACf,IAAI,CAAqB;IACzB,MAAM,CAAU;IAChB,OAAO,CAAU;IACjB,IAAI,CAAqB;IAElC,YAAY,MAAc,EAAE,OAAe,EAAE,IAAa,EAAE,OAAO,GAAqB,EAAE;QACxF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,GAAG,GAAG,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,OAAO,GAAG,YAAY,SAAS,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,GAAG,WAAW,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;AACpF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAG,GAAG,aAAa,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;AAC1F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAG,GAAG,cAAc,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;AAC9F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAG,GAAG,WAAW,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;AACrF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,GAAG,uBAAuB,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from './context.ts';
|
|
2
|
+
import type { Middleware } from './compose.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Decide se a requisição pode seguir para o handler (F-01).
|
|
5
|
+
* Retorne `false` para responder 403, ou lance um `HttpError` (ex.: `unauthorized()`) para outro status.
|
|
6
|
+
*/
|
|
7
|
+
export type Guard = (ctx: Context) => boolean | Promise<boolean>;
|
|
8
|
+
/** Executa os guards em ordem; o primeiro que negar interrompe a requisição. */
|
|
9
|
+
export declare function guardsMiddleware(guards: readonly Guard[]): Middleware;
|
|
10
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/core/guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI/C;;;GAGG;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjE,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,UAAU,CAQrE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HttpError } from './errors.js';
|
|
2
|
+
import { kPhase } from './symbols.js';
|
|
3
|
+
/** Executa os guards em ordem; o primeiro que negar interrompe a requisição. */
|
|
4
|
+
export function guardsMiddleware(guards) {
|
|
5
|
+
return async (ctx, next) => {
|
|
6
|
+
ctx[kPhase] = 'guard';
|
|
7
|
+
for (const guard of guards) {
|
|
8
|
+
if (!(await guard(ctx)))
|
|
9
|
+
throw new HttpError(403, 'Forbidden', 'FORBIDDEN');
|
|
10
|
+
}
|
|
11
|
+
await next();
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/core/guard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQtC,gFAAgF;AAChF,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACvD,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Context } from './context.ts';
|
|
2
|
+
import { type Middleware } from './compose.ts';
|
|
3
|
+
import { type Guard } from './guard.ts';
|
|
4
|
+
import type { Logger } from './logger.ts';
|
|
5
|
+
/** Uma verificação de prontidão: lance um erro ou devolva `false` para indicar falha. */
|
|
6
|
+
export type HealthCheck = () => unknown | Promise<unknown>;
|
|
7
|
+
export interface HealthOptions {
|
|
8
|
+
/** Liveness. Padrão: `'/livez'`; `false` desliga. */
|
|
9
|
+
live?: string | false;
|
|
10
|
+
/** Readiness. Padrão: `'/readyz'`; `false` desliga. */
|
|
11
|
+
ready?: string | false;
|
|
12
|
+
/** Verificações do `/readyz`, rodadas em paralelo: `{ db: () => db.ping() }`. */
|
|
13
|
+
checks?: Record<string, HealthCheck>;
|
|
14
|
+
/** Tempo máximo de cada verificação. Padrão: 2000 ms. */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Rodam antes de responder (os middlewares globais não rodam nestas rotas). */
|
|
17
|
+
middlewares?: readonly Middleware[];
|
|
18
|
+
guards?: readonly Guard[];
|
|
19
|
+
/**
|
|
20
|
+
* Responde mesmo com um `Host` fora do `allowedHosts`. Sondas de orquestradores chegam com o IP do pod
|
|
21
|
+
* no `Host` (Kubernetes, ECS). Um `Host` malformado continua recebendo 400. Padrão: `true`.
|
|
22
|
+
*/
|
|
23
|
+
anyHost?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** @internal */
|
|
26
|
+
export interface HealthConfig {
|
|
27
|
+
/** Responde se a requisição for para uma das rotas; devolve `true` nesse caso. */
|
|
28
|
+
serve(ctx: Context, shuttingDown: boolean): Promise<boolean>;
|
|
29
|
+
/** Paths das rotas ligadas, na forma canônica. */
|
|
30
|
+
readonly paths: readonly string[];
|
|
31
|
+
/** A requisição é para um dos healthchecks (checagem síncrona, sem Promise). */
|
|
32
|
+
handles(method: string, path: string): boolean;
|
|
33
|
+
/** A requisição é para um healthcheck que aceita qualquer host? */
|
|
34
|
+
bypassesHostCheck(method: string, path: string): boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function compileHealth(opts: HealthOptions, log: Logger): HealthConfig;
|
|
37
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,gBAAgB;AAChB,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,gFAAgF;IAChF,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/C,mEAAmE;IACnE,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1D;AAeD,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAqD5E"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { compose } from './compose.js';
|
|
2
|
+
import { kRawSend } from './symbols.js';
|
|
3
|
+
import { guardsMiddleware } from './guard.js';
|
|
4
|
+
import { canonicalPath } from '../utils/path.js';
|
|
5
|
+
function withTimeout(check, ms) {
|
|
6
|
+
let timer;
|
|
7
|
+
const timeout = new Promise((resolve) => {
|
|
8
|
+
timer = setTimeout(() => resolve('timeout'), ms);
|
|
9
|
+
});
|
|
10
|
+
const run = Promise.resolve()
|
|
11
|
+
.then(check)
|
|
12
|
+
.then((v) => (v === false ? 'error' : 'ok'));
|
|
13
|
+
return Promise.race([run, timeout]).finally(() => clearTimeout(timer));
|
|
14
|
+
}
|
|
15
|
+
export function compileHealth(opts, log) {
|
|
16
|
+
const live = opts.live === undefined ? '/livez' : opts.live;
|
|
17
|
+
const ready = opts.ready === undefined ? '/readyz' : opts.ready;
|
|
18
|
+
if (live && ready && canonicalPath(live) === canonicalPath(ready)) {
|
|
19
|
+
throw new Error(`[flowing] health(): "live" and "ready" cannot both be "${live}".`);
|
|
20
|
+
}
|
|
21
|
+
const livePath = live === false ? undefined : canonicalPath(live);
|
|
22
|
+
const readyPath = ready === false ? undefined : canonicalPath(ready);
|
|
23
|
+
const anyHost = opts.anyHost ?? true;
|
|
24
|
+
const isHealth = (method, path) => (method === 'GET' || method === 'HEAD') && (canonicalPath(path) === livePath || canonicalPath(path) === readyPath);
|
|
25
|
+
const checks = Object.entries(opts.checks ?? {});
|
|
26
|
+
const timeoutMs = opts.timeoutMs ?? 2000;
|
|
27
|
+
const pipeline = [...(opts.middlewares ?? [])];
|
|
28
|
+
if (opts.guards?.length)
|
|
29
|
+
pipeline.push(guardsMiddleware(opts.guards));
|
|
30
|
+
const reply = (ctx, status, body) => {
|
|
31
|
+
ctx.res.statusCode = status;
|
|
32
|
+
ctx.res.setHeader('Cache-Control', 'no-store');
|
|
33
|
+
ctx[kRawSend](JSON.stringify(body), 'application/json; charset=utf-8');
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
paths: [livePath, readyPath].filter((p) => p !== undefined),
|
|
37
|
+
handles: isHealth,
|
|
38
|
+
bypassesHostCheck: (method, path) => anyHost && isHealth(method, path),
|
|
39
|
+
async serve(ctx, shuttingDown) {
|
|
40
|
+
if (!isHealth(ctx.method, ctx.path))
|
|
41
|
+
return false;
|
|
42
|
+
const isLive = canonicalPath(ctx.path) === livePath;
|
|
43
|
+
await compose(ctx, [
|
|
44
|
+
...pipeline,
|
|
45
|
+
async () => {
|
|
46
|
+
if (isLive)
|
|
47
|
+
return reply(ctx, 200, { status: 'ok' });
|
|
48
|
+
if (shuttingDown)
|
|
49
|
+
return reply(ctx, 503, { status: 'shutting_down' });
|
|
50
|
+
const results = await Promise.all(checks.map(async ([name, check]) => {
|
|
51
|
+
const result = await withTimeout(check, timeoutMs).catch((err) => {
|
|
52
|
+
// O motivo vai para o log, nunca para a resposta (pode conter host, usuário do banco…).
|
|
53
|
+
log.warn({ err, check: name }, 'health check failed');
|
|
54
|
+
return 'error';
|
|
55
|
+
});
|
|
56
|
+
if (result === 'timeout')
|
|
57
|
+
log.warn({ check: name, timeoutMs }, 'health check timed out');
|
|
58
|
+
return [name, result];
|
|
59
|
+
}));
|
|
60
|
+
const failed = results.some(([, r]) => r !== 'ok');
|
|
61
|
+
reply(ctx, failed ? 503 : 200, { status: failed ? 'error' : 'ok', checks: Object.fromEntries(results) });
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
return true;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { OnErrorHook, OnResponseHook } from './app/options.ts';
|
|
2
|
+
import type { OnSendHook } from './context.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Hooks com escopo (F-43): os mesmos `onSend`, `onResponse` e `onError` do app, declarados num grupo, num
|
|
5
|
+
* `mount()`, numa classe, num método ou numa rota manual. Só rodam quando uma rota daquele escopo atendeu.
|
|
6
|
+
*/
|
|
7
|
+
export interface HookOptions {
|
|
8
|
+
onSend?: OnSendHook | readonly OnSendHook[];
|
|
9
|
+
onResponse?: OnResponseHook | readonly OnResponseHook[];
|
|
10
|
+
onError?: OnErrorHook | readonly OnErrorHook[];
|
|
11
|
+
}
|
|
12
|
+
/** Hooks acumulados de um escopo, de fora para dentro. */
|
|
13
|
+
export interface ScopedHooks {
|
|
14
|
+
onSend: OnSendHook[];
|
|
15
|
+
onResponse: OnResponseHook[];
|
|
16
|
+
onError: OnErrorHook[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* As listas prontas de uma rota, montadas no registro (nada por requisição). `onError` já vem do mais
|
|
20
|
+
* específico para o mais externo, que é a ordem de execução.
|
|
21
|
+
*/
|
|
22
|
+
export interface RouteHooks {
|
|
23
|
+
readonly onSend: readonly OnSendHook[];
|
|
24
|
+
readonly onResponse: readonly OnResponseHook[];
|
|
25
|
+
readonly onError: readonly OnErrorHook[];
|
|
26
|
+
}
|
|
27
|
+
export declare const emptyHooks: () => ScopedHooks;
|
|
28
|
+
/** Normaliza as opções (`fn` ou `[fn, fn]`) em listas. */
|
|
29
|
+
export declare function hookOptions(opts: HookOptions | undefined): ScopedHooks;
|
|
30
|
+
/** Junta escopos na ordem de fora para dentro. */
|
|
31
|
+
export declare function concatHooks(...scopes: readonly ScopedHooks[]): ScopedHooks;
|
|
32
|
+
/** As listas finais da rota, ou `undefined` se ela não tem nenhum hook (o caso comum). */
|
|
33
|
+
export declare function routeHooks(scoped: ScopedHooks): RouteHooks | undefined;
|
|
34
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/core/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC;IAC5C,UAAU,CAAC,EAAE,cAAc,GAAG,SAAS,cAAc,EAAE,CAAC;IACxD,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAC;CAChD;AAED,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,eAAO,MAAM,UAAU,QAAO,WAA4D,CAAC;AAI3F,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,CAEtE;AAED,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CAQ1E;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAOtE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const emptyHooks = () => ({ onSend: [], onResponse: [], onError: [] });
|
|
2
|
+
const list = (value) => (value === undefined ? [] : Array.isArray(value) ? [...value] : [value]);
|
|
3
|
+
/** Normaliza as opções (`fn` ou `[fn, fn]`) em listas. */
|
|
4
|
+
export function hookOptions(opts) {
|
|
5
|
+
return { onSend: list(opts?.onSend), onResponse: list(opts?.onResponse), onError: list(opts?.onError) };
|
|
6
|
+
}
|
|
7
|
+
/** Junta escopos na ordem de fora para dentro. */
|
|
8
|
+
export function concatHooks(...scopes) {
|
|
9
|
+
const out = emptyHooks();
|
|
10
|
+
for (const s of scopes) {
|
|
11
|
+
out.onSend.push(...s.onSend);
|
|
12
|
+
out.onResponse.push(...s.onResponse);
|
|
13
|
+
out.onError.push(...s.onError);
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
/** As listas finais da rota, ou `undefined` se ela não tem nenhum hook (o caso comum). */
|
|
18
|
+
export function routeHooks(scoped) {
|
|
19
|
+
if (scoped.onSend.length + scoped.onResponse.length + scoped.onError.length === 0)
|
|
20
|
+
return undefined;
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
onSend: Object.freeze([...scoped.onSend]),
|
|
23
|
+
onResponse: Object.freeze([...scoped.onResponse]),
|
|
24
|
+
onError: Object.freeze([...scoped.onError].reverse()),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/core/hooks.ts"],"names":[],"mappings":"AA8BA,MAAM,CAAC,MAAM,UAAU,GAAG,GAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AAE3F,MAAM,IAAI,GAAG,CAAI,KAAmC,EAAO,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAU,CAAC,CAAC,CAAC;AAE5I,0DAA0D;AAC1D,MAAM,UAAU,WAAW,CAAC,IAA6B;IACvD,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;AAC1G,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,WAAW,CAAC,GAAG,MAA8B;IAC3D,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7B,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QACrC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACpG,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;KACtD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import '../shims/symbol-metadata.ts';
|
|
2
|
+
export { createApp, FlowingApp, type AppOptions, type ListenOptions, type OnCloseHook, type OnErrorHook, type OnRequestHook, type OnResponseHook, type ResponseInfo, type RequestIdOptions, type ServerTimeouts, } from './app.ts';
|
|
3
|
+
export type { InjectOptions, InjectResponse } from './inject.ts';
|
|
4
|
+
export type { GroupOptions, HostOption, ManualRouteOptions, MethodSelector, MethodsSelector, MountOptions, RouterRef } from './compiler.ts';
|
|
5
|
+
export type { HookOptions } from './hooks.ts';
|
|
6
|
+
export type { QueryParser } from '../utils/query.ts';
|
|
7
|
+
export { type Contracts, contracts, type StandardContractsOptions, type StandardSchemaV1 } from './contracts.ts';
|
|
8
|
+
export { detectors } from './uploads/receive.ts';
|
|
9
|
+
export type { ContentDetector, FileInfo, FileRule, FileRules, Size, UploadedFile, UploadsOptions } from './uploads/types.ts';
|
|
10
|
+
export type { BodyParser } from './body-parser.ts';
|
|
11
|
+
export { MountedGroup, RouteGroupScope } from './mounted-group.ts';
|
|
12
|
+
export type { Middleware } from './compose.ts';
|
|
13
|
+
export type { Guard } from './guard.ts';
|
|
14
|
+
export type { CheckParams, InferBody, InferHeaders, InferParams, InferQuery, InferResponse, InferSchema, ParamsOf, PathParams, } from './infer.ts';
|
|
15
|
+
export type { ShutdownOptions } from './shutdown.ts';
|
|
16
|
+
export type { HealthCheck, HealthOptions } from './health.ts';
|
|
17
|
+
export type { Serializer } from './response/pipeline.ts';
|
|
18
|
+
export type { CookieOptions } from '../utils/cookie.ts';
|
|
19
|
+
export type { TlsCertificate, TlsOptions } from '../server/tls.ts';
|
|
20
|
+
export type { FileOptions, SendFileOptions, StaticOptions } from '../server/static.ts';
|
|
21
|
+
export { consoleLogger, DEFAULT_REDACT_KEYS, redactValue, silentLogger, type ConsoleLoggerOptions, type Logger, type LogLevel, } from './logger.ts';
|
|
22
|
+
export { Context, type BodyFor, type OnSendHook, type SendPayload, type ContextOptions, type ContextState, type DeclaredStatus, type ParamsFor, type SchemasOf, type SseEvent, type SseOptions, type SseStream, type StatusContext, } from './context.ts';
|
|
23
|
+
export { badRequest, forbidden, HttpError, internal, isHttpError, notFound, unauthorized, type ErrorFormatter, type HttpErrorOptions, type ProblemDetails, } from './errors.ts';
|
|
24
|
+
export { isResponseContent, ROUTER_METADATA, readOwnMetadata, type ResponseContent, type ResponseEntry, type RouterMetadata, type MethodMetadata, type RouteSchemas, } from './metadata.ts';
|
|
25
|
+
export type { Handler, RouteInfo } from './route-table.ts';
|
|
26
|
+
export { type AbortCode, type ErrorPhase, RequestAbortedError } from './abort.ts';
|
|
27
|
+
export type { TraceContext } from '../utils/trace-context.ts';
|
|
28
|
+
export type { TelemetryApi, TelemetrySpan } from './telemetry.ts';
|
|
29
|
+
export type { ValidationIssue, ValidationTarget } from '../schema/validation.ts';
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import '../shims/symbol-metadata.js';
|
|
2
|
+
export { createApp, FlowingApp, } from './app.js';
|
|
3
|
+
export { contracts } from './contracts.js';
|
|
4
|
+
export { detectors } from './uploads/receive.js';
|
|
5
|
+
export { MountedGroup, RouteGroupScope } from './mounted-group.js';
|
|
6
|
+
export { consoleLogger, DEFAULT_REDACT_KEYS, redactValue, silentLogger, } from './logger.js';
|
|
7
|
+
export { Context, } from './context.js';
|
|
8
|
+
export { badRequest, forbidden, HttpError, internal, isHttpError, notFound, unauthorized, } from './errors.js';
|
|
9
|
+
export { isResponseContent, ROUTER_METADATA, readOwnMetadata, } from './metadata.js';
|
|
10
|
+
export { RequestAbortedError } from './abort.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,6BAA6B,CAAC;AAErC,OAAO,EACL,SAAS,EACT,UAAU,GAUX,MAAM,UAAU,CAAC;AAKlB,OAAO,EAAkB,SAAS,EAAwD,MAAM,gBAAgB,CAAC;AACjH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAoBnE,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,YAAY,GAIb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,OAAO,GAaR,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,YAAY,GAIb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,eAAe,GAMhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAmC,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { RouteSchemas } from './metadata.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Inferência estrutural de tipos a partir de schemas (F-02), sem importar nenhuma biblioteca:
|
|
4
|
+
* - TypeBox: lê o campo fantasma `static` (o mesmo que `Static<T>` usa).
|
|
5
|
+
* - Standard Schema (Zod, Valibot, ArkType): lê `~standard.types.output` (prepara a F-27).
|
|
6
|
+
* Qualquer outro schema cai no `Fallback`.
|
|
7
|
+
*/
|
|
8
|
+
export type InferSchema<T, Fallback = unknown> = [NonNullable<T>] extends [never] ? Fallback : NonNullable<T> extends {
|
|
9
|
+
'~standard': {
|
|
10
|
+
types?: {
|
|
11
|
+
output: infer O;
|
|
12
|
+
} | undefined;
|
|
13
|
+
};
|
|
14
|
+
} ? O : NonNullable<T> extends {
|
|
15
|
+
static: unknown;
|
|
16
|
+
} ? (NonNullable<T> & {
|
|
17
|
+
params: [];
|
|
18
|
+
})['static'] : Fallback;
|
|
19
|
+
/** Tipo de `ctx.params` para os schemas `S`. */
|
|
20
|
+
export type InferParams<S extends RouteSchemas> = InferSchema<S['params'], Record<string, string>>;
|
|
21
|
+
/** Tipo de `ctx.query` para os schemas `S`. */
|
|
22
|
+
export type InferQuery<S extends RouteSchemas> = InferSchema<S['query'], Record<string, string | string[]>>;
|
|
23
|
+
/** Tipo de `ctx.body` para os schemas `S`. */
|
|
24
|
+
export type InferBody<S extends RouteSchemas> = InferSchema<S['body'], unknown>;
|
|
25
|
+
/** Tipo dos headers validados para os schemas `S`. */
|
|
26
|
+
export type InferHeaders<S extends RouteSchemas> = InferSchema<S['headers'], Record<string, string | string[] | undefined>>;
|
|
27
|
+
/**
|
|
28
|
+
* União dos tipos de resposta declarados (ou de um status específico).
|
|
29
|
+
* Útil para anotar o retorno do handler: `create(ctx): InferResponse<typeof create, 201>`.
|
|
30
|
+
*/
|
|
31
|
+
export type InferResponse<S extends RouteSchemas, Status extends number = number> = S['response'] extends Record<number, unknown> ? {
|
|
32
|
+
[K in keyof S['response'] & Status]: InferSchema<UnwrapResponse<S['response'][K]>>;
|
|
33
|
+
}[keyof S['response'] & Status] : unknown;
|
|
34
|
+
/** `{ contentType, schema }` → `schema`; um schema JSON direto continua como está (F-31). */
|
|
35
|
+
type UnwrapResponse<E> = E extends {
|
|
36
|
+
contentType: unknown;
|
|
37
|
+
schema: infer S;
|
|
38
|
+
} ? S : E extends {
|
|
39
|
+
contentType: unknown;
|
|
40
|
+
} ? undefined : E;
|
|
41
|
+
/** Corta o nome de um parâmetro no primeiro delimitador do find-my-way (`/`, `-`, `.`, `(`). */
|
|
42
|
+
type ParamHead<R extends string, Acc extends string = ''> = R extends `${infer C}${infer T}` ? C extends '/' | '-' | '.' | '(' ? [Acc, R] : ParamHead<T, `${Acc}${C}`> : [Acc, ''];
|
|
43
|
+
/** Descarta a regex de um parâmetro (`(^\\d+)…` → `…`). */
|
|
44
|
+
type SkipRegex<R extends string> = R extends `(${string})${infer Rest}` ? Rest : R;
|
|
45
|
+
type ParamNames<P extends string> = P extends `${string}:${infer R}` ? R extends `:${infer Escaped}` ? ParamNames<Escaped> : ParamHead<R> extends [infer N extends string, infer Rest extends string] ? N | ParamNames<SkipRegex<Rest>> : never : never;
|
|
46
|
+
/**
|
|
47
|
+
* Nomes dos parâmetros de um path: `PathParams<'/:orgId/users/:id'>` → `'orgId' | 'id'`.
|
|
48
|
+
* Regex (`/:id(^\\d+)`) é ignorada, e o curinga final (`/files/*`) vira `'*'`.
|
|
49
|
+
* Com um `string` não literal, resulta em `string`.
|
|
50
|
+
*/
|
|
51
|
+
export type PathParams<P extends string> = string extends P ? string : ParamNames<P> | (P extends `${string}*` ? '*' : never);
|
|
52
|
+
/** `ctx.params` a partir só do path: `{ orgId: string; id: string }`. */
|
|
53
|
+
export type ParamsOf<P extends string> = string extends P ? Record<string, string> : {
|
|
54
|
+
[K in PathParams<P>]: string;
|
|
55
|
+
};
|
|
56
|
+
type SchemaKeys<T> = [T] extends [undefined] ? never : keyof InferSchema<T, Record<never, never>> & string;
|
|
57
|
+
/**
|
|
58
|
+
* Confere, em compilação, que todo parâmetro do path está no schema de `params` (F-29). Chaves a mais
|
|
59
|
+
* no schema podem vir do prefixo do `@Router`/`mount`, que o decorator não enxerga: essas são
|
|
60
|
+
* conferidas no registro. Resulta em `unknown` quando está tudo certo (ou não dá para saber).
|
|
61
|
+
*/
|
|
62
|
+
export type CheckParams<P extends string, S> = string extends P ? unknown : S extends {
|
|
63
|
+
params: infer T;
|
|
64
|
+
} ? [Exclude<PathParams<P>, SchemaKeys<T>>] extends [never] ? unknown : {
|
|
65
|
+
params: `Missing in params schema: "${Exclude<PathParams<P>, SchemaKeys<T>>}"`;
|
|
66
|
+
} : unknown;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=infer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../src/core/infer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infer.js","sourceRoot":"","sources":["../../src/core/infer.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
export interface InjectOptions {
|
|
3
|
+
method?: string;
|
|
4
|
+
/** Caminho com query string, ex.: `/users?limit=10`. */
|
|
5
|
+
url: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
/** Objetos são enviados como JSON (com `content-type` automático); strings e Buffers vão crus. */
|
|
8
|
+
body?: unknown;
|
|
9
|
+
/** IP simulado do cliente (`ctx.ip`). Padrão: `127.0.0.1`. */
|
|
10
|
+
remoteAddress?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface InjectResponse {
|
|
13
|
+
status: number;
|
|
14
|
+
headers: http.OutgoingHttpHeaders;
|
|
15
|
+
/** Corpo como texto. */
|
|
16
|
+
body: string;
|
|
17
|
+
json<T = unknown>(): T;
|
|
18
|
+
}
|
|
19
|
+
type Listener = (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Executa uma requisição direto no listener do app, sem abrir porta nem usar a rede (F-09).
|
|
22
|
+
* Cria `IncomingMessage`/`ServerResponse` reais sobre um socket desconectado e captura o que
|
|
23
|
+
* o app escreve na resposta.
|
|
24
|
+
*/
|
|
25
|
+
export declare function inject(listener: Listener, opts: InjectOptions): Promise<InjectResponse>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=inject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.d.ts","sourceRoot":"","sources":["../../src/core/inject.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kGAAkG;IAClG,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC;IAClC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC;CACxB;AAED,KAAK,QAAQ,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC;AAE9E;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CA8EvF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import { Socket } from 'node:net';
|
|
3
|
+
import { markHeadersSent } from './runtime/observer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Executa uma requisição direto no listener do app, sem abrir porta nem usar a rede (F-09).
|
|
6
|
+
* Cria `IncomingMessage`/`ServerResponse` reais sobre um socket desconectado e captura o que
|
|
7
|
+
* o app escreve na resposta.
|
|
8
|
+
*/
|
|
9
|
+
export function inject(listener, opts) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const socket = new Socket();
|
|
12
|
+
Object.defineProperty(socket, 'remoteAddress', { value: opts.remoteAddress ?? '127.0.0.1' });
|
|
13
|
+
const req = new http.IncomingMessage(socket);
|
|
14
|
+
req.method = (opts.method ?? 'GET').toUpperCase();
|
|
15
|
+
req.url = opts.url;
|
|
16
|
+
req.httpVersion = '1.1';
|
|
17
|
+
req.httpVersionMajor = 1;
|
|
18
|
+
req.httpVersionMinor = 1;
|
|
19
|
+
const headers = { host: 'localhost' };
|
|
20
|
+
for (const [k, v] of Object.entries(opts.headers ?? {}))
|
|
21
|
+
headers[k.toLowerCase()] = v;
|
|
22
|
+
let payload;
|
|
23
|
+
if (opts.body !== undefined) {
|
|
24
|
+
if (typeof opts.body === 'string' || Buffer.isBuffer(opts.body)) {
|
|
25
|
+
payload = Buffer.from(opts.body);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
payload = Buffer.from(JSON.stringify(opts.body));
|
|
29
|
+
headers['content-type'] ??= 'application/json';
|
|
30
|
+
}
|
|
31
|
+
headers['content-length'] = String(payload.length);
|
|
32
|
+
}
|
|
33
|
+
req.headers = headers;
|
|
34
|
+
if (payload)
|
|
35
|
+
req.push(payload);
|
|
36
|
+
req.push(null);
|
|
37
|
+
const res = new http.ServerResponse(req);
|
|
38
|
+
// O instante dos headers, para o `ttfbMs` (F-38); no `listen()`, quem grava é o protótipo da resposta.
|
|
39
|
+
const writeHead = res.writeHead;
|
|
40
|
+
res.writeHead = function (...args) {
|
|
41
|
+
markHeadersSent(this);
|
|
42
|
+
return writeHead.apply(this, args);
|
|
43
|
+
};
|
|
44
|
+
const chunks = [];
|
|
45
|
+
const noBody = req.method === 'HEAD';
|
|
46
|
+
const collect = (chunk, encoding) => {
|
|
47
|
+
if (noBody || chunk === undefined || chunk === null || typeof chunk === 'function')
|
|
48
|
+
return;
|
|
49
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), (typeof encoding === 'string' ? encoding : 'utf8')));
|
|
50
|
+
};
|
|
51
|
+
// Sem socket conectado, o 'finish' não dispara: interceptamos write/end para capturar o corpo
|
|
52
|
+
// e chamamos os originais para manter headersSent/writableEnded corretos.
|
|
53
|
+
const write = res.write.bind(res);
|
|
54
|
+
const end = res.end.bind(res);
|
|
55
|
+
res.write = ((chunk, encoding, cb) => {
|
|
56
|
+
collect(chunk, encoding);
|
|
57
|
+
return write(chunk, encoding, cb);
|
|
58
|
+
});
|
|
59
|
+
res.end = ((chunk, encoding, cb) => {
|
|
60
|
+
collect(chunk, encoding);
|
|
61
|
+
const out = end(chunk, encoding, cb);
|
|
62
|
+
const body = Buffer.concat(chunks).toString('utf8');
|
|
63
|
+
// Sem socket, 'finish' e 'close' nunca seriam emitidos: emite os dois, como numa conexão real,
|
|
64
|
+
// para `onResponse`, `logRequests` e a limpeza das respostas em andamento rodarem também no inject.
|
|
65
|
+
setImmediate(() => {
|
|
66
|
+
res.emit('finish');
|
|
67
|
+
res.emit('close');
|
|
68
|
+
});
|
|
69
|
+
resolve({
|
|
70
|
+
status: res.statusCode,
|
|
71
|
+
headers: res.getHeaders(),
|
|
72
|
+
body,
|
|
73
|
+
json: () => JSON.parse(body),
|
|
74
|
+
});
|
|
75
|
+
return out;
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
listener(req, res);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
reject(err);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=inject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/core/inject.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAuBxD;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,QAAkB,EAAE,IAAmB;IAC5D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,IAAI,WAAW,EAAE,CAAC,CAAC;QAE7F,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC7C,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAClD,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACnB,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC;QACxB,GAAG,CAAC,gBAAgB,GAAG,CAAC,CAAC;QACzB,GAAG,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAEzB,MAAM,OAAO,GAA2B,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QAC9D,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;QAEtF,IAAI,OAA2B,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChE,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACjD,OAAO,CAAC,cAAc,CAAC,KAAK,kBAAkB,CAAC;YACjD,CAAC;YACD,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACrD,CAAC;QACD,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;QACtB,IAAI,OAAO;YAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEf,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACzC,uGAAuG;QACvG,MAAM,SAAS,GAAG,GAAG,CAAC,SAAwD,CAAC;QAC9E,GAA8B,CAAC,SAAS,GAAG,UAAqC,GAAG,IAAe;YACjG,eAAe,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC,CAAC;QACF,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC;QACrC,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,QAAkB,EAAE,EAAE;YACrD,IAAI,MAAM,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,UAAU;gBAAE,OAAO;YAC3F,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAmB,CAAC,CAClI,CAAC;QACJ,CAAC,CAAC;QAEF,8FAA8F;QAC9F,0EAA0E;QAC1E,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAiC,CAAC;QAClE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAA6C,CAAC;QAC1E,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,KAAc,EAAE,QAAkB,EAAE,EAAY,EAAE,EAAE;YAChE,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpC,CAAC,CAAqB,CAAC;QACvB,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,KAAe,EAAE,QAAkB,EAAE,EAAY,EAAE,EAAE;YAC/D,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACzB,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACpD,+FAA+F;YAC/F,oGAAoG;YACpG,YAAY,CAAC,GAAG,EAAE;gBAChB,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACnB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,CAAC,CAAC,CAAC;YACH,OAAO,CAAC;gBACN,MAAM,EAAE,GAAG,CAAC,UAAU;gBACtB,OAAO,EAAE,GAAG,CAAC,UAAU,EAAE;gBACzB,IAAI;gBACJ,IAAI,EAAE,GAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM;aACrC,CAAC,CAAC;YACH,OAAO,GAAG,CAAC;QACb,CAAC,CAAmB,CAAC;QAErB,IAAI,CAAC;YACH,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|