sashiko 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +398 -2
  3. package/dist/core/app.d.ts +104 -0
  4. package/dist/core/app.d.ts.map +1 -0
  5. package/dist/core/app.js +202 -0
  6. package/dist/core/app.js.map +1 -0
  7. package/dist/core/body-parser.d.ts +4 -0
  8. package/dist/core/body-parser.d.ts.map +1 -0
  9. package/dist/core/body-parser.js +38 -0
  10. package/dist/core/body-parser.js.map +1 -0
  11. package/dist/core/compiler.d.ts +52 -0
  12. package/dist/core/compiler.d.ts.map +1 -0
  13. package/dist/core/compiler.js +74 -0
  14. package/dist/core/compiler.js.map +1 -0
  15. package/dist/core/compose.d.ts +4 -0
  16. package/dist/core/compose.d.ts.map +1 -0
  17. package/dist/core/compose.js +11 -0
  18. package/dist/core/compose.js.map +1 -0
  19. package/dist/core/context.d.ts +84 -0
  20. package/dist/core/context.d.ts.map +1 -0
  21. package/dist/core/context.js +121 -0
  22. package/dist/core/context.js.map +1 -0
  23. package/dist/core/errors.d.ts +24 -0
  24. package/dist/core/errors.d.ts.map +1 -0
  25. package/dist/core/errors.js +23 -0
  26. package/dist/core/errors.js.map +1 -0
  27. package/dist/core/guard.d.ts +10 -0
  28. package/dist/core/guard.d.ts.map +1 -0
  29. package/dist/core/guard.js +12 -0
  30. package/dist/core/guard.js.map +1 -0
  31. package/dist/core/index.d.ts +15 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +8 -0
  34. package/dist/core/index.js.map +1 -0
  35. package/dist/core/infer.d.ts +36 -0
  36. package/dist/core/infer.d.ts.map +1 -0
  37. package/dist/core/infer.js +2 -0
  38. package/dist/core/infer.js.map +1 -0
  39. package/dist/core/inject.d.ts +27 -0
  40. package/dist/core/inject.d.ts.map +1 -0
  41. package/dist/core/inject.js +72 -0
  42. package/dist/core/inject.js.map +1 -0
  43. package/dist/core/logger.d.ts +28 -0
  44. package/dist/core/logger.d.ts.map +1 -0
  45. package/dist/core/logger.js +27 -0
  46. package/dist/core/logger.js.map +1 -0
  47. package/dist/core/metadata.d.ts +59 -0
  48. package/dist/core/metadata.d.ts.map +1 -0
  49. package/dist/core/metadata.js +83 -0
  50. package/dist/core/metadata.js.map +1 -0
  51. package/dist/core/mounted-group.d.ts +41 -0
  52. package/dist/core/mounted-group.d.ts.map +1 -0
  53. package/dist/core/mounted-group.js +43 -0
  54. package/dist/core/mounted-group.js.map +1 -0
  55. package/dist/core/request-validation.d.ts +11 -0
  56. package/dist/core/request-validation.d.ts.map +1 -0
  57. package/dist/core/request-validation.js +30 -0
  58. package/dist/core/request-validation.js.map +1 -0
  59. package/dist/core/response.d.ts +16 -0
  60. package/dist/core/response.d.ts.map +1 -0
  61. package/dist/core/response.js +49 -0
  62. package/dist/core/response.js.map +1 -0
  63. package/dist/core/route-table.d.ts +29 -0
  64. package/dist/core/route-table.d.ts.map +1 -0
  65. package/dist/core/route-table.js +41 -0
  66. package/dist/core/route-table.js.map +1 -0
  67. package/dist/decorators/http.d.ts +16 -0
  68. package/dist/decorators/http.d.ts.map +1 -0
  69. package/dist/decorators/http.js +27 -0
  70. package/dist/decorators/http.js.map +1 -0
  71. package/dist/decorators/index.d.ts +6 -0
  72. package/dist/decorators/index.d.ts.map +1 -0
  73. package/dist/decorators/index.js +6 -0
  74. package/dist/decorators/index.js.map +1 -0
  75. package/dist/decorators/log.d.ts +10 -0
  76. package/dist/decorators/log.d.ts.map +1 -0
  77. package/dist/decorators/log.js +32 -0
  78. package/dist/decorators/log.js.map +1 -0
  79. package/dist/decorators/middlewares.d.ts +28 -0
  80. package/dist/decorators/middlewares.d.ts.map +1 -0
  81. package/dist/decorators/middlewares.js +40 -0
  82. package/dist/decorators/middlewares.js.map +1 -0
  83. package/dist/decorators/router.d.ts +12 -0
  84. package/dist/decorators/router.d.ts.map +1 -0
  85. package/dist/decorators/router.js +19 -0
  86. package/dist/decorators/router.js.map +1 -0
  87. package/dist/decorators/schema.d.ts +8 -0
  88. package/dist/decorators/schema.d.ts.map +1 -0
  89. package/dist/decorators/schema.js +13 -0
  90. package/dist/decorators/schema.js.map +1 -0
  91. package/dist/index.d.ts +5 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +7 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/middlewares/cors.d.ts +24 -0
  96. package/dist/middlewares/cors.d.ts.map +1 -0
  97. package/dist/middlewares/cors.js +63 -0
  98. package/dist/middlewares/cors.js.map +1 -0
  99. package/dist/middlewares/index.d.ts +4 -0
  100. package/dist/middlewares/index.d.ts.map +1 -0
  101. package/dist/middlewares/index.js +4 -0
  102. package/dist/middlewares/index.js.map +1 -0
  103. package/dist/middlewares/rate-limit.d.ts +35 -0
  104. package/dist/middlewares/rate-limit.d.ts.map +1 -0
  105. package/dist/middlewares/rate-limit.js +45 -0
  106. package/dist/middlewares/rate-limit.js.map +1 -0
  107. package/dist/middlewares/security-headers.d.ts +26 -0
  108. package/dist/middlewares/security-headers.d.ts.map +1 -0
  109. package/dist/middlewares/security-headers.js +34 -0
  110. package/dist/middlewares/security-headers.js.map +1 -0
  111. package/dist/schema/serialization.d.ts +2 -0
  112. package/dist/schema/serialization.d.ts.map +1 -0
  113. package/dist/schema/serialization.js +11 -0
  114. package/dist/schema/serialization.js.map +1 -0
  115. package/dist/schema/validation.d.ts +11 -0
  116. package/dist/schema/validation.d.ts.map +1 -0
  117. package/dist/schema/validation.js +48 -0
  118. package/dist/schema/validation.js.map +1 -0
  119. package/dist/shims/symbol-metadata.d.ts +2 -0
  120. package/dist/shims/symbol-metadata.d.ts.map +1 -0
  121. package/dist/shims/symbol-metadata.js +8 -0
  122. package/dist/shims/symbol-metadata.js.map +1 -0
  123. package/dist/utils/content-type.d.ts +5 -0
  124. package/dist/utils/content-type.d.ts.map +1 -0
  125. package/dist/utils/content-type.js +10 -0
  126. package/dist/utils/content-type.js.map +1 -0
  127. package/dist/utils/index.d.ts +4 -0
  128. package/dist/utils/index.d.ts.map +1 -0
  129. package/dist/utils/index.js +4 -0
  130. package/dist/utils/index.js.map +1 -0
  131. package/dist/utils/path.d.ts +3 -0
  132. package/dist/utils/path.d.ts.map +1 -0
  133. package/dist/utils/path.js +7 -0
  134. package/dist/utils/path.js.map +1 -0
  135. package/dist/utils/query.d.ts +13 -0
  136. package/dist/utils/query.d.ts.map +1 -0
  137. package/dist/utils/query.js +30 -0
  138. package/dist/utils/query.js.map +1 -0
  139. package/package.json +74 -3
