flowingts 0.0.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +74 -3
  3. package/dist/core/abort.d.ts +23 -0
  4. package/dist/core/abort.d.ts.map +1 -0
  5. package/dist/core/abort.js +23 -0
  6. package/dist/core/abort.js.map +1 -0
  7. package/dist/core/app/core.d.ts +35 -0
  8. package/dist/core/app/core.d.ts.map +1 -0
  9. package/dist/core/app/core.js +2 -0
  10. package/dist/core/app/core.js.map +1 -0
  11. package/dist/core/app/options.d.ts +178 -0
  12. package/dist/core/app/options.d.ts.map +1 -0
  13. package/dist/core/app/options.js +2 -0
  14. package/dist/core/app/options.js.map +1 -0
  15. package/dist/core/app.d.ts +143 -0
  16. package/dist/core/app.d.ts.map +1 -0
  17. package/dist/core/app.js +283 -0
  18. package/dist/core/app.js.map +1 -0
  19. package/dist/core/body-parser.d.ts +14 -0
  20. package/dist/core/body-parser.d.ts.map +1 -0
  21. package/dist/core/body-parser.js +76 -0
  22. package/dist/core/body-parser.js.map +1 -0
  23. package/dist/core/compiler.d.ts +104 -0
  24. package/dist/core/compiler.d.ts.map +1 -0
  25. package/dist/core/compiler.js +151 -0
  26. package/dist/core/compiler.js.map +1 -0
  27. package/dist/core/compose.d.ts +16 -0
  28. package/dist/core/compose.d.ts.map +1 -0
  29. package/dist/core/compose.js +41 -0
  30. package/dist/core/compose.js.map +1 -0
  31. package/dist/core/context.d.ts +290 -0
  32. package/dist/core/context.d.ts.map +1 -0
  33. package/dist/core/context.js +460 -0
  34. package/dist/core/context.js.map +1 -0
  35. package/dist/core/contracts.d.ts +76 -0
  36. package/dist/core/contracts.d.ts.map +1 -0
  37. package/dist/core/contracts.js +87 -0
  38. package/dist/core/contracts.js.map +1 -0
  39. package/dist/core/errors.d.ts +50 -0
  40. package/dist/core/errors.d.ts.map +1 -0
  41. package/dist/core/errors.js +25 -0
  42. package/dist/core/errors.js.map +1 -0
  43. package/dist/core/guard.d.ts +10 -0
  44. package/dist/core/guard.d.ts.map +1 -0
  45. package/dist/core/guard.js +14 -0
  46. package/dist/core/guard.js.map +1 -0
  47. package/dist/core/health.d.ts +37 -0
  48. package/dist/core/health.d.ts.map +1 -0
  49. package/dist/core/health.js +68 -0
  50. package/dist/core/health.js.map +1 -0
  51. package/dist/core/hooks.d.ts +34 -0
  52. package/dist/core/hooks.d.ts.map +1 -0
  53. package/dist/core/hooks.js +27 -0
  54. package/dist/core/hooks.js.map +1 -0
  55. package/dist/core/index.d.ts +30 -0
  56. package/dist/core/index.d.ts.map +1 -0
  57. package/dist/core/index.js +11 -0
  58. package/dist/core/index.js.map +1 -0
  59. package/dist/core/infer.d.ts +68 -0
  60. package/dist/core/infer.d.ts.map +1 -0
  61. package/dist/core/infer.js +2 -0
  62. package/dist/core/infer.js.map +1 -0
  63. package/dist/core/inject.d.ts +27 -0
  64. package/dist/core/inject.d.ts.map +1 -0
  65. package/dist/core/inject.js +85 -0
  66. package/dist/core/inject.js.map +1 -0
  67. package/dist/core/logger.d.ts +57 -0
  68. package/dist/core/logger.d.ts.map +1 -0
  69. package/dist/core/logger.js +134 -0
  70. package/dist/core/logger.js.map +1 -0
  71. package/dist/core/metadata.d.ts +83 -0
  72. package/dist/core/metadata.d.ts.map +1 -0
  73. package/dist/core/metadata.js +91 -0
  74. package/dist/core/metadata.js.map +1 -0
  75. package/dist/core/mounted-group.d.ts +77 -0
  76. package/dist/core/mounted-group.d.ts.map +1 -0
  77. package/dist/core/mounted-group.js +131 -0
  78. package/dist/core/mounted-group.js.map +1 -0
  79. package/dist/core/registry/framework-routes.d.ts +28 -0
  80. package/dist/core/registry/framework-routes.d.ts.map +1 -0
  81. package/dist/core/registry/framework-routes.js +83 -0
  82. package/dist/core/registry/framework-routes.js.map +1 -0
  83. package/dist/core/registry/registry.d.ts +58 -0
  84. package/dist/core/registry/registry.d.ts.map +1 -0
  85. package/dist/core/registry/registry.js +208 -0
  86. package/dist/core/registry/registry.js.map +1 -0
  87. package/dist/core/registry/route-program.d.ts +16 -0
  88. package/dist/core/registry/route-program.d.ts.map +1 -0
  89. package/dist/core/registry/route-program.js +62 -0
  90. package/dist/core/registry/route-program.js.map +1 -0
  91. package/dist/core/request-validation.d.ts +15 -0
  92. package/dist/core/request-validation.d.ts.map +1 -0
  93. package/dist/core/request-validation.js +67 -0
  94. package/dist/core/request-validation.js.map +1 -0
  95. package/dist/core/response/pipeline.d.ts +51 -0
  96. package/dist/core/response/pipeline.d.ts.map +1 -0
  97. package/dist/core/response/pipeline.js +169 -0
  98. package/dist/core/response/pipeline.js.map +1 -0
  99. package/dist/core/route-table.d.ts +58 -0
  100. package/dist/core/route-table.d.ts.map +1 -0
  101. package/dist/core/route-table.js +108 -0
  102. package/dist/core/route-table.js.map +1 -0
  103. package/dist/core/runtime/observer.d.ts +47 -0
  104. package/dist/core/runtime/observer.d.ts.map +1 -0
  105. package/dist/core/runtime/observer.js +125 -0
  106. package/dist/core/runtime/observer.js.map +1 -0
  107. package/dist/core/runtime/request-handler.d.ts +62 -0
  108. package/dist/core/runtime/request-handler.d.ts.map +1 -0
  109. package/dist/core/runtime/request-handler.js +256 -0
  110. package/dist/core/runtime/request-handler.js.map +1 -0
  111. package/dist/core/runtime/server.d.ts +59 -0
  112. package/dist/core/runtime/server.d.ts.map +1 -0
  113. package/dist/core/runtime/server.js +180 -0
  114. package/dist/core/runtime/server.js.map +1 -0
  115. package/dist/core/shutdown.d.ts +41 -0
  116. package/dist/core/shutdown.d.ts.map +1 -0
  117. package/dist/core/shutdown.js +53 -0
  118. package/dist/core/shutdown.js.map +1 -0
  119. package/dist/core/streaming/sse.d.ts +35 -0
  120. package/dist/core/streaming/sse.d.ts.map +1 -0
  121. package/dist/core/streaming/sse.js +121 -0
  122. package/dist/core/streaming/sse.js.map +1 -0
  123. package/dist/core/symbols.d.ts +28 -0
  124. package/dist/core/symbols.d.ts.map +1 -0
  125. package/dist/core/symbols.js +28 -0
  126. package/dist/core/symbols.js.map +1 -0
  127. package/dist/core/telemetry.d.ts +83 -0
  128. package/dist/core/telemetry.d.ts.map +1 -0
  129. package/dist/core/telemetry.js +96 -0
  130. package/dist/core/telemetry.js.map +1 -0
  131. package/dist/core/uploads/receive.d.ts +63 -0
  132. package/dist/core/uploads/receive.d.ts.map +1 -0
  133. package/dist/core/uploads/receive.js +342 -0
  134. package/dist/core/uploads/receive.js.map +1 -0
  135. package/dist/core/uploads/types.d.ts +79 -0
  136. package/dist/core/uploads/types.d.ts.map +1 -0
  137. package/dist/core/uploads/types.js +2 -0
  138. package/dist/core/uploads/types.js.map +1 -0
  139. package/dist/decorators/hooks.d.ts +25 -0
  140. package/dist/decorators/hooks.d.ts.map +1 -0
  141. package/dist/decorators/hooks.js +36 -0
  142. package/dist/decorators/hooks.js.map +1 -0
  143. package/dist/decorators/http.d.ts +21 -0
  144. package/dist/decorators/http.d.ts.map +1 -0
  145. package/dist/decorators/http.js +21 -0
  146. package/dist/decorators/http.js.map +1 -0
  147. package/dist/decorators/index.d.ts +7 -0
  148. package/dist/decorators/index.d.ts.map +1 -0
  149. package/dist/decorators/index.js +7 -0
  150. package/dist/decorators/index.js.map +1 -0
  151. package/dist/decorators/log.d.ts +10 -0
  152. package/dist/decorators/log.d.ts.map +1 -0
  153. package/dist/decorators/log.js +32 -0
  154. package/dist/decorators/log.js.map +1 -0
  155. package/dist/decorators/middlewares.d.ts +28 -0
  156. package/dist/decorators/middlewares.d.ts.map +1 -0
  157. package/dist/decorators/middlewares.js +40 -0
  158. package/dist/decorators/middlewares.js.map +1 -0
  159. package/dist/decorators/router.d.ts +12 -0
  160. package/dist/decorators/router.d.ts.map +1 -0
  161. package/dist/decorators/router.js +20 -0
  162. package/dist/decorators/router.js.map +1 -0
  163. package/dist/decorators/schema.d.ts +8 -0
  164. package/dist/decorators/schema.d.ts.map +1 -0
  165. package/dist/decorators/schema.js +14 -0
  166. package/dist/decorators/schema.js.map +1 -0
  167. package/dist/index.d.ts +7 -0
  168. package/dist/index.d.ts.map +1 -0
  169. package/dist/index.js +10 -0
  170. package/dist/index.js.map +1 -0
  171. package/dist/middlewares/auth.d.ts +41 -0
  172. package/dist/middlewares/auth.d.ts.map +1 -0
  173. package/dist/middlewares/auth.js +74 -0
  174. package/dist/middlewares/auth.js.map +1 -0
  175. package/dist/middlewares/cors.d.ts +30 -0
  176. package/dist/middlewares/cors.d.ts.map +1 -0
  177. package/dist/middlewares/cors.js +69 -0
  178. package/dist/middlewares/cors.js.map +1 -0
  179. package/dist/middlewares/csrf.d.ts +42 -0
  180. package/dist/middlewares/csrf.d.ts.map +1 -0
  181. package/dist/middlewares/csrf.js +70 -0
  182. package/dist/middlewares/csrf.js.map +1 -0
  183. package/dist/middlewares/index.d.ts +6 -0
  184. package/dist/middlewares/index.d.ts.map +1 -0
  185. package/dist/middlewares/index.js +6 -0
  186. package/dist/middlewares/index.js.map +1 -0
  187. package/dist/middlewares/rate-limit.d.ts +35 -0
  188. package/dist/middlewares/rate-limit.d.ts.map +1 -0
  189. package/dist/middlewares/rate-limit.js +45 -0
  190. package/dist/middlewares/rate-limit.js.map +1 -0
  191. package/dist/middlewares/security-headers.d.ts +28 -0
  192. package/dist/middlewares/security-headers.d.ts.map +1 -0
  193. package/dist/middlewares/security-headers.js +42 -0
  194. package/dist/middlewares/security-headers.js.map +1 -0
  195. package/dist/openapi/generate.d.ts +51 -0
  196. package/dist/openapi/generate.d.ts.map +1 -0
  197. package/dist/openapi/generate.js +175 -0
  198. package/dist/openapi/generate.js.map +1 -0
  199. package/dist/schema/serialization.d.ts +2 -0
  200. package/dist/schema/serialization.d.ts.map +1 -0
  201. package/dist/schema/serialization.js +11 -0
  202. package/dist/schema/serialization.js.map +1 -0
  203. package/dist/schema/validation.d.ts +22 -0
  204. package/dist/schema/validation.d.ts.map +1 -0
  205. package/dist/schema/validation.js +68 -0
  206. package/dist/schema/validation.js.map +1 -0
  207. package/dist/server/static.d.ts +45 -0
  208. package/dist/server/static.d.ts.map +1 -0
  209. package/dist/server/static.js +204 -0
  210. package/dist/server/static.js.map +1 -0
  211. package/dist/server/tls.d.ts +48 -0
  212. package/dist/server/tls.d.ts.map +1 -0
  213. package/dist/server/tls.js +60 -0
  214. package/dist/server/tls.js.map +1 -0
  215. package/dist/shims/symbol-metadata.d.ts +2 -0
  216. package/dist/shims/symbol-metadata.d.ts.map +1 -0
  217. package/dist/shims/symbol-metadata.js +8 -0
  218. package/dist/shims/symbol-metadata.js.map +1 -0
  219. package/dist/utils/content-type.d.ts +13 -0
  220. package/dist/utils/content-type.d.ts.map +1 -0
  221. package/dist/utils/content-type.js +75 -0
  222. package/dist/utils/content-type.js.map +1 -0
  223. package/dist/utils/cookie.d.ts +34 -0
  224. package/dist/utils/cookie.d.ts.map +1 -0
  225. package/dist/utils/cookie.js +94 -0
  226. package/dist/utils/cookie.js.map +1 -0
  227. package/dist/utils/index.d.ts +7 -0
  228. package/dist/utils/index.d.ts.map +1 -0
  229. package/dist/utils/index.js +7 -0
  230. package/dist/utils/index.js.map +1 -0
  231. package/dist/utils/mime.d.ts +2 -0
  232. package/dist/utils/mime.d.ts.map +1 -0
  233. package/dist/utils/mime.js +52 -0
  234. package/dist/utils/mime.js.map +1 -0
  235. package/dist/utils/origin-match.d.ts +58 -0
  236. package/dist/utils/origin-match.d.ts.map +1 -0
  237. package/dist/utils/origin-match.js +194 -0
  238. package/dist/utils/origin-match.js.map +1 -0
  239. package/dist/utils/path.d.ts +10 -0
  240. package/dist/utils/path.d.ts.map +1 -0
  241. package/dist/utils/path.js +60 -0
  242. package/dist/utils/path.js.map +1 -0
  243. package/dist/utils/query.d.ts +28 -0
  244. package/dist/utils/query.d.ts.map +1 -0
  245. package/dist/utils/query.js +67 -0
  246. package/dist/utils/query.js.map +1 -0
  247. package/dist/utils/request-target.d.ts +31 -0
  248. package/dist/utils/request-target.d.ts.map +1 -0
  249. package/dist/utils/request-target.js +80 -0
  250. package/dist/utils/request-target.js.map +1 -0
  251. package/dist/utils/safe-json.d.ts +24 -0
  252. package/dist/utils/safe-json.d.ts.map +1 -0
  253. package/dist/utils/safe-json.js +83 -0
  254. package/dist/utils/safe-json.js.map +1 -0
  255. package/dist/utils/trace-context.d.ts +17 -0
  256. package/dist/utils/trace-context.d.ts.map +1 -0
  257. package/dist/utils/trace-context.js +30 -0
  258. package/dist/utils/trace-context.js.map +1 -0
  259. package/package.json +85 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-routes.d.ts","sourceRoot":"","sources":["../../../src/core/registry/framework-routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5B,CAAC;AAEF,oFAAoF;AACpF,qBAAa,eAAe;IAKxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;IAEnC,YACmB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,aAAa,EACtC;IAEJ,0GAA0G;IAC1G,aAAa,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAShE;IAED,2GAA2G;IAC3G,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CAiBzF;IAED,qGAAqG;IAC/F,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCjD;CACF"}
