sashiko 0.0.0-stage → 0.1.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 (135) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +370 -2
  3. package/dist/core/app.d.ts +101 -0
  4. package/dist/core/app.d.ts.map +1 -0
  5. package/dist/core/app.js +199 -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 +28 -0
  12. package/dist/core/compiler.d.ts.map +1 -0
  13. package/dist/core/compiler.js +50 -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 +14 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +7 -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/request-validation.d.ts +11 -0
  52. package/dist/core/request-validation.d.ts.map +1 -0
  53. package/dist/core/request-validation.js +30 -0
  54. package/dist/core/request-validation.js.map +1 -0
  55. package/dist/core/response.d.ts +16 -0
  56. package/dist/core/response.d.ts.map +1 -0
  57. package/dist/core/response.js +49 -0
  58. package/dist/core/response.js.map +1 -0
  59. package/dist/core/route-table.d.ts +29 -0
  60. package/dist/core/route-table.d.ts.map +1 -0
  61. package/dist/core/route-table.js +41 -0
  62. package/dist/core/route-table.js.map +1 -0
  63. package/dist/decorators/http.d.ts +16 -0
  64. package/dist/decorators/http.d.ts.map +1 -0
  65. package/dist/decorators/http.js +27 -0
  66. package/dist/decorators/http.js.map +1 -0
  67. package/dist/decorators/index.d.ts +6 -0
  68. package/dist/decorators/index.d.ts.map +1 -0
  69. package/dist/decorators/index.js +6 -0
  70. package/dist/decorators/index.js.map +1 -0
  71. package/dist/decorators/log.d.ts +10 -0
  72. package/dist/decorators/log.d.ts.map +1 -0
  73. package/dist/decorators/log.js +32 -0
  74. package/dist/decorators/log.js.map +1 -0
  75. package/dist/decorators/router.d.ts +12 -0
  76. package/dist/decorators/router.d.ts.map +1 -0
  77. package/dist/decorators/router.js +19 -0
  78. package/dist/decorators/router.js.map +1 -0
  79. package/dist/decorators/schema.d.ts +8 -0
  80. package/dist/decorators/schema.d.ts.map +1 -0
  81. package/dist/decorators/schema.js +13 -0
  82. package/dist/decorators/schema.js.map +1 -0
  83. package/dist/decorators/use.d.ts +26 -0
  84. package/dist/decorators/use.d.ts.map +1 -0
  85. package/dist/decorators/use.js +38 -0
  86. package/dist/decorators/use.js.map +1 -0
  87. package/dist/index.d.ts +5 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +7 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/middlewares/cors.d.ts +24 -0
  92. package/dist/middlewares/cors.d.ts.map +1 -0
  93. package/dist/middlewares/cors.js +63 -0
  94. package/dist/middlewares/cors.js.map +1 -0
  95. package/dist/middlewares/index.d.ts +4 -0
  96. package/dist/middlewares/index.d.ts.map +1 -0
  97. package/dist/middlewares/index.js +4 -0
  98. package/dist/middlewares/index.js.map +1 -0
  99. package/dist/middlewares/rate-limit.d.ts +35 -0
  100. package/dist/middlewares/rate-limit.d.ts.map +1 -0
  101. package/dist/middlewares/rate-limit.js +45 -0
  102. package/dist/middlewares/rate-limit.js.map +1 -0
  103. package/dist/middlewares/security-headers.d.ts +26 -0
  104. package/dist/middlewares/security-headers.d.ts.map +1 -0
  105. package/dist/middlewares/security-headers.js +34 -0
  106. package/dist/middlewares/security-headers.js.map +1 -0
  107. package/dist/schema/serialization.d.ts +2 -0
  108. package/dist/schema/serialization.d.ts.map +1 -0
  109. package/dist/schema/serialization.js +11 -0
  110. package/dist/schema/serialization.js.map +1 -0
  111. package/dist/schema/validation.d.ts +11 -0
  112. package/dist/schema/validation.d.ts.map +1 -0
  113. package/dist/schema/validation.js +48 -0
  114. package/dist/schema/validation.js.map +1 -0
  115. package/dist/shims/symbol-metadata.d.ts +2 -0
  116. package/dist/shims/symbol-metadata.d.ts.map +1 -0
  117. package/dist/shims/symbol-metadata.js +8 -0
  118. package/dist/shims/symbol-metadata.js.map +1 -0
  119. package/dist/utils/content-type.d.ts +5 -0
  120. package/dist/utils/content-type.d.ts.map +1 -0
  121. package/dist/utils/content-type.js +10 -0
  122. package/dist/utils/content-type.js.map +1 -0
  123. package/dist/utils/index.d.ts +4 -0
  124. package/dist/utils/index.d.ts.map +1 -0
  125. package/dist/utils/index.js +4 -0
  126. package/dist/utils/index.js.map +1 -0
  127. package/dist/utils/path.d.ts +3 -0
  128. package/dist/utils/path.d.ts.map +1 -0
  129. package/dist/utils/path.js +7 -0
  130. package/dist/utils/path.js.map +1 -0
  131. package/dist/utils/query.d.ts +13 -0
  132. package/dist/utils/query.d.ts.map +1 -0
  133. package/dist/utils/query.js +30 -0
  134. package/dist/utils/query.js.map +1 -0
  135. package/package.json +74 -3
