sashiko 0.6.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 (49) hide show
  1. package/README.md +64 -4
  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 +14 -9
  13. package/dist/core/context.d.ts.map +1 -1
  14. package/dist/core/context.js +1 -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 +3 -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/openapi/generate.d.ts +43 -0
  42. package/dist/openapi/generate.d.ts.map +1 -0
  43. package/dist/openapi/generate.js +136 -0
  44. package/dist/openapi/generate.js.map +1 -0
  45. package/dist/utils/path.d.ts +5 -0
  46. package/dist/utils/path.d.ts.map +1 -1
  47. package/dist/utils/path.js +49 -0
  48. package/dist/utils/path.js.map +1 -1
  49. package/package.json +1 -1
@@ -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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"}
@@ -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.6.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",