sashiko 0.5.0 → 0.7.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 (65) hide show
  1. package/README.md +77 -6
  2. package/dist/core/app.d.ts +19 -0
  3. package/dist/core/app.d.ts.map +1 -1
  4. package/dist/core/app.js +55 -10
  5. package/dist/core/app.js.map +1 -1
  6. package/dist/core/body-parser.js +2 -2
  7. package/dist/core/body-parser.js.map +1 -1
  8. package/dist/core/compiler.d.ts +3 -0
  9. package/dist/core/compiler.d.ts.map +1 -1
  10. package/dist/core/compiler.js +32 -2
  11. package/dist/core/compiler.js.map +1 -1
  12. package/dist/core/context.d.ts +25 -9
  13. package/dist/core/context.d.ts.map +1 -1
  14. package/dist/core/context.js +19 -0
  15. package/dist/core/context.js.map +1 -1
  16. package/dist/core/errors.d.ts +26 -0
  17. package/dist/core/errors.d.ts.map +1 -1
  18. package/dist/core/errors.js +2 -0
  19. package/dist/core/errors.js.map +1 -1
  20. package/dist/core/index.d.ts +4 -3
  21. package/dist/core/index.d.ts.map +1 -1
  22. package/dist/core/index.js.map +1 -1
  23. package/dist/core/infer.d.ts +26 -0
  24. package/dist/core/infer.d.ts.map +1 -1
  25. package/dist/core/mounted-group.d.ts +3 -0
  26. package/dist/core/mounted-group.d.ts.map +1 -1
  27. package/dist/core/mounted-group.js +2 -0
  28. package/dist/core/mounted-group.js.map +1 -1
  29. package/dist/core/response.d.ts +12 -3
  30. package/dist/core/response.d.ts.map +1 -1
  31. package/dist/core/response.js +35 -10
  32. package/dist/core/response.js.map +1 -1
  33. package/dist/decorators/http.d.ts +10 -5
  34. package/dist/decorators/http.d.ts.map +1 -1
  35. package/dist/decorators/http.js +7 -14
  36. package/dist/decorators/http.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/middlewares/csrf.d.ts +42 -0
  42. package/dist/middlewares/csrf.d.ts.map +1 -0
  43. package/dist/middlewares/csrf.js +70 -0
  44. package/dist/middlewares/csrf.js.map +1 -0
  45. package/dist/middlewares/index.d.ts +1 -0
  46. package/dist/middlewares/index.d.ts.map +1 -1
  47. package/dist/middlewares/index.js +1 -0
  48. package/dist/middlewares/index.js.map +1 -1
  49. package/dist/openapi/generate.d.ts +43 -0
  50. package/dist/openapi/generate.d.ts.map +1 -0
  51. package/dist/openapi/generate.js +136 -0
  52. package/dist/openapi/generate.js.map +1 -0
  53. package/dist/utils/cookie.d.ts +34 -0
  54. package/dist/utils/cookie.d.ts.map +1 -0
  55. package/dist/utils/cookie.js +93 -0
  56. package/dist/utils/cookie.js.map +1 -0
  57. package/dist/utils/index.d.ts +1 -0
  58. package/dist/utils/index.d.ts.map +1 -1
  59. package/dist/utils/index.js +1 -0
  60. package/dist/utils/index.js.map +1 -1
  61. package/dist/utils/path.d.ts +5 -0
  62. package/dist/utils/path.d.ts.map +1 -1
  63. package/dist/utils/path.js +49 -0
  64. package/dist/utils/path.js.map +1 -1
  65. package/package.json +1 -1