@@ -0,0 +1,199 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import http from 'node:http';
3
+ import { performance } from 'node:perf_hooks';
4
+ import { compileRoutes } from './compiler.js';
5
+ import { compose } from './compose.js';
6
+ import { Context } from './context.js';
7
+ import '../shims/symbol-metadata.js';
8
+ import { bodyParser } from './body-parser.js';
9
+ import { assertSchemas, validateRequest } from './request-validation.js';
10
+ import { sendError, sendResult } from './response.js';
11
+ import { RouteTable } from './route-table.js';
12
+ import { inject } from './inject.js';
13
+ import { guardsMiddleware } from './guard.js';
14
+ import { consoleLogger, silentLogger } from './logger.js';
15
+ import { buildSecurityHeaders } from '../middlewares/security-headers.js';
16
+ const SAFE_REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/;
17
+ export class SashikoApp {
18
+ middlewares = [];
19
+ router = new RouteTable();
20
+ errorHandlers = [];
21
+ applySecurityHeaders;
22
+ opts;
23
+ server;
24
+ /** Logger do app (F-06). Use para logs fora de uma requisição. */
25
+ log;
26
+ constructor(opts = {}) {
27
+ this.opts = opts;
28
+ this.log = opts.logger === false ? silentLogger : (opts.logger ?? consoleLogger());
29
+ if (opts.securityHeaders !== false)
30
+ this.applySecurityHeaders = buildSecurityHeaders(opts.securityHeaders);
31
+ this.use(bodyParser(opts.bodyLimit ?? 1024 * 1024));
32
+ }
33
+ use(mw) {
34
+ this.middlewares.push(mw);
35
+ return this;
36
+ }
37
+ onError(h) {
38
+ this.errorHandlers.push(h);
39
+ return this;
40
+ }
41
+ mount(ref, options = {}) {
42
+ const name = typeof ref === 'function' ? ref.name : ref.constructor.name;
43
+ for (const route of compileRoutes(ref, options)) {
44
+ if (this.opts.requireSchemas)
45
+ assertSchemas(name, route);
46
+ try {
47
+ this.router.on(route.method, route.path, route);
48
+ }
49
+ catch (err) {
50
+ throw new Error(`[sashiko] app.mount(${name}): ${route.method} ${route.path} is already registered. ` +
51
+ `Use a different "prefix" to mount the same class twice.`, { cause: err });
52
+ }
53
+ }
54
+ return this;
55
+ }
56
+ async handle(ctx) {
57
+ // Antes de tudo, para valer inclusive em erros, 404 e preflight (S-04).
58
+ this.applySecurityHeaders?.(ctx.res);
59
+ try {
60
+ await compose(ctx, this.middlewares);
61
+ if (ctx.sent)
62
+ return;
63
+ const match = this.router.find(ctx.method, ctx.path);
64
+ if (!match) {
65
+ const allowed = this.router.allowedMethods(ctx.path);
66
+ if (allowed.length > 0) {
67
+ ctx.res.setHeader('Allow', allowed.join(', '));
68
+ // F-08: OPTIONS automático para qualquer path com rotas.
69
+ if (ctx.method === 'OPTIONS')
70
+ return void ctx.status(204).res.end();
71
+ return void ctx.status(405).json({ error: 'Method Not Allowed', allowed });
72
+ }
73
+ return void ctx.status(404).json({ error: 'Not Found' });
74
+ }
75
+ ctx.params = match.params;
76
+ // F-01: @Use → @UseGuards → validação → handler. Guards antes da validação, para que
77
+ // quem não tem acesso receba 401/403 e não um 400 que revele a estrutura esperada.
78
+ const route = match;
79
+ const pipeline = [...route.middlewares];
80
+ if (route.guards.length > 0)
81
+ pipeline.push(guardsMiddleware(route.guards));
82
+ pipeline.push(async () => {
83
+ validateRequest(ctx, route.schemas);
84
+ const result = await route.handler(ctx);
85
+ if (!ctx.sent)
86
+ sendResult(ctx, route.schemas.response, result);
87
+ });
88
+ await compose(ctx, pipeline);
89
+ }
90
+ catch (err) {
91
+ await this.runErrorHandlers(err, ctx);
92
+ }
93
+ }
94
+ /**
95
+ * Executa os `onError` do usuário. Se um deles lançar, o erro é logado e seguimos
96
+ * para a resposta padrão com o erro **original** — nunca deixamos a Promise rejeitar (B-08).
97
+ */
98
+ async runErrorHandlers(err, ctx) {
99
+ for (const h of this.errorHandlers) {
100
+ try {
101
+ await h(err, ctx);
102
+ }
103
+ catch (handlerErr) {
104
+ ctx.log.error({ err: handlerErr, method: ctx.method, path: ctx.path }, 'onError handler failed');
105
+ break;
106
+ }
107
+ if (ctx.res.writableEnded)
108
+ return;
109
+ }
110
+ sendError(ctx, err);
111
+ }
112
+ /**
113
+ * Listener `(req, res)` do app, para usar com qualquer servidor do Node:
114
+ * `https.createServer(opts, app.callback())`, `http2` em modo compat, testes etc. (F-09).
115
+ */
116
+ callback() {
117
+ const rid = this.opts.requestId ?? {};
118
+ const header = rid.header === false ? undefined : (rid.header ?? 'x-request-id').toLowerCase();
119
+ const trust = rid.trustIncoming ?? true;
120
+ const generate = rid.generate ?? (() => randomUUID());
121
+ return (req, res) => {
122
+ const started = performance.now();
123
+ const incoming = header ? req.headers[header] : undefined;
124
+ const id = trust && typeof incoming === 'string' && SAFE_REQUEST_ID.test(incoming) ? incoming : generate();
125
+ if (header)
126
+ res.setHeader(header, id);
127
+ // Última barreira (B-08): nada que aconteça numa requisição pode virar unhandledRejection.
128
+ const fail = (err) => {
129
+ this.log.error({ err, reqId: id, method: req.method, url: req.url }, 'unhandled error while handling request');
130
+ if (!res.headersSent)
131
+ res.statusCode = 500;
132
+ if (!res.writableEnded)
133
+ res.end();
134
+ };
135
+ try {
136
+ const ctx = new Context(req, res, { queryMaxKeys: this.opts.queryMaxKeys, id, logger: this.log });
137
+ if (this.opts.logRequests) {
138
+ res.once('finish', () => {
139
+ const ms = Math.round((performance.now() - started) * 100) / 100;
140
+ ctx.log.info({ method: ctx.method, path: ctx.path, status: res.statusCode, ms }, 'request completed');
141
+ });
142
+ }
143
+ this.handle(ctx).catch(fail);
144
+ }
145
+ catch (err) {
146
+ fail(err);
147
+ }
148
+ };
149
+ }
150
+ /** Sobe o servidor HTTP e resolve quando ele está aceitando conexões (F-09). */
151
+ async listen(options = {}) {
152
+ if (this.server)
153
+ throw new Error('[sashiko] app is already listening; call app.close() first.');
154
+ const { port = 3000, host = '127.0.0.1' } = typeof options === 'number' ? { port: options } : options;
155
+ const server = http.createServer(this.callback());
156
+ const t = this.opts.timeouts ?? {};
157
+ server.requestTimeout = t.requestTimeout ?? 30_000;
158
+ server.headersTimeout = Math.min(t.headersTimeout ?? 10_000, server.requestTimeout || Infinity);
159
+ server.keepAliveTimeout = t.keepAliveTimeout ?? 5_000;
160
+ await new Promise((resolve, reject) => {
161
+ server.once('error', reject);
162
+ server.listen(port, host, () => {
163
+ server.off('error', reject);
164
+ resolve();
165
+ });
166
+ });
167
+ this.server = server;
168
+ return server;
169
+ }
170
+ /**
171
+ * Para de aceitar conexões, encerra as ociosas e espera as requisições em andamento terminarem.
172
+ * Depois de `graceMs`, força o fechamento das restantes.
173
+ */
174
+ async close(graceMs = 10_000) {
175
+ const server = this.server;
176
+ if (!server)
177
+ return;
178
+ this.server = undefined;
179
+ const force = setTimeout(() => server.closeAllConnections(), graceMs);
180
+ force.unref();
181
+ try {
182
+ await new Promise((resolve, reject) => {
183
+ server.close((err) => (err ? reject(err) : resolve()));
184
+ server.closeIdleConnections();
185
+ });
186
+ }
187
+ finally {
188
+ clearTimeout(force);
189
+ }
190
+ }
191
+ /** Executa uma requisição em memória, sem rede. Ideal para testes (F-09). */
192
+ inject(opts) {
193
+ return inject(this.callback(), typeof opts === 'string' ? { url: opts } : opts);
194
+ }
195
+ }
196
+ export function createApp(opts) {
197
+ return new SashikoApp(opts);
198
+ }
199
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/core/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Middleware } from './compose.ts';
2
+ /** Lê e parseia o body JSON, respeitando `limit` em bytes. Registrado pelo app como primeiro middleware. */
3
+ export declare function bodyParser(limit: number): Middleware;
4
+ //# sourceMappingURL=body-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body-parser.d.ts","sourceRoot":"","sources":["../../src/core/body-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,4GAA4G;AAC5G,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA4BpD"}
@@ -0,0 +1,38 @@
1
+ import { isJsonContentType } from '../utils/content-type.js';
2
+ /** Lê e parseia o body JSON, respeitando `limit` em bytes. Registrado pelo app como primeiro middleware. */
3
+ export function bodyParser(limit) {
4
+ return async (ctx, next) => {
5
+ if (ctx.method === 'GET' || ctx.method === 'HEAD')
6
+ return next();
7
+ if (!isJsonContentType(ctx.req.headers['content-type']))
8
+ return next();
9
+ // B-12: rejeita cedo pelo Content-Length e fecha a conexão ao exceder,
10
+ // para o cliente não continuar enviando um corpo que será descartado.
11
+ const tooLarge = () => {
12
+ ctx.res.setHeader('Connection', 'close');
13
+ return ctx.throw(413, 'Payload too large');
14
+ };
15
+ const declared = Number(ctx.req.headers['content-length']);
16
+ if (Number.isFinite(declared) && declared > limit)
17
+ tooLarge();
18
+ const chunks = [];
19
+ let size = 0;
20
+ for await (const chunk of ctx.req) {
21
+ size += chunk.length;
22
+ if (size > limit)
23
+ tooLarge();
24
+ chunks.push(chunk);
25
+ }
26
+ const raw = Buffer.concat(chunks).toString('utf8');
27
+ if (raw) {
28
+ try {
29
+ ctx.body = JSON.parse(raw);
30
+ }
31
+ catch {
32
+ ctx.throw(400, 'Invalid JSON');
33
+ }
34
+ }
35
+ await next();
36
+ };
37
+ }
38
+ //# sourceMappingURL=body-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body-parser.js","sourceRoot":"","sources":["../../src/core/body-parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,4GAA4G;AAC5G,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,EAAE,CAAC;QACjE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAEvE,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,QAAQ,GAAG,GAAU,EAAE;YAC3B,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACzC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QAC7C,CAAC,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,KAAK;YAAE,QAAQ,EAAE,CAAC;QAE9D,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YAClC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YACrB,IAAI,IAAI,GAAG,KAAK;gBAAE,QAAQ,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC;gBAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YACnC,MAAM,CAAC;gBAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAC,CAAC;QAC3C,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { Guard } from './guard.ts';
2
+ import type { Middleware } from './compose.ts';
3
+ import type { RouteDefinition } from './route-table.ts';
4
+ export interface CompiledRoute extends RouteDefinition {
5
+ method: string;
6
+ path: string;
7
+ }
8
+ /** Classe com construtor sem argumentos **ou** instância já construída (B-10). */
9
+ export type RouterRef = (new () => object) | object;
10
+ /** Nomes dos métodos de uma instância (para `only`/`except`). */
11
+ export type MethodNames<T> = {
12
+ [K in keyof T]: T[K] extends (...args: never[]) => unknown ? K : never;
13
+ }[keyof T] & string;
14
+ /** Opções de `app.mount()`, aplicadas no ponto de composição (F-28). */
15
+ export interface MountOptions<T = object> {
16
+ /** Substitui o prefixo definido no `@Router`. */
17
+ prefix?: string;
18
+ /** Registra só estes métodos de rota. Não pode ser usado junto com `except`. */
19
+ only?: readonly MethodNames<T>[];
20
+ /** Registra todos os métodos de rota, exceto estes. */
21
+ except?: readonly MethodNames<T>[];
22
+ /** Middlewares para todas as rotas registradas; rodam antes dos `@Use` da classe. */
23
+ middlewares?: readonly Middleware[];
24
+ /** Guards para todas as rotas registradas; rodam antes dos `@UseGuards` da classe. */
25
+ guards?: readonly Guard[];
26
+ }
27
+ export declare function compileRoutes(ref: RouterRef, opts?: MountOptions): CompiledRoute[];
28
+ //# sourceMappingURL=compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/core/compiler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,WAAW,aAAc,SAAQ,eAAe;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,CAAC,UAAU,MAAM,CAAC,GAAG,MAAM,CAAC;AAEpD,iEAAiE;AACjE,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,CAAC,GAAG,KAAK;CAAE,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC;AAE1H,wEAAwE;AACxE,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM;IACtC,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,IAAI,CAAC,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACjC,uDAAuD;IACvD,MAAM,CAAC,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACnC,qFAAqF;IACrF,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACpC,sFAAsF;IACtF,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;CAC3B;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,GAAE,YAAiB,GAAG,aAAa,EAAE,CAoCtF"}
@@ -0,0 +1,50 @@
1
+ import { readOwnMetadata } from './metadata.js';
2
+ import { joinPaths } from '../utils/path.js';
3
+ export function compileRoutes(ref, opts = {}) {
4
+ const isClass = typeof ref === 'function';
5
+ const Class = (isClass ? ref : ref.constructor);
6
+ const meta = readOwnMetadata(Class);
7
+ if (!meta?.isRouter) {
8
+ throw new Error(`[sashiko] Class ${Class.name} is not decorated with @Router('/prefix'). ` +
9
+ `Add it to register the class with app.mount().`);
10
+ }
11
+ const routeNames = [...meta.methods].filter(([, m]) => m.method && m.path).map(([name]) => String(name));
12
+ const selected = selectRoutes(Class.name, routeNames, opts);
13
+ // Instâncias permitem injetar dependências pelo construtor: app.mount(new UserRoutes(repo))
14
+ const instance = isClass ? new Class() : ref;
15
+ const prefix = opts.prefix ?? meta.prefix;
16
+ const routes = [];
17
+ for (const [name, methodMeta] of meta.methods) {
18
+ if (!methodMeta.method || !methodMeta.path || !selected.has(String(name)))
19
+ continue;
20
+ const fn = instance[name];
21
+ if (typeof fn !== 'function')
22
+ continue;
23
+ routes.push({
24
+ method: methodMeta.method,
25
+ path: joinPaths(prefix, methodMeta.path),
26
+ handler: (ctx) => fn.call(instance, ctx),
27
+ schemas: methodMeta.schemas,
28
+ // Ordem: composição (app.mount) → classe → método (F-01, F-28)
29
+ middlewares: [...(opts.middlewares ?? []), ...meta.middlewares, ...methodMeta.middlewares],
30
+ guards: [...(opts.guards ?? []), ...meta.guards, ...methodMeta.guards],
31
+ });
32
+ }
33
+ return routes;
34
+ }
35
+ function selectRoutes(className, names, opts) {
36
+ if (opts.only && opts.except) {
37
+ throw new Error(`[sashiko] app.mount(${className}): use either "only" or "except", not both.`);
38
+ }
39
+ const listed = opts.only ?? opts.except ?? [];
40
+ const unknown = listed.filter((n) => !names.includes(n));
41
+ if (unknown.length > 0) {
42
+ throw new Error(`[sashiko] app.mount(${className}): ${unknown.map((n) => `"${n}"`).join(', ')} ` +
43
+ `is not a route method. Available: ${names.join(', ') || '(none)'}.`);
44
+ }
45
+ if (opts.only)
46
+ return new Set(opts.only);
47
+ const except = new Set(opts.except ?? []);
48
+ return new Set(names.filter((n) => !except.has(n)));
49
+ }
50
+ //# sourceMappingURL=compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../src/core/compiler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Context } from './context.ts';
2
+ export type Middleware = (ctx: Context, next: () => Promise<void>) => Promise<void> | void;
3
+ export declare function compose(ctx: Context, fns: Middleware[]): Promise<void>;
4
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/core/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE3F,wBAAsB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5E"}
@@ -0,0 +1,11 @@
1
+ export async function compose(ctx, fns) {
2
+ let i = 0;
3
+ const dispatch = async () => {
4
+ if (i >= fns.length)
5
+ return;
6
+ const fn = fns[i++];
7
+ await fn(ctx, dispatch);
8
+ };
9
+ await dispatch();
10
+ }
11
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/core/compose.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAY,EAAE,GAAiB;IAC3D,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO;QAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,CAAE,CAAC;QACrB,MAAM,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC1B,CAAC,CAAC;IACF,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,84 @@
1
+ import type { IncomingMessage, OutgoingHttpHeader, ServerResponse } from 'node:http';
2
+ import { Readable } from 'node:stream';
3
+ import { type Logger } from './logger.ts';
4
+ import type { InferBody, InferParams, InferQuery } from './infer.ts';
5
+ import type { RouteSchemas } from './metadata.ts';
6
+ /**
7
+ * Estado por requisição, compartilhado entre middlewares, guards e handlers.
8
+ * Estenda com declaration merging para ter tipos (F-01):
9
+ *
10
+ * ```ts
11
+ * declare module 'sashiko' {
12
+ * interface ContextState { user?: { id: string; role: 'admin' | 'user' } }
13
+ * }
14
+ * ```
15
+ */
16
+ export interface ContextState {
17
+ [key: string]: unknown;
18
+ }
19
+ export interface ContextOptions {
20
+ queryMaxKeys?: number | undefined;
21
+ /** Id da requisição; o app gera ou reaproveita o header recebido (F-06). */
22
+ id?: string | undefined;
23
+ /** Logger do app; o contexto cria um filho com o `reqId`. */
24
+ logger?: Logger | undefined;
25
+ }
26
+ /**
27
+ * Contexto da requisição. Passe os schemas da rota no genérico para tipar
28
+ * `params`, `query` e `body` a partir do `@Schema` (F-02):
29
+ *
30
+ * ```ts
31
+ * const create = { body: CreateUserSchema, response: { 201: UserSchema } } satisfies RouteSchemas;
32
+ * @Post('/') @Schema(create) createUser(ctx: Context<typeof create>) { ctx.body.name }
33
+ * ```
34
+ */
35
+ export declare class Context<S extends RouteSchemas = RouteSchemas> {
36
+ readonly req: IncomingMessage;
37
+ readonly res: ServerResponse;
38
+ readonly method: string;
39
+ readonly path: string;
40
+ readonly url: URL;
41
+ /** Id da requisição, também enviado no header `X-Request-Id` (F-06). */
42
+ readonly id: string;
43
+ /** Logger da requisição, já com `reqId` (F-06). */
44
+ readonly log: Logger;
45
+ /** Parâmetros de rota; tipados pelo schema `params` quando informado em `Context<S>`. */
46
+ params: InferParams<S>;
47
+ /** Query string; tipada pelo schema `query` (já com coerção e defaults aplicados). */
48
+ query: InferQuery<S>;
49
+ /** Body; tipado pelo schema `body`. */
50
+ body: InferBody<S>;
51
+ state: ContextState;
52
+ /** `true` quando o handler chamou `ctx.status()`. Usado para escolher o schema de resposta (B-05). */
53
+ statusExplicit: boolean;
54
+ private streaming;
55
+ constructor(req: IncomingMessage, res: ServerResponse, opts?: ContextOptions);
56
+ /** IP do cliente conforme o socket. Sem `trustProxy`, `X-Forwarded-For` é ignorado (S-09). */
57
+ get ip(): string;
58
+ status(code: number): this;
59
+ /**
60
+ * `true` quando a resposta já foi enviada (ou está sendo, no caso de streams).
61
+ * Depois disso o framework não escreve mais nada na resposta.
62
+ */
63
+ get sent(): boolean;
64
+ /** Lê um header da requisição (case-insensitive). Headers repetidos são unidos com `, `. */
65
+ get(name: string): string | undefined;
66
+ /** Define um header da resposta. */
67
+ header(name: string, value: OutgoingHttpHeader): this;
68
+ json(data: unknown): this;
69
+ /** Resposta em texto puro (`text/plain; charset=utf-8`). */
70
+ text(data: string): this;
71
+ /**
72
+ * Envia string, Buffer/Uint8Array ou stream (`Readable`). O `Content-Type` padrão é
73
+ * `application/octet-stream`; passe o tipo correto. Para HTML, faça o escape do conteúdo
74
+ * dinâmico antes: o framework não faz isso por você (S-02).
75
+ */
76
+ send(body: string | Uint8Array | Readable, contentType?: string): this;
77
+ /**
78
+ * Redireciona (padrão 302). Não use `url` vinda do usuário sem validar: isso abre um
79
+ * open redirect. Prefira caminhos relativos ou uma lista de destinos permitidos.
80
+ */
81
+ redirect(url: string, status?: 301 | 302 | 303 | 307 | 308): this;
82
+ throw(status: number, message: string, code?: string): never;
83
+ }
84
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvC,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4EAA4E;IAC5E,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,qBAAa,OAAO,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IACxD,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,yFAAyF;IACzF,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAwB;IAC9C,sFAAsF;IACtF,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACrB,uCAAuC;IACvC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAA6B;IAC/C,KAAK,EAAE,YAAY,CAAM;IAEzB,sGAAsG;IACtG,cAAc,UAAS;IAEvB,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,GAAE,cAAmB,EAS/E;IAED,8FAA8F;IAC9F,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIzB;IAED;;;OAGG;IACH,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,4FAA4F;IAC5F,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGpC;IAED,oCAAoC;IACpC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAGpD;IAED,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAKxB;IAED,4DAA4D;IAC5D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvB;IAED;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAerE;IAED;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAS,GAAG,IAAI,CAMrE;IAED,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAE3D;CACF"}
@@ -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"}