@@ -0,0 +1,121 @@
1
+ import { Readable } from 'node:stream';
2
+ import { pipeline } from 'node:stream/promises';
3
+ import { HttpError } from './errors.js';
4
+ import { parseQuery } from '../utils/query.js';
5
+ import { silentLogger } from './logger.js';
6
+ /**
7
+ * Contexto da requisição. Passe os schemas da rota no genérico para tipar
8
+ * `params`, `query` e `body` a partir do `@Schema` (F-02):
9
+ *
10
+ * ```ts
11
+ * const create = { body: CreateUserSchema, response: { 201: UserSchema } } satisfies RouteSchemas;
12
+ * @Post('/') @Schema(create) createUser(ctx: Context<typeof create>) { ctx.body.name }
13
+ * ```
14
+ */
15
+ export class Context {
16
+ req;
17
+ res;
18
+ method;
19
+ path;
20
+ url;
21
+ /** Id da requisição, também enviado no header `X-Request-Id` (F-06). */
22
+ id;
23
+ /** Logger da requisição, já com `reqId` (F-06). */
24
+ log;
25
+ /** Parâmetros de rota; tipados pelo schema `params` quando informado em `Context<S>`. */
26
+ params = {};
27
+ /** Query string; tipada pelo schema `query` (já com coerção e defaults aplicados). */
28
+ query;
29
+ /** Body; tipado pelo schema `body`. */
30
+ body = undefined;
31
+ state = {};
32
+ /** `true` quando o handler chamou `ctx.status()`. Usado para escolher o schema de resposta (B-05). */
33
+ statusExplicit = false;
34
+ streaming = false;
35
+ constructor(req, res, opts = {}) {
36
+ this.req = req;
37
+ this.res = res;
38
+ this.method = req.method ?? 'GET';
39
+ this.url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
40
+ this.path = this.url.pathname;
41
+ this.query = parseQuery(this.url.searchParams, opts.queryMaxKeys);
42
+ this.id = opts.id ?? '';
43
+ this.log = (opts.logger ?? silentLogger).child({ reqId: this.id });
44
+ }
45
+ /** IP do cliente conforme o socket. Sem `trustProxy`, `X-Forwarded-For` é ignorado (S-09). */
46
+ get ip() {
47
+ return this.req.socket.remoteAddress ?? '';
48
+ }
49
+ status(code) {
50
+ this.res.statusCode = code;
51
+ this.statusExplicit = true;
52
+ return this;
53
+ }
54
+ /**
55
+ * `true` quando a resposta já foi enviada (ou está sendo, no caso de streams).
56
+ * Depois disso o framework não escreve mais nada na resposta.
57
+ */
58
+ get sent() {
59
+ return this.streaming || this.res.writableEnded;
60
+ }
61
+ /** Lê um header da requisição (case-insensitive). Headers repetidos são unidos com `, `. */
62
+ get(name) {
63
+ const value = this.req.headers[name.toLowerCase()];
64
+ return Array.isArray(value) ? value.join(', ') : value;
65
+ }
66
+ /** Define um header da resposta. */
67
+ header(name, value) {
68
+ this.res.setHeader(name, value);
69
+ return this;
70
+ }
71
+ json(data) {
72
+ if (this.sent)
73
+ return this;
74
+ this.res.setHeader('Content-Type', 'application/json; charset=utf-8');
75
+ this.res.end(JSON.stringify(data));
76
+ return this;
77
+ }
78
+ /** Resposta em texto puro (`text/plain; charset=utf-8`). */
79
+ text(data) {
80
+ return this.send(data, 'text/plain; charset=utf-8');
81
+ }
82
+ /**
83
+ * Envia string, Buffer/Uint8Array ou stream (`Readable`). O `Content-Type` padrão é
84
+ * `application/octet-stream`; passe o tipo correto. Para HTML, faça o escape do conteúdo
85
+ * dinâmico antes: o framework não faz isso por você (S-02).
86
+ */
87
+ send(body, contentType) {
88
+ if (this.sent)
89
+ return this;
90
+ if (contentType)
91
+ this.res.setHeader('Content-Type', contentType);
92
+ else if (!this.res.hasHeader('Content-Type'))
93
+ this.res.setHeader('Content-Type', 'application/octet-stream');
94
+ if (body instanceof Readable) {
95
+ this.streaming = true;
96
+ pipeline(body, this.res).catch((err) => {
97
+ this.log.error({ err, method: this.method, path: this.path }, 'stream failed');
98
+ this.res.destroy(err);
99
+ });
100
+ return this;
101
+ }
102
+ this.res.end(body);
103
+ return this;
104
+ }
105
+ /**
106
+ * Redireciona (padrão 302). Não use `url` vinda do usuário sem validar: isso abre um
107
+ * open redirect. Prefira caminhos relativos ou uma lista de destinos permitidos.
108
+ */
109
+ redirect(url, status = 302) {
110
+ if (this.sent)
111
+ return this;
112
+ this.status(status);
113
+ this.res.setHeader('Location', url);
114
+ this.res.end();
115
+ return this;
116
+ }
117
+ throw(status, message, code) {
118
+ throw new HttpError(status, message, code);
119
+ }
120
+ }
121
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface HttpErrorOptions {
2
+ /**
3
+ * Se a mensagem pode ser enviada ao cliente.
4
+ * Padrão: `true` para 4xx e `false` para 5xx, para não vazar detalhes internos (S-05).
5
+ */
6
+ expose?: boolean;
7
+ cause?: unknown;
8
+ /** Dados extras enviados ao cliente junto da mensagem (ex.: erros de validação). */
9
+ details?: unknown;
10
+ }
11
+ export declare class HttpError extends Error {
12
+ readonly status: number;
13
+ readonly code: string | undefined;
14
+ readonly expose: boolean;
15
+ readonly details: unknown;
16
+ constructor(status: number, message: string, code?: string, options?: HttpErrorOptions);
17
+ }
18
+ export declare function isHttpError(err: unknown): err is HttpError;
19
+ export declare const notFound: (msg?: string) => HttpError;
20
+ export declare const badRequest: (msg?: string) => HttpError;
21
+ export declare const unauthorized: (msg?: string) => HttpError;
22
+ export declare const forbidden: (msg?: string) => HttpError;
23
+ export declare const internal: (msg?: string) => HttpError;
24
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,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;CACnB;AAED,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;IAE1B,YAAY,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,EAOzF;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,23 @@
1
+ export class HttpError extends Error {
2
+ status;
3
+ code;
4
+ expose;
5
+ details;
6
+ constructor(status, message, code, options = {}) {
7
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
8
+ this.name = 'HttpError';
9
+ this.status = status;
10
+ this.code = code;
11
+ this.expose = options.expose ?? status < 500;
12
+ this.details = options.details;
13
+ }
14
+ }
15
+ export function isHttpError(err) {
16
+ return err instanceof HttpError;
17
+ }
18
+ export const notFound = (msg = 'Not Found') => new HttpError(404, msg, 'NOT_FOUND');
19
+ export const badRequest = (msg = 'Bad Request') => new HttpError(400, msg, 'BAD_REQUEST');
20
+ export const unauthorized = (msg = 'Unauthorized') => new HttpError(401, msg, 'UNAUTHORIZED');
21
+ export const forbidden = (msg = 'Forbidden') => new HttpError(403, msg, 'FORBIDDEN');
22
+ export const internal = (msg = 'Internal Server Error') => new HttpError(500, msg, 'INTERNAL');
23
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,MAAM,CAAS;IACf,IAAI,CAAqB;IACzB,MAAM,CAAU;IAChB,OAAO,CAAU;IAE1B,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;IACjC,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;AAG/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,CAOrE"}
@@ -0,0 +1,12 @@
1
+ import { HttpError } from './errors.js';
2
+ /** Executa os guards em ordem; o primeiro que negar interrompe a requisição. */
3
+ export function guardsMiddleware(guards) {
4
+ return async (ctx, next) => {
5
+ for (const guard of guards) {
6
+ if (!(await guard(ctx)))
7
+ throw new HttpError(403, 'Forbidden', 'FORBIDDEN');
8
+ }
9
+ await next();
10
+ };
11
+ }
12
+ //# 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;AAQxC,gFAAgF;AAChF,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACvD,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,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,15 @@
1
+ import '../shims/symbol-metadata.ts';
2
+ export { createApp, SashikoApp, type AppOptions, type ListenOptions, type RequestIdOptions, type ServerTimeouts } from './app.ts';
3
+ export type { InjectOptions, InjectResponse } from './inject.ts';
4
+ export type { ManualRouteOptions, MethodSelector, MethodsSelector, MountOptions, RouterRef } from './compiler.ts';
5
+ export { MountedGroup } from './mounted-group.ts';
6
+ export type { Middleware } from './compose.ts';
7
+ export type { Guard } from './guard.ts';
8
+ export type { InferBody, InferHeaders, InferParams, InferQuery, InferResponse, InferSchema } from './infer.ts';
9
+ export { consoleLogger, silentLogger, type ConsoleLoggerOptions, type Logger, type LogLevel } from './logger.ts';
10
+ export { Context, type ContextOptions, type ContextState } from './context.ts';
11
+ 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';
13
+ export type { Handler } from './route-table.ts';
14
+ export type { ValidationIssue, ValidationTarget } from '../schema/validation.ts';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;AAC/E,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,eAAe,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9H,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import '../shims/symbol-metadata.js';
2
+ export { createApp, SashikoApp } from './app.js';
3
+ export { MountedGroup } from './mounted-group.js';
4
+ export { consoleLogger, silentLogger } from './logger.js';
5
+ export { Context } from './context.js';
6
+ export { badRequest, forbidden, HttpError, internal, isHttpError, notFound, unauthorized } from './errors.js';
7
+ export { ROUTER_METADATA, readOwnMetadata } from './metadata.js';
8
+ //# 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,EAAE,SAAS,EAAE,UAAU,EAAmF,MAAM,UAAU,CAAC;AAGlI,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAyD,MAAM,aAAa,CAAC;AACjH,OAAO,EAAE,OAAO,EAA0C,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAyB,MAAM,aAAa,CAAC;AACrI,OAAO,EAAE,eAAe,EAAE,eAAe,EAA+D,MAAM,eAAe,CAAC"}
@@ -0,0 +1,36 @@
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> = [
9
+ NonNullable<T>
10
+ ] extends [never] ? Fallback : NonNullable<T> extends {
11
+ '~standard': {
12
+ types?: {
13
+ output: infer O;
14
+ } | undefined;
15
+ };
16
+ } ? O : NonNullable<T> extends {
17
+ static: unknown;
18
+ } ? (NonNullable<T> & {
19
+ params: [];
20
+ })['static'] : Fallback;
21
+ /** Tipo de `ctx.params` para os schemas `S`. */
22
+ export type InferParams<S extends RouteSchemas> = InferSchema<S['params'], Record<string, string>>;
23
+ /** Tipo de `ctx.query` para os schemas `S`. */
24
+ export type InferQuery<S extends RouteSchemas> = InferSchema<S['query'], Record<string, string | string[]>>;
25
+ /** Tipo de `ctx.body` para os schemas `S`. */
26
+ export type InferBody<S extends RouteSchemas> = InferSchema<S['body'], unknown>;
27
+ /** Tipo dos headers validados para os schemas `S`. */
28
+ export type InferHeaders<S extends RouteSchemas> = InferSchema<S['headers'], Record<string, string | string[] | undefined>>;
29
+ /**
30
+ * União dos tipos de resposta declarados (ou de um status específico).
31
+ * Útil para anotar o retorno do handler: `create(ctx): InferResponse<typeof create, 201>`.
32
+ */
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]>;
35
+ }[keyof S['response'] & Status] : unknown;
36
+ //# sourceMappingURL=infer.d.ts.map
@@ -0,0 +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,GACpG,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=infer.js.map
@@ -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;AAG7B,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,CAgEvF"}
@@ -0,0 +1,72 @@
1
+ import http from 'node:http';
2
+ import { Socket } from 'node:net';
3
+ /**
4
+ * Executa uma requisição direto no listener do app, sem abrir porta nem usar a rede (F-09).
5
+ * Cria `IncomingMessage`/`ServerResponse` reais sobre um socket desconectado e captura o que
6
+ * o app escreve na resposta.
7
+ */
8
+ export function inject(listener, opts) {
9
+ return new Promise((resolve, reject) => {
10
+ const socket = new Socket();
11
+ Object.defineProperty(socket, 'remoteAddress', { value: opts.remoteAddress ?? '127.0.0.1' });
12
+ const req = new http.IncomingMessage(socket);
13
+ req.method = (opts.method ?? 'GET').toUpperCase();
14
+ req.url = opts.url;
15
+ req.httpVersion = '1.1';
16
+ req.httpVersionMajor = 1;
17
+ req.httpVersionMinor = 1;
18
+ const headers = { host: 'localhost' };
19
+ for (const [k, v] of Object.entries(opts.headers ?? {}))
20
+ headers[k.toLowerCase()] = v;
21
+ let payload;
22
+ if (opts.body !== undefined) {
23
+ if (typeof opts.body === 'string' || Buffer.isBuffer(opts.body)) {
24
+ payload = Buffer.from(opts.body);
25
+ }
26
+ else {
27
+ payload = Buffer.from(JSON.stringify(opts.body));
28
+ headers['content-type'] ??= 'application/json';
29
+ }
30
+ headers['content-length'] = String(payload.length);
31
+ }
32
+ req.headers = headers;
33
+ if (payload)
34
+ req.push(payload);
35
+ req.push(null);
36
+ const res = new http.ServerResponse(req);
37
+ const chunks = [];
38
+ const noBody = req.method === 'HEAD';
39
+ const collect = (chunk, encoding) => {
40
+ if (noBody || chunk === undefined || chunk === null || typeof chunk === 'function')
41
+ return;
42
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), (typeof encoding === 'string' ? encoding : 'utf8')));
43
+ };
44
+ // Sem socket conectado, o 'finish' não dispara: interceptamos write/end para capturar o corpo
45
+ // e chamamos os originais para manter headersSent/writableEnded corretos.
46
+ const write = res.write.bind(res);
47
+ const end = res.end.bind(res);
48
+ res.write = ((chunk, encoding, cb) => {
49
+ collect(chunk, encoding);
50
+ return write(chunk, encoding, cb);
51
+ });
52
+ res.end = ((chunk, encoding, cb) => {
53
+ collect(chunk, encoding);
54
+ const out = end(chunk, encoding, cb);
55
+ const body = Buffer.concat(chunks).toString('utf8');
56
+ resolve({
57
+ status: res.statusCode,
58
+ headers: res.getHeaders(),
59
+ body,
60
+ json: () => JSON.parse(body),
61
+ });
62
+ return out;
63
+ });
64
+ try {
65
+ listener(req, res);
66
+ }
67
+ catch (err) {
68
+ reject(err);
69
+ }
70
+ });
71
+ }
72
+ //# sourceMappingURL=inject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/core/inject.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Contrato de logger compatível com o pino (F-06): `logger.info({ reqId }, 'mensagem')`.
3
+ * Para usar o pino, basta passar a instância: `createApp({ logger: pino() })`.
4
+ */
5
+ export interface Logger {
6
+ debug(obj: object, msg?: string): void;
7
+ debug(msg: string): void;
8
+ info(obj: object, msg?: string): void;
9
+ info(msg: string): void;
10
+ warn(obj: object, msg?: string): void;
11
+ warn(msg: string): void;
12
+ error(obj: object, msg?: string): void;
13
+ error(msg: string): void;
14
+ child(bindings: Record<string, unknown>): Logger;
15
+ }
16
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
17
+ export interface ConsoleLoggerOptions {
18
+ /** Padrão: `info`. */
19
+ level?: LogLevel;
20
+ }
21
+ /**
22
+ * Logger padrão sobre o `console`, sem dependências. Formato: `[sashiko] mensagem { ...dados }`.
23
+ * Não registra headers nem body: dados sensíveis só entram se você os passar (S-11).
24
+ */
25
+ export declare function consoleLogger(opts?: ConsoleLoggerOptions, bindings?: Record<string, unknown>): Logger;
26
+ /** Logger que descarta tudo (`createApp({ logger: false })`). */
27
+ export declare const silentLogger: Logger;
28
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAItE,MAAM,WAAW,oBAAoB;IACnC,sBAAsB;IACtB,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,EAAE,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAc7G;AAED,iEAAiE;AACjE,eAAO,MAAM,YAAY,EAAE,MAG1B,CAAC"}
@@ -0,0 +1,27 @@
1
+ const LEVELS = { debug: 10, info: 20, warn: 30, error: 40, silent: 99 };
2
+ /**
3
+ * Logger padrão sobre o `console`, sem dependências. Formato: `[sashiko] mensagem { ...dados }`.
4
+ * Não registra headers nem body: dados sensíveis só entram se você os passar (S-11).
5
+ */
6
+ export function consoleLogger(opts = {}, bindings = {}) {
7
+ const min = LEVELS[opts.level ?? 'info'];
8
+ const emit = (level) => (objOrMsg, msg) => {
9
+ if (LEVELS[level] < min)
10
+ return;
11
+ const [data, text] = typeof objOrMsg === 'string' ? [{}, objOrMsg] : [objOrMsg, msg ?? ''];
12
+ console[level](`[sashiko] ${text}`, { ...bindings, ...data });
13
+ };
14
+ return {
15
+ debug: emit('debug'),
16
+ info: emit('info'),
17
+ warn: emit('warn'),
18
+ error: emit('error'),
19
+ child: (extra) => consoleLogger(opts, { ...bindings, ...extra }),
20
+ };
21
+ }
22
+ /** Logger que descarta tudo (`createApp({ logger: false })`). */
23
+ export const silentLogger = {
24
+ debug() { }, info() { }, warn() { }, error() { },
25
+ child: () => silentLogger,
26
+ };
27
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,GAA6B,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAOlG;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAI,GAAyB,EAAE,EAAE,QAAQ,GAA4B,EAAE;IACnG,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,KAAkC,EAAE,EAAE,CAAC,CAAC,QAAyB,EAAE,GAAY,EAAE,EAAE;QAC/F,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG;YAAE,OAAO;QAChC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QAC3F,OAAO,CAAC,KAAK,CAAC,CAAC,aAAa,IAAI,EAAE,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC;IACF,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;QACpB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QAClB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;QACpB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;KACjE,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,MAAM,YAAY,GAAW;IAClC,KAAK,KAAI,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,KAAK,KAAI,CAAC;IAC5C,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY;CAC1B,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { Middleware } from './compose.ts';
2
+ import type { Guard } from './guard.ts';
3
+ export declare const ROUTER_METADATA: unique symbol;
4
+ export interface RouteSchemas {
5
+ params?: object;
6
+ body?: object;
7
+ query?: object;
8
+ headers?: object;
9
+ response?: Record<number, object>;
10
+ }
11
+ export interface MethodMetadata {
12
+ method?: string;
13
+ path?: string;
14
+ schemas: RouteSchemas;
15
+ handlerName?: string | symbol;
16
+ /** `@Middlewares` no método (F-01). */
17
+ middlewares: Middleware[];
18
+ /** `@Guards` no método (F-01). */
19
+ guards: Guard[];
20
+ }
21
+ export interface RouterMetadata {
22
+ /** `true` apenas quando a própria classe recebeu `@Router` (não herda). */
23
+ isRouter: boolean;
24
+ prefix: string;
25
+ methods: Map<string | symbol, MethodMetadata>;
26
+ /** `@Middlewares` na classe: valem para todas as rotas da classe (F-01). */
27
+ middlewares: Middleware[];
28
+ /** `@Guards` na classe (F-01). */
29
+ guards: Guard[];
30
+ }
31
+ /**
32
+ * Retorna a metadata **própria** da classe.
33
+ *
34
+ * Em subclasses, `context.metadata` herda do pai via protótipo. Ler a chave direto
35
+ * devolveria o objeto do pai, e qualquer escrita o corromperia (B-09). Por isso
36
+ * clonamos a metadata herdada na primeira escrita (copy-on-write): a subclasse
37
+ * herda as rotas do pai, mas nunca altera a classe base.
38
+ */
39
+ export declare function getOrCreateMetadata(meta: DecoratorMetadataObject): RouterMetadata;
40
+ /** Lê a metadata própria de uma classe já decorada, sem criar nem herdar. */
41
+ export declare function readOwnMetadata(Class: object): RouterMetadata | undefined;
42
+ /**
43
+ * Ponto de entrada dos decorators: valida o ambiente e devolve a metadata própria da classe.
44
+ *
45
+ * - R-02: com `experimentalDecorators`, o TypeScript chama o decorator com
46
+ * `(target, propertyKey, descriptor)` em vez de `(value, context)`.
47
+ * - R-01: sem `Symbol.metadata`, o `context.metadata` chega `undefined` e não há onde guardar as rotas.
48
+ *
49
+ * Nos dois casos, falhamos cedo com uma mensagem que diz como resolver.
50
+ */
51
+ export declare function metadataFor(context: unknown, decorator: string): RouterMetadata;
52
+ /**
53
+ * Junta schemas vindos de `@Get(path, schemas)` e de `@Schema(...)` no mesmo método.
54
+ * Partes diferentes se combinam; a mesma parte declarada duas vezes é erro, para não haver
55
+ * dúvida sobre qual vale.
56
+ */
57
+ export declare function mergeSchemas(method: MethodMetadata, schemas: RouteSchemas, where: string): void;
58
+ export declare function getOrCreateMethod(meta: RouterMetadata, name: string | symbol): MethodMetadata;
59
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +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,MAAM,CAAC,CAAC;CACnC;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"}
@@ -0,0 +1,83 @@
1
+ export const ROUTER_METADATA = Symbol.for('sashiko:router');
2
+ /**
3
+ * Retorna a metadata **própria** da classe.
4
+ *
5
+ * Em subclasses, `context.metadata` herda do pai via protótipo. Ler a chave direto
6
+ * devolveria o objeto do pai, e qualquer escrita o corromperia (B-09). Por isso
7
+ * clonamos a metadata herdada na primeira escrita (copy-on-write): a subclasse
8
+ * herda as rotas do pai, mas nunca altera a classe base.
9
+ */
10
+ export function getOrCreateMetadata(meta) {
11
+ if (Object.hasOwn(meta, ROUTER_METADATA)) {
12
+ return meta[ROUTER_METADATA];
13
+ }
14
+ const inherited = meta[ROUTER_METADATA];
15
+ const own = {
16
+ isRouter: false,
17
+ prefix: '',
18
+ methods: new Map([...(inherited?.methods ?? [])].map(([name, m]) => [
19
+ name,
20
+ { ...m, schemas: { ...m.schemas }, middlewares: [...m.middlewares], guards: [...m.guards] },
21
+ ])),
22
+ // A subclasse herda os middlewares/guards de classe do pai e pode acrescentar os seus.
23
+ middlewares: [...(inherited?.middlewares ?? [])],
24
+ guards: [...(inherited?.guards ?? [])],
25
+ };
26
+ meta[ROUTER_METADATA] = own;
27
+ return own;
28
+ }
29
+ /** Lê a metadata própria de uma classe já decorada, sem criar nem herdar. */
30
+ export function readOwnMetadata(Class) {
31
+ if (!Object.hasOwn(Class, Symbol.metadata))
32
+ return undefined;
33
+ const meta = Class[Symbol.metadata];
34
+ if (!meta || !Object.hasOwn(meta, ROUTER_METADATA))
35
+ return undefined;
36
+ return meta[ROUTER_METADATA];
37
+ }
38
+ /**
39
+ * Ponto de entrada dos decorators: valida o ambiente e devolve a metadata própria da classe.
40
+ *
41
+ * - R-02: com `experimentalDecorators`, o TypeScript chama o decorator com
42
+ * `(target, propertyKey, descriptor)` em vez de `(value, context)`.
43
+ * - R-01: sem `Symbol.metadata`, o `context.metadata` chega `undefined` e não há onde guardar as rotas.
44
+ *
45
+ * Nos dois casos, falhamos cedo com uma mensagem que diz como resolver.
46
+ */
47
+ export function metadataFor(context, decorator) {
48
+ if (typeof context !== 'object' || context === null || !('kind' in context)) {
49
+ throw new Error(`[sashiko] @${decorator} received legacy decorator arguments. Sashiko uses standard (TC39) ` +
50
+ `decorators: remove "experimentalDecorators" (and "emitDecoratorMetadata") from your tsconfig.json.`);
51
+ }
52
+ const meta = context.metadata;
53
+ if (!meta) {
54
+ throw new Error(`[sashiko] @${decorator}: decorator metadata is unavailable (Symbol.metadata is not defined). ` +
55
+ `Import 'sashiko' before declaring routers, or use TypeScript >= 5.2 / a runtime with decorator metadata.`);
56
+ }
57
+ return getOrCreateMetadata(meta);
58
+ }
59
+ /**
60
+ * Junta schemas vindos de `@Get(path, schemas)` e de `@Schema(...)` no mesmo método.
61
+ * Partes diferentes se combinam; a mesma parte declarada duas vezes é erro, para não haver
62
+ * dúvida sobre qual vale.
63
+ */
64
+ export function mergeSchemas(method, schemas, where) {
65
+ for (const key of Object.keys(schemas)) {
66
+ if (schemas[key] === undefined)
67
+ continue;
68
+ if (method.schemas[key] !== undefined) {
69
+ throw new Error(`[sashiko] ${where}: schema "${key}" is declared twice for ${String(method.handlerName)}. ` +
70
+ `Declare each part either in the HTTP decorator or in @Schema, not both.`);
71
+ }
72
+ }
73
+ method.schemas = { ...method.schemas, ...schemas };
74
+ }
75
+ export function getOrCreateMethod(meta, name) {
76
+ let m = meta.methods.get(name);
77
+ if (!m) {
78
+ m = { schemas: {}, handlerName: name, middlewares: [], guards: [] };
79
+ meta.methods.set(name, m);
80
+ }
81
+ return m;
82
+ }
83
+ //# sourceMappingURL=metadata.js.map