@@ -0,0 +1,42 @@
1
+ import type { Middleware } from '../core/compose.ts';
2
+ import { type CookieOptions } from '../utils/cookie.ts';
3
+ import { type MatchRule } from '../utils/origin-match.ts';
4
+ declare module '../core/context.ts' {
5
+ interface ContextState {
6
+ /** Token CSRF da requisição, com `csrf({ token: true })`. Envie-o ao front no header configurado. */
7
+ csrfToken?: string;
8
+ }
9
+ }
10
+ export interface CsrfTokenOptions {
11
+ /** Padrão: `'__Host-csrf'`, que não pode ser definido por subdomínios. */
12
+ cookieName?: string;
13
+ /** Header onde o cliente devolve o token. Padrão: `'X-CSRF-Token'`. */
14
+ headerName?: string;
15
+ /** Atributos do cookie. `httpOnly` é sempre `false` (o front precisa ler) e `SameSite` padrão é `Strict`. */
16
+ cookie?: Omit<CookieOptions, 'httpOnly'>;
17
+ }
18
+ export interface CsrfOptions {
19
+ /**
20
+ * Outras origens confiáveis além da própria (`ctx.protocol://ctx.host`), com as mesmas regras do
21
+ * `cors()`: `'https://app.exemplo.com'`, `'https://*.exemplo.dev'` ou RegExp de casamento inteiro.
22
+ */
23
+ trustedOrigins?: readonly MatchRule[];
24
+ /** Ativa também o double-submit token. `true` usa os padrões de {@link CsrfTokenOptions}. */
25
+ token?: boolean | CsrfTokenOptions;
26
+ }
27
+ /**
28
+ * Proteção contra CSRF. Use sempre que a autenticação for por cookie.
29
+ *
30
+ * Métodos que alteram estado (todos menos GET, HEAD e OPTIONS) são recusados com 403 quando vêm de
31
+ * outra origem:
32
+ * 1. `Sec-Fetch-Site: same-origin` ou `none` (navegação do próprio usuário) passa; `same-site` e
33
+ * `cross-site` só passam se o `Origin` estiver em `trustedOrigins`;
34
+ * 2. sem `Sec-Fetch-Site`, o `Origin` precisa ser a própria origem ou uma confiável;
35
+ * 3. sem nenhum dos dois, a requisição não veio de um navegador (curl, servidor a servidor) e passa,
36
+ * porque CSRF depende de um navegador que anexe os cookies da vítima.
37
+ *
38
+ * Com `token`, além disso, exige o double-submit: o valor do header deve ser igual ao do cookie.
39
+ * Por isso, rotas GET nunca devem alterar estado.
40
+ */
41
+ export declare function csrf(opts?: CsrfOptions): Middleware;
42
+ //# sourceMappingURL=csrf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/middlewares/csrf.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAEjG,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,YAAY;QACpB,qGAAqG;QACrG,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;CACF;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6GAA6G;IAC7G,MAAM,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,6FAA6F;IAC7F,KAAK,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;CACpC;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,IAAI,CAAC,IAAI,GAAE,WAAgB,GAAG,UAAU,CAgCvD"}
@@ -0,0 +1,70 @@
1
+ import { randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { HttpError } from '../core/errors.js';
3
+ import { serializeCookie } from '../utils/cookie.js';
4
+ import { compileOriginMatcher, normalizeOrigin } from '../utils/origin-match.js';
5
+ const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
6
+ /**
7
+ * Proteção contra CSRF. Use sempre que a autenticação for por cookie.
8
+ *
9
+ * Métodos que alteram estado (todos menos GET, HEAD e OPTIONS) são recusados com 403 quando vêm de
10
+ * outra origem:
11
+ * 1. `Sec-Fetch-Site: same-origin` ou `none` (navegação do próprio usuário) passa; `same-site` e
12
+ * `cross-site` só passam se o `Origin` estiver em `trustedOrigins`;
13
+ * 2. sem `Sec-Fetch-Site`, o `Origin` precisa ser a própria origem ou uma confiável;
14
+ * 3. sem nenhum dos dois, a requisição não veio de um navegador (curl, servidor a servidor) e passa,
15
+ * porque CSRF depende de um navegador que anexe os cookies da vítima.
16
+ *
17
+ * Com `token`, além disso, exige o double-submit: o valor do header deve ser igual ao do cookie.
18
+ * Por isso, rotas GET nunca devem alterar estado.
19
+ */
20
+ export function csrf(opts = {}) {
21
+ const trusted = compileOriginMatcher(opts.trustedOrigins ?? []);
22
+ const token = opts.token ? (opts.token === true ? {} : opts.token) : undefined;
23
+ const cookieName = token?.cookieName ?? '__Host-csrf';
24
+ const headerName = (token?.headerName ?? 'X-CSRF-Token').toLowerCase();
25
+ const cookieOpts = { sameSite: 'Strict', ...token?.cookie, httpOnly: false };
26
+ // Valida nome e atributos já na criação, e não na primeira requisição.
27
+ if (token)
28
+ serializeCookie(cookieName, 'x', cookieOpts);
29
+ return async (ctx, next) => {
30
+ const safe = SAFE_METHODS.has(ctx.method);
31
+ if (!safe && !originAllowed(ctx, trusted)) {
32
+ throw new HttpError(403, 'Cross-origin request blocked', 'CSRF_ORIGIN');
33
+ }
34
+ if (token) {
35
+ const current = ctx.cookies[cookieName];
36
+ if (!safe) {
37
+ const sent = ctx.get(headerName);
38
+ if (!current || !sent || !equal(current, sent)) {
39
+ throw new HttpError(403, 'Invalid CSRF token', 'CSRF_TOKEN');
40
+ }
41
+ }
42
+ if (current && current.length >= 32) {
43
+ ctx.state.csrfToken = current;
44
+ }
45
+ else {
46
+ ctx.state.csrfToken = randomBytes(32).toString('base64url');
47
+ ctx.setCookie(cookieName, ctx.state.csrfToken, cookieOpts);
48
+ }
49
+ }
50
+ await next();
51
+ };
52
+ }
53
+ function originAllowed(ctx, trusted) {
54
+ const origin = ctx.get('origin');
55
+ const site = ctx.get('sec-fetch-site');
56
+ if (site === 'same-origin' || site === 'none')
57
+ return true;
58
+ if (site !== undefined)
59
+ return origin !== undefined && trusted(origin);
60
+ if (origin === undefined)
61
+ return true;
62
+ const own = ctx.host ? normalizeOrigin(`${ctx.protocol}://${ctx.host}`) : undefined;
63
+ return (own !== undefined && normalizeOrigin(origin) === own) || trusted(origin);
64
+ }
65
+ function equal(a, b) {
66
+ const x = Buffer.from(a);
67
+ const y = Buffer.from(b);
68
+ return x.length === y.length && timingSafeEqual(x, y);
69
+ }
70
+ //# sourceMappingURL=csrf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../src/middlewares/csrf.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { cors, type CorsOptions, type CorsOrigin } from './cors.ts';
2
2
  export { MemoryRateLimitStore, rateLimit, type RateLimitOptions, type RateLimitStore } from './rate-limit.ts';
3
3
  export { buildSecurityHeaders, type SecurityHeadersOptions, type SecurityPreset } from './security-headers.ts';
4
+ export { csrf, type CsrfOptions, type CsrfTokenOptions } from './csrf.ts';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC/G,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { cors } from './cors.js';
2
2
  export { MemoryRateLimitStore, rateLimit } from './rate-limit.js';
3
3
  export { buildSecurityHeaders } from './security-headers.js';
4
+ export { csrf } from './csrf.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAqC,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAA8C,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAoD,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middlewares/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAqC,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAA8C,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAoD,MAAM,uBAAuB,CAAC;AAC/G,OAAO,EAAE,IAAI,EAA2C,MAAM,WAAW,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { CompiledRoute } from '../core/compiler.ts';
2
+ import type { Middleware } from '../core/compose.ts';
3
+ import type { Guard } from '../core/guard.ts';
4
+ /** Opções de `app.openapi()` (F-16). */
5
+ export interface OpenAPIOptions {
6
+ /** Padrão: `{ title: 'API', version: '0.0.0' }`. */
7
+ info?: {
8
+ title: string;
9
+ version: string;
10
+ description?: string;
11
+ [key: string]: unknown;
12
+ };
13
+ servers?: readonly {
14
+ url: string;
15
+ description?: string;
16
+ }[];
17
+ /** Onde servir o documento JSON. Padrão: `'/openapi.json'`. */
18
+ path?: string;
19
+ /** Swagger UI. Desligado por padrão: `true` serve em `'/docs'`, uma string escolhe o path. */
20
+ docs?: string | boolean;
21
+ /** Rodam antes de servir o documento e a página, depois dos middlewares globais. */
22
+ middlewares?: readonly Middleware[];
23
+ /** Rodam depois dos `middlewares`, como nas rotas (401/403 antes de gerar o documento). */
24
+ guards?: readonly Guard[];
25
+ }
26
+ type Json = Record<string, unknown>;
27
+ /** `/:org/users/:id(^\\d+)` → `/{org}/users/{id}`; `::` → `:`; `*` → `{*}`; sem barra final (o roteador a ignora). */
28
+ export declare function toOpenAPIPath(path: string): string;
29
+ /**
30
+ * Gera o documento OpenAPI 3.1 a partir das rotas registradas (F-16). Os schemas TypeBox já são
31
+ * JSON Schema, então entram como estão. Schemas que não são JSON Schema (Zod etc.) não são convertidos.
32
+ */
33
+ export declare function generateOpenAPI(routes: readonly CompiledRoute[], opts: OpenAPIOptions, problemErrors: boolean): Json;
34
+ /**
35
+ * Página do Swagger UI (versão fixada, com SRI) e a CSP própria dela: scripts só do CDN e o inline com
36
+ * `nonce`, `connect-src 'self'` para buscar o documento.
37
+ */
38
+ export declare function docsPage(specUrl: string, title: string, nonce: string): {
39
+ html: string;
40
+ csp: string;
41
+ };
42
+ export {};
43
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/openapi/generate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAG9C,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACxF,OAAO,CAAC,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3D,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,oFAAoF;IACpF,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACpC,2FAA2F;IAC3F,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;CAC3B;AAED,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAKpC,sHAAsH;AACtH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOlD;AAiED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI,CAoBpH;AASD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CA2BrG"}
@@ -0,0 +1,136 @@
1
+ import { STATUS_CODES } from 'node:http';
2
+ import { isResponseContent } from '../core/metadata.js';
3
+ /** Cópia em JSON puro: tira as chaves `Symbol` do TypeBox e qualquer coisa não serializável. */
4
+ const plain = (schema) => JSON.parse(JSON.stringify(schema));
5
+ /** `/:org/users/:id(^\\d+)` → `/{org}/users/{id}`; `::` → `:`; `*` → `{*}`; sem barra final (o roteador a ignora). */
6
+ export function toOpenAPIPath(path) {
7
+ return path
8
+ .replace(/::/g, '\u0000')
9
+ .replace(/:([^/\-.(]+)(\((?:\\.|[^\\])*?\))?/g, '{$1}')
10
+ .replace(/\*$/, '{*}')
11
+ .replace(/\u0000/g, ':')
12
+ .replace(/(.)\/$/, '$1');
13
+ }
14
+ /** Problem Details (RFC 9457), o corpo padrão de todos os erros (F-15). */
15
+ const PROBLEM_SCHEMA = {
16
+ type: 'object',
17
+ required: ['type', 'title', 'status'],
18
+ properties: {
19
+ type: { type: 'string' },
20
+ title: { type: 'string' },
21
+ status: { type: 'integer' },
22
+ detail: { type: 'string' },
23
+ code: { type: 'string' },
24
+ details: {},
25
+ },
26
+ };
27
+ function parameters(where, schema) {
28
+ const json = schema ? plain(schema) : undefined;
29
+ const props = json?.properties;
30
+ if (!props)
31
+ return [];
32
+ const required = new Set(json.required ?? []);
33
+ return Object.entries(props).map(([name, s]) => ({
34
+ name,
35
+ in: where,
36
+ required: where === 'path' || required.has(name),
37
+ schema: s,
38
+ }));
39
+ }
40
+ function responseContent(entry) {
41
+ if (isResponseContent(entry)) {
42
+ const types = typeof entry.contentType === 'string' ? [entry.contentType] : entry.contentType;
43
+ return Object.fromEntries(types.map((t) => [t, entry.schema ? { schema: plain(entry.schema) } : {}]));
44
+ }
45
+ return { 'application/json': { schema: plain(entry) } };
46
+ }
47
+ function operation(route, problemErrors) {
48
+ const { schemas } = route;
49
+ const params = [
50
+ ...parameters('path', schemas.params),
51
+ ...parameters('query', schemas.query),
52
+ ...parameters('header', schemas.headers),
53
+ ];
54
+ const responses = {};
55
+ for (const [status, entry] of Object.entries(schemas.response ?? {})) {
56
+ const content = Number(status) === 204 ? undefined : responseContent(entry);
57
+ responses[status] = { description: STATUS_CODES[Number(status)] ?? 'Response', ...(content ? { content } : {}) };
58
+ }
59
+ if (Object.keys(responses).length === 0)
60
+ responses['200'] = { description: 'OK' };
61
+ responses.default = problemErrors
62
+ ? { description: 'Error', content: { 'application/problem+json': { schema: { $ref: '#/components/schemas/ProblemDetails' } } } }
63
+ : { description: 'Error' };
64
+ return {
65
+ operationId: `${route.owner}.${route.handlerName}`,
66
+ tags: [route.owner],
67
+ ...(params.length ? { parameters: params } : {}),
68
+ ...(schemas.body
69
+ ? { requestBody: { required: true, content: { 'application/json': { schema: plain(schemas.body) } } } }
70
+ : {}),
71
+ responses,
72
+ };
73
+ }
74
+ /**
75
+ * Gera o documento OpenAPI 3.1 a partir das rotas registradas (F-16). Os schemas TypeBox já são
76
+ * JSON Schema, então entram como estão. Schemas que não são JSON Schema (Zod etc.) não são convertidos.
77
+ */
78
+ export function generateOpenAPI(routes, opts, problemErrors) {
79
+ const paths = {};
80
+ const ids = new Map();
81
+ for (const route of routes) {
82
+ const path = toOpenAPIPath(route.path);
83
+ const op = operation(route, problemErrors);
84
+ // A mesma classe montada em dois prefixos gera o mesmo id; o OpenAPI exige ids únicos.
85
+ const base = op.operationId;
86
+ const n = (ids.get(base) ?? 0) + 1;
87
+ ids.set(base, n);
88
+ if (n > 1)
89
+ op.operationId = `${base}_${n}`;
90
+ (paths[path] ??= {})[route.method.toLowerCase()] = op;
91
+ }
92
+ return {
93
+ openapi: '3.1.0',
94
+ info: opts.info ?? { title: 'API', version: '0.0.0' },
95
+ ...(opts.servers ? { servers: opts.servers } : {}),
96
+ paths,
97
+ ...(problemErrors ? { components: { schemas: { ProblemDetails: PROBLEM_SCHEMA } } } : {}),
98
+ };
99
+ }
100
+ const SWAGGER = 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.34.1';
101
+ const SWAGGER_JS_SRI = 'sha384-XFYN47VzPiDY6qPtxYKg0nqUeW/upo20xf1j2SUE7gT/i35FJB4cuS/71Iegzk8Z';
102
+ const SWAGGER_CSS_SRI = 'sha384-4c3vsE5f+trhFyHrFrLM1fFGUn531NIaC7dcqEWIJdoRQJ5CkJbSuSCwXLpaS1KF';
103
+ const escapeHtml = (s) => s.replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
104
+ /**
105
+ * Página do Swagger UI (versão fixada, com SRI) e a CSP própria dela: scripts só do CDN e o inline com
106
+ * `nonce`, `connect-src 'self'` para buscar o documento.
107
+ */
108
+ export function docsPage(specUrl, title, nonce) {
109
+ const html = `<!DOCTYPE html>
110
+ <html lang="en">
111
+ <head>
112
+ <meta charset="utf-8">
113
+ <meta name="viewport" content="width=device-width, initial-scale=1">
114
+ <title>${escapeHtml(title)}</title>
115
+ <link rel="stylesheet" href="${SWAGGER}/swagger-ui.css" integrity="${SWAGGER_CSS_SRI}" crossorigin="anonymous">
116
+ </head>
117
+ <body>
118
+ <div id="swagger-ui"></div>
119
+ <script src="${SWAGGER}/swagger-ui-bundle.js" integrity="${SWAGGER_JS_SRI}" crossorigin="anonymous"></script>
120
+ <script nonce="${nonce}">SwaggerUIBundle({ url: ${JSON.stringify(specUrl).replace(/</g, '\\u003c')}, dom_id: '#swagger-ui' });</script>
121
+ </body>
122
+ </html>
123
+ `;
124
+ const csp = [
125
+ "default-src 'none'",
126
+ `script-src https://cdn.jsdelivr.net 'nonce-${nonce}'`,
127
+ "style-src https://cdn.jsdelivr.net 'unsafe-inline'",
128
+ "img-src 'self' data: https:",
129
+ "connect-src 'self'",
130
+ "frame-ancestors 'none'",
131
+ "base-uri 'none'",
132
+ "form-action 'none'",
133
+ ].join('; ');
134
+ return { html, csp };
135
+ }
136
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/openapi/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Leitura e escrita de cookies (RFC 6265), seguras por padrão.
3
+ *
4
+ * - **Leitura:** o resultado é um objeto sem protótipo (`__proto__` vira um cookie comum, sem poluir
5
+ * nada); em nomes repetidos vale o primeiro, como nos navegadores; um valor com `%` inválido fica cru.
6
+ * - **Escrita:** `HttpOnly`, `Secure`, `SameSite=Lax` e `Path=/` por padrão. Nome, `Domain` e `Path`
7
+ * são validados (sem `;`, quebra de linha nem caracteres de controle), então não dá para injetar
8
+ * atributos. O valor é codificado com `encodeURIComponent`.
9
+ * - **Prefixos:** `__Host-` exige `Secure`, `Path=/` e nenhum `Domain`; `__Secure-` exige `Secure`.
10
+ * `SameSite=None` exige `Secure`. Violações lançam erro na hora de definir o cookie.
11
+ */
12
+ export interface CookieOptions {
13
+ /** Padrão: `true`. Impede a leitura por JavaScript no navegador. */
14
+ httpOnly?: boolean;
15
+ /** Padrão: `true`. Os navegadores aceitam cookies `Secure` em `http://localhost`. */
16
+ secure?: boolean;
17
+ /** Padrão: `'Lax'`. `'None'` exige `secure`. */
18
+ sameSite?: 'Strict' | 'Lax' | 'None' | false;
19
+ /** Padrão: `'/'`. */
20
+ path?: string;
21
+ /** Sem `domain`, o cookie vale só para o host atual (o mais restrito). */
22
+ domain?: string;
23
+ /** Duração em segundos. `0` remove o cookie. */
24
+ maxAge?: number;
25
+ expires?: Date;
26
+ /** Cookie particionado (CHIPS), para uso em iframes de terceiros. Exige `secure`. */
27
+ partitioned?: boolean;
28
+ priority?: 'Low' | 'Medium' | 'High';
29
+ }
30
+ /** Lê o header `Cookie` num objeto sem protótipo. */
31
+ export declare function parseCookies(header: string | undefined): Record<string, string>;
32
+ /** Monta o valor de um header `Set-Cookie`, aplicando os padrões seguros. */
33
+ export declare function serializeCookie(name: string, value: string, opts?: CookieOptions): string;
34
+ //# sourceMappingURL=cookie.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../src/utils/cookie.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC7C,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,qFAAqF;IACrF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CACtC;AAOD,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAa/E;AAWD,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAoC7F"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Leitura e escrita de cookies (RFC 6265), seguras por padrão.
3
+ *
4
+ * - **Leitura:** o resultado é um objeto sem protótipo (`__proto__` vira um cookie comum, sem poluir
5
+ * nada); em nomes repetidos vale o primeiro, como nos navegadores; um valor com `%` inválido fica cru.
6
+ * - **Escrita:** `HttpOnly`, `Secure`, `SameSite=Lax` e `Path=/` por padrão. Nome, `Domain` e `Path`
7
+ * são validados (sem `;`, quebra de linha nem caracteres de controle), então não dá para injetar
8
+ * atributos. O valor é codificado com `encodeURIComponent`.
9
+ * - **Prefixos:** `__Host-` exige `Secure`, `Path=/` e nenhum `Domain`; `__Secure-` exige `Secure`.
10
+ * `SameSite=None` exige `Secure`. Violações lançam erro na hora de definir o cookie.
11
+ */
12
+ /** `token` da RFC 7230: o que um nome de cookie pode conter. */
13
+ const TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
14
+ /** Domain e Path: nada de `;`, controle (inclui CR/LF) nem DEL. */
15
+ const ATTR_VALUE = /^[^\x00-\x1f\x7f;]*$/;
16
+ /** Lê o header `Cookie` num objeto sem protótipo. */
17
+ export function parseCookies(header) {
18
+ const out = Object.create(null);
19
+ if (!header)
20
+ return out;
21
+ for (const part of header.split(';')) {
22
+ const eq = part.indexOf('=');
23
+ if (eq === -1)
24
+ continue;
25
+ const name = part.slice(0, eq).trim();
26
+ if (!name || name in out)
27
+ continue;
28
+ let value = part.slice(eq + 1).trim();
29
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"'))
30
+ value = value.slice(1, -1);
31
+ out[name] = safeDecode(value);
32
+ }
33
+ return out;
34
+ }
35
+ function safeDecode(value) {
36
+ if (!value.includes('%'))
37
+ return value;
38
+ try {
39
+ return decodeURIComponent(value);
40
+ }
41
+ catch {
42
+ return value;
43
+ }
44
+ }
45
+ /** Monta o valor de um header `Set-Cookie`, aplicando os padrões seguros. */
46
+ export function serializeCookie(name, value, opts = {}) {
47
+ if (!TOKEN.test(name))
48
+ throw new Error(`[sashiko] invalid cookie name "${name}".`);
49
+ const httpOnly = opts.httpOnly ?? true;
50
+ const secure = opts.secure ?? true;
51
+ const sameSite = opts.sameSite ?? 'Lax';
52
+ const path = opts.path ?? '/';
53
+ if (!ATTR_VALUE.test(path))
54
+ throw new Error(`[sashiko] invalid cookie path "${path}".`);
55
+ if (opts.domain !== undefined && (!opts.domain || !ATTR_VALUE.test(opts.domain))) {
56
+ throw new Error(`[sashiko] invalid cookie domain "${opts.domain}".`);
57
+ }
58
+ if (sameSite === 'None' && !secure)
59
+ throw new Error(`[sashiko] cookie "${name}": SameSite=None requires secure.`);
60
+ if (opts.partitioned && !secure)
61
+ throw new Error(`[sashiko] cookie "${name}": partitioned requires secure.`);
62
+ if (name.startsWith('__Secure-') && !secure)
63
+ throw new Error(`[sashiko] cookie "${name}": the __Secure- prefix requires secure.`);
64
+ if (name.startsWith('__Host-') && (!secure || path !== '/' || opts.domain !== undefined)) {
65
+ throw new Error(`[sashiko] cookie "${name}": the __Host- prefix requires secure, path "/" and no domain.`);
66
+ }
67
+ let out = `${name}=${encodeURIComponent(value)}`;
68
+ if (opts.maxAge !== undefined) {
69
+ if (!Number.isFinite(opts.maxAge))
70
+ throw new Error(`[sashiko] cookie "${name}": invalid maxAge.`);
71
+ out += `; Max-Age=${Math.floor(opts.maxAge)}`;
72
+ }
73
+ if (opts.expires) {
74
+ if (Number.isNaN(opts.expires.getTime()))
75
+ throw new Error(`[sashiko] cookie "${name}": invalid expires.`);
76
+ out += `; Expires=${opts.expires.toUTCString()}`;
77
+ }
78
+ if (opts.domain !== undefined)
79
+ out += `; Domain=${opts.domain}`;
80
+ out += `; Path=${path}`;
81
+ if (httpOnly)
82
+ out += '; HttpOnly';
83
+ if (secure)
84
+ out += '; Secure';
85
+ if (sameSite)
86
+ out += `; SameSite=${sameSite}`;
87
+ if (opts.partitioned)
88
+ out += '; Partitioned';
89
+ if (opts.priority)
90
+ out += `; Priority=${opts.priority}`;
91
+ return out;
92
+ }
93
+ //# sourceMappingURL=cookie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../src/utils/cookie.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,5 @@ export { joinPaths } from './path.ts';
3
3
  export { DEFAULT_MAX_QUERY_KEYS, parseQuery, type ParsedQuery } from './query.ts';
4
4
  export { hostnameOf, isValidHost, parseRequestTarget, pickForwarded } from './request-target.ts';
5
5
  export { compileHostMatcher, compileOriginMatcher, normalizeOrigin, toFullMatch, type MatchRule } from './origin-match.ts';
6
+ export { parseCookies, serializeCookie, type CookieOptions } from './cookie.ts';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC3H,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -3,4 +3,5 @@ export { joinPaths } from './path.js';
3
3
  export { DEFAULT_MAX_QUERY_KEYS, parseQuery } from './query.js';
4
4
  export { hostnameOf, isValidHost, parseRequestTarget, pickForwarded } from './request-target.js';
5
5
  export { compileHostMatcher, compileOriginMatcher, normalizeOrigin, toFullMatch } from './origin-match.js';
6
+ export { parseCookies, serializeCookie } from './cookie.js';
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAoB,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,eAAe,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAoB,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,eAAe,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAC3H,OAAO,EAAE,YAAY,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC"}
@@ -1,3 +1,8 @@
1
1
  /** Junta prefixo e caminho com exatamente uma barra entre eles: `('/users/', 'id')` → `/users/id`. */
2
2
  export declare function joinPaths(prefix: string, path: string): string;
3
+ /**
4
+ * Parâmetros de um path na sintaxe do find-my-way (`/:org/users/:id(^\\d+)`, `/files/*`), em ordem (F-29).
5
+ * Lança erro para parâmetro sem nome (`/:`), nome repetido, regex sem fechamento e `*` fora do fim.
6
+ */
7
+ export declare function pathParams(path: string): string[];
3
8
  //# sourceMappingURL=path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9D"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9D;AAID;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA6BjD"}
@@ -4,4 +4,53 @@ export function joinPaths(prefix, path) {
4
4
  const s = path.startsWith('/') ? path : `/${path}`;
5
5
  return `${p}${s}` || '/';
6
6
  }
7
+ const NAME_END = new Set(['/', '-', '.', '(']);
8
+ /**
9
+ * Parâmetros de um path na sintaxe do find-my-way (`/:org/users/:id(^\\d+)`, `/files/*`), em ordem (F-29).
10
+ * Lança erro para parâmetro sem nome (`/:`), nome repetido, regex sem fechamento e `*` fora do fim.
11
+ */
12
+ export function pathParams(path) {
13
+ const names = [];
14
+ for (let i = 0; i < path.length; i++) {
15
+ const ch = path[i];
16
+ if (ch === '*') {
17
+ if (i !== path.length - 1)
18
+ throw new Error(`invalid path "${path}": "*" is only allowed at the end`);
19
+ names.push('*');
20
+ continue;
21
+ }
22
+ if (ch !== ':')
23
+ continue;
24
+ if (path[i + 1] === ':') {
25
+ i++;
26
+ continue;
27
+ } // `::` é um ':' literal
28
+ let j = i + 1;
29
+ while (j < path.length && !NAME_END.has(path[j]))
30
+ j++;
31
+ const name = path.slice(i + 1, j);
32
+ if (!name)
33
+ throw new Error(`invalid path "${path}": parameter without a name`);
34
+ if (names.includes(name))
35
+ throw new Error(`invalid path "${path}": parameter "${name}" is repeated`);
36
+ names.push(name);
37
+ if (path[j] === '(') {
38
+ let depth = 0;
39
+ for (; j < path.length; j++) {
40
+ if (path[j] === '\\') {
41
+ j++;
42
+ continue;
43
+ }
44
+ if (path[j] === '(')
45
+ depth++;
46
+ else if (path[j] === ')' && --depth === 0)
47
+ break;
48
+ }
49
+ if (depth !== 0)
50
+ throw new Error(`invalid path "${path}": unterminated regex for "${name}"`);
51
+ }
52
+ i = j - (path[j] === ')' ? 0 : 1);
53
+ }
54
+ return names;
55
+ }
7
56
  //# sourceMappingURL=path.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,IAAY;IACpD,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IACnD,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sashiko",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Micro-framework HTTP para Node.js com decorators TC39 nativos, schema-first (TypeBox + Ajv) e defaults seguros.",
5
5
  "keywords": [
6
6
  "http",