@@ -0,0 +1,83 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { compose } from '../compose.js';
3
+ import { kRawSend } from '../symbols.js';
4
+ import { docsPage, generateOpenAPI } from '../../openapi/generate.js';
5
+ import { canonicalPath } from '../../utils/path.js';
6
+ /** Rotas servidas pelo próprio app: healthchecks e OpenAPI (documento e página). */
7
+ export class FrameworkRoutes {
8
+ core;
9
+ registry;
10
+ health;
11
+ openapi;
12
+ constructor(core, registry) {
13
+ this.core = core;
14
+ this.registry = registry;
15
+ }
16
+ /** Paths servidos pelo próprio app (`health()`, `openapi()`), que encobririam uma rota GET do usuário. */
17
+ reservedPaths(except) {
18
+ const out = new Map();
19
+ if (except !== 'health')
20
+ for (const p of this.health?.paths ?? [])
21
+ out.set(p, 'app.health()');
22
+ const cfg = this.openapi;
23
+ if (cfg && except !== 'openapi') {
24
+ out.set(canonicalPath(cfg.path), 'app.openapi()');
25
+ if (cfg.docs)
26
+ out.set(canonicalPath(cfg.docs), 'app.openapi({ docs })');
27
+ }
28
+ return out;
29
+ }
30
+ /** Um path do framework não pode coincidir com uma rota GET/HEAD registrada nem com outro do framework. */
31
+ assertFreePaths(where, paths, kind) {
32
+ // `health()`/`openapi()` configuram o app, não o grupo: dentro de um `group()` em andamento, a
33
+ // configuração sobreviveria a um grupo desfeito e não enxergaria as rotas ainda pendentes.
34
+ if (this.registry.pending !== undefined) {
35
+ throw new Error(`[flowing] ${where}: cannot be called inside an app.group() callback; call it on the app, outside the group.`);
36
+ }
37
+ const reserved = this.reservedPaths(kind);
38
+ for (const path of paths) {
39
+ const other = reserved.get(path);
40
+ if (other)
41
+ throw new Error(`[flowing] ${where}: path "${path}" is already used by ${other}.`);
42
+ const route = this.registry.routes.find((r) => (r.method === 'GET' || r.method === 'HEAD') && canonicalPath(r.path) === path);
43
+ if (route) {
44
+ throw new Error(`[flowing] ${where}: path "${path}" would shadow ${route.owner}.${route.handlerName} (GET ${route.path}); choose another path.`);
45
+ }
46
+ }
47
+ }
48
+ /** Se a requisição é para `/openapi.json` ou `/docs` (quando ligados), responde e devolve `true`. */
49
+ async serveOpenAPI(ctx) {
50
+ const cfg = this.openapi;
51
+ if (!cfg || (ctx.method !== 'GET' && ctx.method !== 'HEAD'))
52
+ return false;
53
+ const path = canonicalPath(ctx.path);
54
+ const isSpec = path === canonicalPath(cfg.path);
55
+ if (!isSpec && !(cfg.docs && path === canonicalPath(cfg.docs)))
56
+ return false;
57
+ await compose(ctx, [
58
+ ...cfg.pipeline,
59
+ () => {
60
+ if (isSpec) {
61
+ // Um documento por conjunto de rotas visíveis no host da requisição (F-35); a chave não usa o
62
+ // hostname em si, então hosts arbitrários não fazem o cache crescer.
63
+ const visible = this.registry.routesFor(ctx.hostname.toLowerCase());
64
+ const key = visible.map((r) => this.registry.routes.indexOf(r)).join(',');
65
+ let doc = cfg.cache.get(key);
66
+ if (doc === undefined) {
67
+ doc = JSON.stringify(generateOpenAPI(visible, cfg.opts, !this.core.opts.formatError, (s) => this.core.contracts.toJsonSchema(s)));
68
+ cfg.cache.set(key, doc);
69
+ }
70
+ ctx[kRawSend](doc, 'application/json; charset=utf-8');
71
+ return;
72
+ }
73
+ const nonce = randomBytes(16).toString('base64');
74
+ const { html, csp } = docsPage(cfg.path, cfg.opts.info?.title ?? 'API', nonce);
75
+ ctx.res.setHeader('Content-Security-Policy', csp);
76
+ ctx.res.setHeader('Cache-Control', 'no-store');
77
+ ctx[kRawSend](html, 'text/html; charset=utf-8');
78
+ },
79
+ ]);
80
+ return true;
81
+ }
82
+ }
83
+ //# sourceMappingURL=framework-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-routes.js","sourceRoot":"","sources":["../../../src/core/registry/framework-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import type { AppCore } from '../app/core.ts';
2
+ import type { CompiledRoute } from '../compiler.ts';
3
+ import { RouteTable } from '../route-table.ts';
4
+ /** Uma rota validada e ainda não registrada (registro em duas fases). */
5
+ export interface PendingRoute {
6
+ owner: string;
7
+ route: CompiledRoute;
8
+ hosts: (string | undefined)[];
9
+ }
10
+ /**
11
+ * Registro de rotas em duas fases (F-35): `stage()` valida tudo contra o roteador e contra o próprio lote,
12
+ * `commit()` registra e desfaz se algo falhar. Também sabe quais rotas responderiam num host (`routesFor`).
13
+ */
14
+ export declare class RouteRegistry {
15
+ private readonly core;
16
+ /** Paths servidos pelo próprio app (health, openapi), que uma rota GET não pode encobrir. */
17
+ private readonly reservedPaths;
18
+ /** Chamado depois de cada commit com rotas (invalida o cache do OpenAPI). */
19
+ private readonly onCommit;
20
+ readonly router: RouteTable;
21
+ readonly routes: CompiledRoute[];
22
+ /** Registros pendentes de um `group()` em andamento. */
23
+ pending: PendingRoute[] | undefined;
24
+ private readonly warnedHosts;
25
+ constructor(core: AppCore,
26
+ /** Paths servidos pelo próprio app (health, openapi), que uma rota GET não pode encobrir. */
27
+ reservedPaths: () => Map<string, string>,
28
+ /** Chamado depois de cada commit com rotas (invalida o cache do OpenAPI). */
29
+ onCommit: () => void);
30
+ /**
31
+ * Primeira fase: valida as rotas (schemas obrigatórios, hosts alcançáveis, conflitos com o roteador e
32
+ * dentro do próprio lote). Dentro de um `group()`, acumula; fora, efetiva na hora.
33
+ */
34
+ stage(owner: string, routes: readonly CompiledRoute[]): void;
35
+ /**
36
+ * Segunda fase: registra o lote no roteador. Se algo ainda falhar (um conflito que o `has()` não prevê),
37
+ * desfaz o que já entrou, e nenhuma rota do lote fica registrada.
38
+ */
39
+ commit(batch: readonly PendingRoute[]): void;
40
+ /**
41
+ * Sem mistura de motores (F-27): todo schema da rota precisa ser do motor de contratos do app. Um schema TypeBox num
42
+ * app com `contracts.standard()` (ou um Zod no nativo) falharia em silêncio na validação; aqui o erro é no registro.
43
+ */
44
+ private checkContracts;
45
+ /**
46
+ * Um host de rota sem interseção com o `allowedHosts` nunca seria alcançado: o app responde 400 antes do
47
+ * roteamento (F-35). Comprovado: erro (ou aviso, com `unreachableHosts: 'warn'`). Sem como provar
48
+ * (RegExp em algum dos lados): aviso.
49
+ */
50
+ private checkReachableHosts;
51
+ /**
52
+ * Rotas que o roteador **executaria** num host (F-35): para cada método e path, só a vencedora pela mesma
53
+ * prioridade do roteador (exato → curinga mais específico → RegExp → sem host). Assim o OpenAPI documenta
54
+ * exatamente o handler que responde. Sem `hostname`, só as rotas sem host.
55
+ */
56
+ routesFor(hostname: string | undefined): CompiledRoute[];
57
+ }
58
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/registry/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAkB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA0B/D,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,qBAAa,aAAa;IAQtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAX3B,QAAQ,CAAC,MAAM,aAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAM;IACtC,wDAAwD;IACxD,OAAO,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAEjD,YACmB,IAAI,EAAE,OAAO;IAC9B,6FAA6F;IAC5E,aAAa,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IACzD,6EAA6E;IAC5D,QAAQ,EAAE,MAAM,IAAI,EACnC;IAEJ;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAwC3D;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CA0B3C;IAED;;;OAGG;IACH,OAAO,CAAC,cAAc;IAsBtB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAyB3B;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,EAAE,CAkBvD;CACF"}
@@ -0,0 +1,208 @@
1
+ import { uploadPlan } from '../uploads/receive.js';
2
+ import { assertSchemas } from '../request-validation.js';
3
+ import { RouteTable } from '../route-table.js';
4
+ import { buildRouteProgram } from './route-program.js';
5
+ import { hostRank, hostReachability, NO_HOST_RANK } from '../../utils/origin-match.js';
6
+ import { canonicalPath } from '../../utils/path.js';
7
+ import { kHooks } from '../symbols.js';
8
+ import { isResponseContent } from '../metadata.js';
9
+ /**
10
+ * O objeto guardado no roteador para um host: herda tudo da rota (inclusive o programa) e acrescenta o
11
+ * `RouteInfo` daquele host, para o `ctx.route` dizer qual regra venceu sem trabalho por requisição.
12
+ */
13
+ function withInfo(route, hostKey) {
14
+ const label = hostKey === undefined ? undefined : route.hosts.find((h) => h.key === hostKey)?.label;
15
+ const info = {
16
+ method: route.method,
17
+ path: route.path,
18
+ owner: route.owner,
19
+ handler: route.handlerName,
20
+ ...(label === undefined ? {} : { host: label }),
21
+ };
22
+ // Não enumerável: fora do JSON e do `ctx.route` que o usuário inspeciona.
23
+ if (route.hooks)
24
+ Object.defineProperty(info, kHooks, { value: route.hooks });
25
+ Object.freeze(info);
26
+ return Object.create(route, { info: { value: info } });
27
+ }
28
+ /**
29
+ * Registro de rotas em duas fases (F-35): `stage()` valida tudo contra o roteador e contra o próprio lote,
30
+ * `commit()` registra e desfaz se algo falhar. Também sabe quais rotas responderiam num host (`routesFor`).
31
+ */
32
+ export class RouteRegistry {
33
+ core;
34
+ reservedPaths;
35
+ onCommit;
36
+ router = new RouteTable();
37
+ routes = [];
38
+ /** Registros pendentes de um `group()` em andamento. */
39
+ pending;
40
+ warnedHosts = new Set();
41
+ constructor(core,
42
+ /** Paths servidos pelo próprio app (health, openapi), que uma rota GET não pode encobrir. */
43
+ reservedPaths,
44
+ /** Chamado depois de cada commit com rotas (invalida o cache do OpenAPI). */
45
+ onCommit) {
46
+ this.core = core;
47
+ this.reservedPaths = reservedPaths;
48
+ this.onCommit = onCommit;
49
+ }
50
+ /**
51
+ * Primeira fase: valida as rotas (schemas obrigatórios, hosts alcançáveis, conflitos com o roteador e
52
+ * dentro do próprio lote). Dentro de um `group()`, acumula; fora, efetiva na hora.
53
+ */
54
+ stage(owner, routes) {
55
+ const batch = this.pending ?? [];
56
+ // Checkpoint: dentro de um group(), um mount() que falha no meio não deixa parte dele no lote,
57
+ // mesmo que o callback capture o erro e siga em frente.
58
+ const mark = batch.length;
59
+ try {
60
+ for (const route of routes) {
61
+ // Rotas internas (static) não têm schema por natureza: o requireSchemas vale para as da aplicação.
62
+ if (this.core.opts.requireSchemas && !route.internal)
63
+ assertSchemas(owner, route);
64
+ this.checkContracts(owner, route);
65
+ if (route.method === 'GET' || route.method === 'HEAD') {
66
+ const reservedBy = this.reservedPaths().get(canonicalPath(route.path));
67
+ if (reservedBy) {
68
+ throw new Error(`[flowing] app.mount(${owner}): GET ${route.path} is served by ${reservedBy} and would never be reached; choose another path.`);
69
+ }
70
+ }
71
+ this.checkReachableHosts(owner, route);
72
+ for (const host of route.hosts.length > 0 ? route.hosts.map((h) => h.key) : [undefined]) {
73
+ const taken = this.router.has(route.method, route.path, host) ||
74
+ batch.some((p) => p.route.method === route.method && canonicalPath(p.route.path) === canonicalPath(route.path) && p.hosts.includes(host));
75
+ if (taken) {
76
+ const where = host ? ` on host ${host}` : '';
77
+ throw new Error(`[flowing] app.mount(${owner}): ${route.method} ${route.path}${where} is already registered. ` +
78
+ `Use a different "prefix" or "host" to mount the same class twice.`);
79
+ }
80
+ }
81
+ batch.push({ owner, route, hosts: route.hosts.length > 0 ? route.hosts.map((h) => h.key) : [undefined] });
82
+ }
83
+ }
84
+ catch (err) {
85
+ batch.length = mark;
86
+ throw err;
87
+ }
88
+ if (this.pending === undefined)
89
+ this.commit(batch);
90
+ }
91
+ /**
92
+ * Segunda fase: registra o lote no roteador. Se algo ainda falhar (um conflito que o `has()` não prevê),
93
+ * desfaz o que já entrou, e nenhuma rota do lote fica registrada.
94
+ */
95
+ commit(batch) {
96
+ const done = [];
97
+ try {
98
+ for (const { owner, route, hosts } of batch) {
99
+ route.run ??= buildRouteProgram(route, this.core.serializers, this.core.uploads);
100
+ for (const host of hosts) {
101
+ try {
102
+ this.router.on(route.method, route.path, withInfo(route, host), host);
103
+ }
104
+ catch (err) {
105
+ throw new Error(`[flowing] app.mount(${owner}): ${route.method} ${route.path} conflicts with a registered route.`, {
106
+ cause: err,
107
+ });
108
+ }
109
+ done.push({ method: route.method, path: route.path, host });
110
+ }
111
+ }
112
+ }
113
+ catch (err) {
114
+ for (const r of done.reverse())
115
+ this.router.off(r.method, r.path, r.host);
116
+ throw err;
117
+ }
118
+ for (const { route } of batch) {
119
+ this.routes.push(route);
120
+ // O observer só liga a observação do fim se algum hook (global ou de rota) precisar dela.
121
+ if (route.hooks?.onResponse.length)
122
+ this.core.scopedOnResponse++;
123
+ }
124
+ if (batch.length > 0)
125
+ this.onCommit();
126
+ }
127
+ /**
128
+ * Sem mistura de motores (F-27): todo schema da rota precisa ser do motor de contratos do app. Um schema TypeBox num
129
+ * app com `contracts.standard()` (ou um Zod no nativo) falharia em silêncio na validação; aqui o erro é no registro.
130
+ */
131
+ checkContracts(owner, route) {
132
+ const engine = this.core.contracts;
133
+ const { response, files, ...input } = route.schemas;
134
+ // Valida as regras de arquivo no registro: uma rota que amplia um limite global falha aqui (F-46).
135
+ if (files)
136
+ uploadPlan(files, this.core.uploads, `app.mount(${owner}): ${route.method} ${route.path}`);
137
+ const parts = Object.entries(input).filter(([, s]) => s !== undefined);
138
+ for (const [status, entry] of Object.entries(response ?? {})) {
139
+ const schema = isResponseContent(entry) ? entry.schema : entry;
140
+ if (schema !== undefined)
141
+ parts.push([`response ${status}`, schema]);
142
+ }
143
+ for (const [part, schema] of parts) {
144
+ if (engine.owns(schema))
145
+ continue;
146
+ const hint = engine.kind === 'native'
147
+ ? 'it looks like a Standard Schema (Zod, Valibot, ArkType); use createApp({ contracts: contracts.standard() })'
148
+ : 'it is not a Standard Schema; with contracts.standard(), every schema must be one (Zod, Valibot, ArkType)';
149
+ throw new Error(`[flowing] app.mount(${owner}): ${route.method} ${route.path}: the ${part} schema does not match the app's contracts: ${hint}.`);
150
+ }
151
+ }
152
+ /**
153
+ * Um host de rota sem interseção com o `allowedHosts` nunca seria alcançado: o app responde 400 antes do
154
+ * roteamento (F-35). Comprovado: erro (ou aviso, com `unreachableHosts: 'warn'`). Sem como provar
155
+ * (RegExp em algum dos lados): aviso.
156
+ */
157
+ checkReachableHosts(owner, route) {
158
+ const mode = this.core.opts.unreachableHosts ?? 'error';
159
+ const allowed = this.core.opts.allowedHosts;
160
+ if (mode === 'off' || !allowed)
161
+ return;
162
+ for (const host of route.hosts) {
163
+ const verdict = hostReachability(host, allowed);
164
+ if (verdict === 'yes')
165
+ continue;
166
+ const where = `app.mount(${owner}): ${route.method} ${route.path} uses host ${host.label}`;
167
+ if (verdict === 'no' && mode === 'error') {
168
+ throw new Error(`[flowing] ${where}, which no allowedHosts rule accepts; the route is unreachable. Add it to allowedHosts, or set unreachableHosts: 'warn'.`);
169
+ }
170
+ const key = `${owner} ${host.key}`;
171
+ if (this.warnedHosts.has(key))
172
+ continue;
173
+ this.warnedHosts.add(key);
174
+ this.core.log.warn({ host: host.label }, verdict === 'no'
175
+ ? `[flowing] ${where}, which no allowedHosts rule accepts; the route is unreachable.`
176
+ : `[flowing] ${where}; could not verify it against allowedHosts (RegExp).`);
177
+ }
178
+ }
179
+ /**
180
+ * Rotas que o roteador **executaria** num host (F-35): para cada método e path, só a vencedora pela mesma
181
+ * prioridade do roteador (exato → curinga mais específico → RegExp → sem host). Assim o OpenAPI documenta
182
+ * exatamente o handler que responde. Sem `hostname`, só as rotas sem host.
183
+ */
184
+ routesFor(hostname) {
185
+ const best = new Map();
186
+ for (const [seq, route] of this.routes.entries()) {
187
+ let rank = route.hosts.length === 0 ? NO_HOST_RANK : -1;
188
+ if (rank === -1 && hostname !== undefined) {
189
+ for (const h of route.hosts) {
190
+ if (!h.test(hostname))
191
+ continue;
192
+ // Desempate pela ordem de registro, a mesma que o roteador usa em cada path.
193
+ const r = hostRank(h, seq);
194
+ if (rank === -1 || r < rank)
195
+ rank = r;
196
+ }
197
+ }
198
+ if (rank === -1)
199
+ continue;
200
+ const key = `${route.method} ${canonicalPath(route.path)}`;
201
+ const current = best.get(key);
202
+ if (!current || rank < current.rank)
203
+ best.set(key, { route, rank });
204
+ }
205
+ return this.routes.filter((r) => best.get(`${r.method} ${canonicalPath(r.path)}`)?.route === r);
206
+ }
207
+ }
208
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/registry/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type Step } from '../compose.ts';
2
+ import type { Context } from '../context.ts';
3
+ import { type Serializer } from '../response/pipeline.ts';
4
+ import type { RouteDefinition } from '../route-table.ts';
5
+ import { type UploadSettings } from '../uploads/receive.ts';
6
+ /** A função completa de uma rota, montada uma vez no registro. */
7
+ export type RouteProgram = (ctx: Context) => Step;
8
+ /**
9
+ * Builder (F-39): monta, no registro, o programa de cada rota. Antes, a cada requisição, o app criava o
10
+ * array do pipeline, o middleware de guards, a closure final e o objeto de resposta.
11
+ *
12
+ * Ordem (F-01): middlewares da rota → guards → validação → handler → resposta. Os guards vêm antes da
13
+ * validação, para quem não tem acesso receber 401/403, e não um 400 que revele o formato esperado.
14
+ */
15
+ export declare function buildRouteProgram(route: RouteDefinition, serializers: ReadonlyMap<string, Serializer>, uploads: UploadSettings): RouteProgram;
16
+ //# sourceMappingURL=route-program.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-program.d.ts","sourceRoot":"","sources":["../../../src/core/registry/route-program.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAmC,KAAK,cAAc,EAAc,MAAM,uBAAuB,CAAC;AAEzG,kEAAkE;AAClE,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;AAElD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,EAC5C,OAAO,EAAE,cAAc,GACtB,YAAY,CA4Cd"}
@@ -0,0 +1,62 @@
1
+ import { compileChain, isPromiseLike } from '../compose.js';
2
+ import { guardsMiddleware } from '../guard.js';
3
+ import { validateRequest } from '../request-validation.js';
4
+ import { responsePlan, sendResult } from '../response/pipeline.js';
5
+ import { kBindResponse, kFiles, kPendingSend, kPhase } from '../symbols.js';
6
+ import { receiveUploads, uploadPlan } from '../uploads/receive.js';
7
+ /**
8
+ * Builder (F-39): monta, no registro, o programa de cada rota. Antes, a cada requisição, o app criava o
9
+ * array do pipeline, o middleware de guards, a closure final e o objeto de resposta.
10
+ *
11
+ * Ordem (F-01): middlewares da rota → guards → validação → handler → resposta. Os guards vêm antes da
12
+ * validação, para quem não tem acesso receber 401/403, e não um 400 que revele o formato esperado.
13
+ */
14
+ export function buildRouteProgram(route, serializers, uploads) {
15
+ const { schemas, handler } = route;
16
+ // Só rotas com `files` pagam pelo upload (F-46); as demais seguem o caminho de sempre.
17
+ const upload = schemas.files ? uploadPlan(schemas.files, uploads, 'route') : undefined;
18
+ const plan = responsePlan(schemas.response, serializers);
19
+ const send = (ctx, result) => {
20
+ ctx[kPhase] = 'response';
21
+ if (!ctx.sent)
22
+ sendResult(ctx, plan.response, result, plan.serializers, 'auto', plan.defaultStatus);
23
+ };
24
+ const finish = (ctx, result) => {
25
+ // Um sendFile() sem await no handler: espera o envio pendente antes de responder.
26
+ const pending = ctx[kPendingSend];
27
+ if (pending)
28
+ return pending.then(() => send(ctx, result));
29
+ send(ctx, result);
30
+ return undefined;
31
+ };
32
+ // Síncrono quando o handler é síncrono: Promise só se o handler devolver uma (F-41).
33
+ const terminal = (ctx) => {
34
+ ctx[kPhase] = 'validation';
35
+ // Assíncrona só com um schema de validação assíncrona (Standard Schema); o nativo é sempre síncrono.
36
+ if (upload)
37
+ return receive(ctx, upload);
38
+ const validating = validateRequest(ctx, schemas);
39
+ return validating ? resume(validating, ctx) : run(ctx);
40
+ };
41
+ // Upload (F-46): recebe e confere os arquivos, põe os campos de texto no body e só então valida o contrato.
42
+ const receive = (ctx, plan) => receiveUploads(ctx, plan).then((received) => {
43
+ ctx.body = received.fields;
44
+ ctx[kFiles] = received.files;
45
+ const validating = validateRequest(ctx, schemas);
46
+ return validating ? resume(validating, ctx) : run(ctx);
47
+ });
48
+ // Separado do `terminal`: um callback ali capturaria o `ctx` e o V8 alocaria um contexto em toda requisição (F-41).
49
+ const resume = (validating, ctx) => validating.then(() => run(ctx));
50
+ const run = (ctx) => {
51
+ // F-32: a partir daqui, ctx.json/send/text passam pelo mesmo pipeline do `return`.
52
+ ctx[kBindResponse](plan);
53
+ ctx[kPhase] = 'handler';
54
+ const result = handler(ctx);
55
+ if (isPromiseLike(result))
56
+ return Promise.resolve(result).then((value) => finish(ctx, value));
57
+ return finish(ctx, result);
58
+ };
59
+ const layers = route.guards.length > 0 ? [...route.middlewares, guardsMiddleware(route.guards)] : route.middlewares;
60
+ return layers.length === 0 ? terminal : compileChain(layers, terminal);
61
+ }
62
+ //# sourceMappingURL=route-program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-program.js","sourceRoot":"","sources":["../../../src/core/registry/route-program.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Context } from './context.ts';
2
+ import type { RouteSchemas } from './metadata.ts';
3
+ /** Valida cada parte da requisição que tiver schema; o primeiro erro vira 400 com detalhes (B-02/B-03). */
4
+ /**
5
+ * Valida headers, params, query e body pelo motor dos contratos do app (F-27), nessa ordem. Síncrono no motor nativo;
6
+ * com Standard Schema, vira assíncrono só quando um schema valida de forma assíncrona. O valor validado substitui
7
+ * `ctx.params`, `ctx.query` e `ctx.body` (o motor pode converter e aplicar padrões); os headers não são trocados.
8
+ */
9
+ export declare function validateRequest(ctx: Context, schemas: RouteSchemas): Promise<void> | undefined;
10
+ export declare function assertSchemas(owner: string, route: {
11
+ method: string;
12
+ path: string;
13
+ schemas: RouteSchemas;
14
+ }): void;
15
+ //# sourceMappingURL=request-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-validation.d.ts","sourceRoot":"","sources":["../../src/core/request-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAclD,2GAA2G;AAC3G;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAE9F;AAoCD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAAG,IAAI,CAQjH"}
@@ -0,0 +1,67 @@
1
+ import { HttpError } from './errors.js';
2
+ import { isPromiseLike } from './compose.js';
3
+ import { kContracts, kFiles } from './symbols.js';
4
+ const VALIDATION_ORDER = ['headers', 'params', 'query', 'body'];
5
+ const TARGET_LABEL = {
6
+ headers: 'Invalid headers',
7
+ params: 'Invalid params',
8
+ query: 'Invalid query',
9
+ body: 'Invalid body',
10
+ };
11
+ /** Valida cada parte da requisição que tiver schema; o primeiro erro vira 400 com detalhes (B-02/B-03). */
12
+ /**
13
+ * Valida headers, params, query e body pelo motor dos contratos do app (F-27), nessa ordem. Síncrono no motor nativo;
14
+ * com Standard Schema, vira assíncrono só quando um schema valida de forma assíncrona. O valor validado substitui
15
+ * `ctx.params`, `ctx.query` e `ctx.body` (o motor pode converter e aplicar padrões); os headers não são trocados.
16
+ */
17
+ export function validateRequest(ctx, schemas) {
18
+ return validateFrom(ctx, schemas, 0);
19
+ }
20
+ function validateFrom(ctx, schemas, start) {
21
+ const engine = ctx[kContracts];
22
+ for (let i = start; i < VALIDATION_ORDER.length; i++) {
23
+ const target = VALIDATION_ORDER[i];
24
+ const schema = schemas[target];
25
+ if (!schema)
26
+ continue;
27
+ const data = target === 'headers' ? { ...ctx.req.headers } : ctx[target];
28
+ // Body de formulário (F-46): tudo chega como texto, então valida com coerção, como a query.
29
+ const result = engine.check(schema, data, target === 'body' && ctx[kFiles] !== undefined ? 'query' : target);
30
+ if (isPromiseLike(result))
31
+ return resumeAsync(ctx, schemas, i, data, result);
32
+ apply(ctx, target, data, result);
33
+ }
34
+ return undefined;
35
+ }
36
+ /**
37
+ * O resto da validação depois de um schema assíncrono. Fica fora do laço de propósito: um callback que captura
38
+ * variáveis do laço faria o V8 alocar um contexto em toda requisição, mesmo no caminho síncrono (F-41).
39
+ */
40
+ function resumeAsync(ctx, schemas, i, data, pending) {
41
+ return Promise.resolve(pending).then((r) => {
42
+ apply(ctx, VALIDATION_ORDER[i], data, r);
43
+ return validateFrom(ctx, schemas, i + 1);
44
+ });
45
+ }
46
+ function apply(ctx, target, data, result) {
47
+ if (!result.ok)
48
+ throw new HttpError(400, TARGET_LABEL[target], 'VALIDATION_ERROR', { details: result.issues });
49
+ if (target === 'headers' || result.value === data)
50
+ return;
51
+ if (target === 'body')
52
+ ctx.body = result.value;
53
+ else if (target === 'query')
54
+ ctx.query = result.value;
55
+ else
56
+ ctx.params = result.value;
57
+ }
58
+ export function assertSchemas(owner, route) {
59
+ const where = `${owner}: ${route.method} ${route.path}`;
60
+ if (['POST', 'PUT', 'PATCH'].includes(route.method) && !route.schemas.body && !route.schemas.files) {
61
+ throw new Error(`[flowing] requireSchemas: ${where} has no body schema.`);
62
+ }
63
+ if (!route.schemas.response || Object.keys(route.schemas.response).length === 0) {
64
+ throw new Error(`[flowing] requireSchemas: ${where} has no response schema.`);
65
+ }
66
+ }
67
+ //# sourceMappingURL=request-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-validation.js","sourceRoot":"","sources":["../../src/core/request-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { Context } from '../context.ts';
2
+ import { type ErrorFormatter, type ProblemDetails } from '../errors.ts';
3
+ import { type RouteSchemas } from '../metadata.ts';
4
+ /** Converte dados num corpo de resposta para um content type (F-31). */
5
+ export type Serializer = (data: unknown) => string | Uint8Array;
6
+ /**
7
+ * Como o corpo chegou ao pipeline (F-32): `auto` (`return` e `ctx.send()`), `json` (`ctx.json()`,
8
+ * sempre JSON) ou `text` (`ctx.text()`, sempre texto).
9
+ */
10
+ export type SendMode = 'auto' | 'json' | 'text';
11
+ /**
12
+ * Envia o retorno do handler.
13
+ *
14
+ * Status (B-05): sem `ctx.status()` e com um único 2xx declarado, esse 2xx vira o padrão. Com schemas
15
+ * de resposta declarados, um status sem schema correspondente vira 500 em vez de enviar o objeto
16
+ * sem filtro (S-07). Respostas sem corpo (`undefined`) dispensam schema.
17
+ *
18
+ * Content type (F-31), em ordem de prioridade:
19
+ * 1. o `Content-Type` definido pelo handler;
20
+ * 2. o `contentType` declarado no schema de resposta (com negociação pelo `Accept` se houver vários);
21
+ * 3. schema de resposta sem `contentType` → JSON;
22
+ * 4. sem schema, o tipo do valor: string → `text/plain`, Buffer/Uint8Array/stream → `application/octet-stream`,
23
+ * o resto → JSON.
24
+ */
25
+ /**
26
+ * O que cada rota sabe sobre a resposta, decidido no registro (Strategy, F-39): schemas, serializadores e o
27
+ * status padrão (o único 2xx declarado, quando não há 200). O mesmo objeto serve a todas as requisições.
28
+ */
29
+ export interface ResponsePlan {
30
+ readonly response: RouteSchemas['response'];
31
+ readonly serializers: ReadonlyMap<string, Serializer>;
32
+ readonly defaultStatus: number | undefined;
33
+ }
34
+ /** Status padrão de uma rota (B-05): com um único 2xx declarado e sem 200, é esse 2xx. */
35
+ export declare function defaultStatusFor(response: RouteSchemas['response']): number | undefined;
36
+ export declare function responsePlan(response: RouteSchemas['response'], serializers: ReadonlyMap<string, Serializer>): ResponsePlan;
37
+ export declare function sendResult(ctx: Context, response: RouteSchemas['response'], result: unknown, serializers?: ReadonlyMap<string, Serializer>, mode?: SendMode, defaultStatus?: number | undefined): void;
38
+ /** Monta um Problem Details (RFC 9457). Sem `type`, usa `about:blank` e a frase do status como título. */
39
+ export declare function problem(status: number, detail?: string, extra?: {
40
+ code?: string | undefined;
41
+ details?: unknown;
42
+ type?: string | undefined;
43
+ }): ProblemDetails;
44
+ /** Escreve um problema (status + corpo), sem passar pelo schema da rota (F-32). */
45
+ export declare function sendProblem(ctx: Context, body: ProblemDetails, format?: ErrorFormatter): void;
46
+ /**
47
+ * Resposta de erro padrão, no formato Problem Details (F-15). Só mensagens marcadas como `expose`
48
+ * chegam ao cliente; o resto vira 500 genérico e o erro original é logado no servidor (S-05).
49
+ */
50
+ export declare function sendError(ctx: Context, err: unknown, format?: ErrorFormatter): void;
51
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../src/core/response/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAAa,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAItE,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,UAAU,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAIhD;;;;;;;;;;;;;GAaG;AACH;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,SAAS,CAMvF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,YAAY,CAE3H;AAID,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,EAClC,MAAM,EAAE,OAAO,EACf,WAAW,GAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAqB,EAChE,IAAI,GAAE,QAAiB,EACvB,aAAa,GAAE,MAAM,GAAG,SAAsC,GAC7D,IAAI,CAmCN;AAwFD,0GAA0G;AAC1G,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAO,GACtF,cAAc,CAMhB;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAa7F;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAanF"}